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, but
346 * only if we are not in transacted mode
348 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
349 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
350 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
352 res = STG_E_ACCESSDENIED;
358 * Create a property enumeration to search the properties
360 propertyEnumeration = IEnumSTATSTGImpl_Construct(
361 This->ancestorStorage,
362 This->rootPropertySetIndex);
365 * Search the enumeration for the property with the given name
367 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
373 * Delete the property enumeration since we don't need it anymore
375 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
378 * If it was found, construct the stream object and return a pointer to it.
380 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
381 (currentProperty.propertyType==PROPTYPE_STREAM) )
383 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
387 newStream->grfMode = grfMode;
388 *ppstm = (IStream*)newStream;
391 * Since we are returning a pointer to the interface, we have to
392 * nail down the reference.
394 IStream_AddRef(*ppstm);
397 * add us to the storage's list of active streams
400 StorageBaseImpl_AddStream(This,newStream);
410 res = STG_E_FILENOTFOUND;
414 TRACE("<-- IStream %p\n", *ppstm);
415 TRACE("<-- %08lx\n", res);
419 /************************************************************************
420 * Storage32BaseImpl_OpenStorage (IStorage)
422 * This method will open a new storage object from the current storage.
424 * See Windows documentation for more details on IStorage methods.
426 HRESULT WINAPI StorageBaseImpl_OpenStorage(
428 const OLECHAR* pwcsName, /* [string][unique][in] */
429 IStorage* pstgPriority, /* [unique][in] */
430 DWORD grfMode, /* [in] */
431 SNB snbExclude, /* [unique][in] */
432 DWORD reserved, /* [in] */
433 IStorage** ppstg) /* [out] */
435 StorageBaseImpl *This = (StorageBaseImpl *)iface;
436 StorageInternalImpl* newStorage;
437 IEnumSTATSTGImpl* propertyEnumeration;
438 StgProperty currentProperty;
439 ULONG foundPropertyIndex;
440 HRESULT res = STG_E_UNKNOWN;
441 DWORD parent_grfMode;
443 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
444 iface, debugstr_w(pwcsName), pstgPriority,
445 grfMode, snbExclude, reserved, ppstg);
448 * Perform a sanity check on the parameters.
450 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
457 if (snbExclude != NULL)
459 res = STG_E_INVALIDPARAMETER;
464 * Validate the STGM flags
466 if ( FAILED( validateSTGM(grfMode) ))
468 res = STG_E_INVALIDFLAG;
475 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
476 (grfMode & STGM_DELETEONRELEASE) ||
477 (grfMode & STGM_PRIORITY) )
479 res = STG_E_INVALIDFUNCTION;
484 * Check that we're compatible with the parent's storage mode,
485 * but only if we are not transacted
487 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
488 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
489 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
491 res = STG_E_ACCESSDENIED;
497 * Initialize the out parameter
502 * Create a property enumeration to search the properties
504 propertyEnumeration = IEnumSTATSTGImpl_Construct(
505 This->ancestorStorage,
506 This->rootPropertySetIndex);
509 * Search the enumeration for the property with the given name
511 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
517 * Delete the property enumeration since we don't need it anymore
519 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
522 * If it was found, construct the stream object and return a pointer to it.
524 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
525 (currentProperty.propertyType==PROPTYPE_STORAGE) )
528 * Construct a new Storage object
530 newStorage = StorageInternalImpl_Construct(
531 This->ancestorStorage,
537 *ppstg = (IStorage*)newStorage;
540 * Since we are returning a pointer to the interface,
541 * we have to nail down the reference.
543 StorageBaseImpl_AddRef(*ppstg);
549 res = STG_E_INSUFFICIENTMEMORY;
553 res = STG_E_FILENOTFOUND;
556 TRACE("<-- %08lx\n", res);
560 /************************************************************************
561 * Storage32BaseImpl_EnumElements (IStorage)
563 * This method will create an enumerator object that can be used to
564 * retrieve informatino about all the properties in the storage object.
566 * See Windows documentation for more details on IStorage methods.
568 HRESULT WINAPI StorageBaseImpl_EnumElements(
570 DWORD reserved1, /* [in] */
571 void* reserved2, /* [size_is][unique][in] */
572 DWORD reserved3, /* [in] */
573 IEnumSTATSTG** ppenum) /* [out] */
575 StorageBaseImpl *This = (StorageBaseImpl *)iface;
576 IEnumSTATSTGImpl* newEnum;
578 TRACE("(%p, %ld, %p, %ld, %p)\n",
579 iface, reserved1, reserved2, reserved3, ppenum);
582 * Perform a sanity check on the parameters.
584 if ( (This==0) || (ppenum==0))
588 * Construct the enumerator.
590 newEnum = IEnumSTATSTGImpl_Construct(
591 This->ancestorStorage,
592 This->rootPropertySetIndex);
596 *ppenum = (IEnumSTATSTG*)newEnum;
599 * Don't forget to nail down a reference to the new object before
602 IEnumSTATSTG_AddRef(*ppenum);
607 return E_OUTOFMEMORY;
610 /************************************************************************
611 * Storage32BaseImpl_Stat (IStorage)
613 * This method will retrieve information about this storage object.
615 * See Windows documentation for more details on IStorage methods.
617 HRESULT WINAPI StorageBaseImpl_Stat(
619 STATSTG* pstatstg, /* [out] */
620 DWORD grfStatFlag) /* [in] */
622 StorageBaseImpl *This = (StorageBaseImpl *)iface;
623 StgProperty curProperty;
625 HRESULT res = STG_E_UNKNOWN;
627 TRACE("(%p, %p, %lx)\n",
628 iface, pstatstg, grfStatFlag);
631 * Perform a sanity check on the parameters.
633 if ( (This==0) || (pstatstg==0))
640 * Read the information from the property.
642 readSuccessful = StorageImpl_ReadProperty(
643 This->ancestorStorage,
644 This->rootPropertySetIndex,
649 StorageUtl_CopyPropertyToSTATSTG(
663 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);
665 TRACE("<-- %08lx\n", res);
669 /************************************************************************
670 * Storage32BaseImpl_RenameElement (IStorage)
672 * This method will rename the specified element.
674 * See Windows documentation for more details on IStorage methods.
676 * Implementation notes: The method used to rename consists of creating a clone
677 * of the deleted StgProperty object setting it with the new name and to
678 * perform a DestroyElement of the old StgProperty.
680 HRESULT WINAPI StorageBaseImpl_RenameElement(
682 const OLECHAR* pwcsOldName, /* [in] */
683 const OLECHAR* pwcsNewName) /* [in] */
685 StorageBaseImpl *This = (StorageBaseImpl *)iface;
686 IEnumSTATSTGImpl* propertyEnumeration;
687 StgProperty currentProperty;
688 ULONG foundPropertyIndex;
690 TRACE("(%p, %s, %s)\n",
691 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
694 * Create a property enumeration to search the properties
696 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
697 This->rootPropertySetIndex);
700 * Search the enumeration for the new property name
702 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
706 if (foundPropertyIndex != PROPERTY_NULL)
709 * There is already a property with the new name
711 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
712 return STG_E_FILEALREADYEXISTS;
715 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
718 * Search the enumeration for the old property name
720 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
725 * Delete the property enumeration since we don't need it anymore
727 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
729 if (foundPropertyIndex != PROPERTY_NULL)
731 StgProperty renamedProperty;
732 ULONG renamedPropertyIndex;
735 * Setup a new property for the renamed property
737 renamedProperty.sizeOfNameString =
738 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
740 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
741 return STG_E_INVALIDNAME;
743 strcpyW(renamedProperty.name, pwcsNewName);
745 renamedProperty.propertyType = currentProperty.propertyType;
746 renamedProperty.startingBlock = currentProperty.startingBlock;
747 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
748 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
750 renamedProperty.previousProperty = PROPERTY_NULL;
751 renamedProperty.nextProperty = PROPERTY_NULL;
754 * Bring the dirProperty link in case it is a storage and in which
755 * case the renamed storage elements don't require to be reorganized.
757 renamedProperty.dirProperty = currentProperty.dirProperty;
759 /* call CoFileTime to get the current time
760 renamedProperty.timeStampS1
761 renamedProperty.timeStampD1
762 renamedProperty.timeStampS2
763 renamedProperty.timeStampD2
764 renamedProperty.propertyUniqueID
768 * Obtain a free property in the property chain
770 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
773 * Save the new property into the new property spot
775 StorageImpl_WriteProperty(
776 This->ancestorStorage,
777 renamedPropertyIndex,
781 * Find a spot in the property chain for our newly created property.
785 renamedPropertyIndex,
789 * At this point the renamed property has been inserted in the tree,
790 * now, before Destroying the old property we must zero its dirProperty
791 * otherwise the DestroyProperty below will zap it all and we do not want
793 * Also, we fake that the old property is a storage so the DestroyProperty
794 * will not do a SetSize(0) on the stream data.
796 * This means that we need to tweak the StgProperty if it is a stream or a
799 StorageImpl_ReadProperty(This->ancestorStorage,
803 currentProperty.dirProperty = PROPERTY_NULL;
804 currentProperty.propertyType = PROPTYPE_STORAGE;
805 StorageImpl_WriteProperty(
806 This->ancestorStorage,
811 * Invoke Destroy to get rid of the ole property and automatically redo
812 * the linking of its previous and next members...
814 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
820 * There is no property with the old name
822 return STG_E_FILENOTFOUND;
828 /************************************************************************
829 * Storage32BaseImpl_CreateStream (IStorage)
831 * This method will create a stream object within this storage
833 * See Windows documentation for more details on IStorage methods.
835 HRESULT WINAPI StorageBaseImpl_CreateStream(
837 const OLECHAR* pwcsName, /* [string][in] */
838 DWORD grfMode, /* [in] */
839 DWORD reserved1, /* [in] */
840 DWORD reserved2, /* [in] */
841 IStream** ppstm) /* [out] */
843 StorageBaseImpl *This = (StorageBaseImpl *)iface;
844 IEnumSTATSTGImpl* propertyEnumeration;
845 StgStreamImpl* newStream;
846 StgProperty currentProperty, newStreamProperty;
847 ULONG foundPropertyIndex, newPropertyIndex;
848 DWORD parent_grfMode;
850 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
851 iface, debugstr_w(pwcsName), grfMode,
852 reserved1, reserved2, ppstm);
855 * Validate parameters
858 return STG_E_INVALIDPOINTER;
861 return STG_E_INVALIDNAME;
863 if (reserved1 || reserved2)
864 return STG_E_INVALIDPARAMETER;
867 * Validate the STGM flags
869 if ( FAILED( validateSTGM(grfMode) ))
870 return STG_E_INVALIDFLAG;
872 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
873 return STG_E_INVALIDFLAG;
878 if ((grfMode & STGM_DELETEONRELEASE) ||
879 (grfMode & STGM_TRANSACTED))
880 return STG_E_INVALIDFUNCTION;
883 * Check that we're compatible with the parent's storage mode
884 * if not in transacted mode
886 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
887 if(!(parent_grfMode & STGM_TRANSACTED)) {
888 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
889 return STG_E_ACCESSDENIED;
893 * Initialize the out parameter
898 * Create a property enumeration to search the properties
900 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
901 This->rootPropertySetIndex);
903 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
907 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
909 if (foundPropertyIndex != PROPERTY_NULL)
912 * An element with this name already exists
914 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
916 IStorage_DestroyElement(iface, pwcsName);
919 return STG_E_FILEALREADYEXISTS;
923 * memset the empty property
925 memset(&newStreamProperty, 0, sizeof(StgProperty));
927 newStreamProperty.sizeOfNameString =
928 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
930 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
931 return STG_E_INVALIDNAME;
933 strcpyW(newStreamProperty.name, pwcsName);
935 newStreamProperty.propertyType = PROPTYPE_STREAM;
936 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
937 newStreamProperty.size.u.LowPart = 0;
938 newStreamProperty.size.u.HighPart = 0;
940 newStreamProperty.previousProperty = PROPERTY_NULL;
941 newStreamProperty.nextProperty = PROPERTY_NULL;
942 newStreamProperty.dirProperty = PROPERTY_NULL;
944 /* call CoFileTime to get the current time
945 newStreamProperty.timeStampS1
946 newStreamProperty.timeStampD1
947 newStreamProperty.timeStampS2
948 newStreamProperty.timeStampD2
951 /* newStreamProperty.propertyUniqueID */
954 * Get a free property or create a new one
956 newPropertyIndex = getFreeProperty(This->ancestorStorage);
959 * Save the new property into the new property spot
961 StorageImpl_WriteProperty(
962 This->ancestorStorage,
967 * Find a spot in the property chain for our newly created property.
975 * Open the stream to return it.
977 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
981 *ppstm = (IStream*)newStream;
984 * Since we are returning a pointer to the interface, we have to nail down
987 IStream_AddRef(*ppstm);
989 /* add us to the storage's list of active streams
991 StorageBaseImpl_AddStream(This,newStream);
996 return STG_E_INSUFFICIENTMEMORY;
1002 /************************************************************************
1003 * Storage32BaseImpl_SetClass (IStorage)
1005 * This method will write the specified CLSID in the property of this
1008 * See Windows documentation for more details on IStorage methods.
1010 HRESULT WINAPI StorageBaseImpl_SetClass(
1012 REFCLSID clsid) /* [in] */
1014 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1015 HRESULT hRes = E_FAIL;
1016 StgProperty curProperty;
1019 TRACE("(%p, %p)\n", iface, clsid);
1021 success = StorageImpl_ReadProperty(This->ancestorStorage,
1022 This->rootPropertySetIndex,
1026 curProperty.propertyUniqueID = *clsid;
1028 success = StorageImpl_WriteProperty(This->ancestorStorage,
1029 This->rootPropertySetIndex,
1038 /************************************************************************
1039 ** Storage32Impl implementation
1042 /************************************************************************
1043 * Storage32Impl_CreateStorage (IStorage)
1045 * This method will create the storage object within the provided storage.
1047 * See Windows documentation for more details on IStorage methods.
1049 HRESULT WINAPI StorageImpl_CreateStorage(
1051 const OLECHAR *pwcsName, /* [string][in] */
1052 DWORD grfMode, /* [in] */
1053 DWORD reserved1, /* [in] */
1054 DWORD reserved2, /* [in] */
1055 IStorage **ppstg) /* [out] */
1057 StorageImpl* const This=(StorageImpl*)iface;
1059 IEnumSTATSTGImpl *propertyEnumeration;
1060 StgProperty currentProperty;
1061 StgProperty newProperty;
1062 ULONG foundPropertyIndex;
1063 ULONG newPropertyIndex;
1065 DWORD parent_grfMode;
1067 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1068 iface, debugstr_w(pwcsName), grfMode,
1069 reserved1, reserved2, ppstg);
1072 * Validate parameters
1075 return STG_E_INVALIDPOINTER;
1078 return STG_E_INVALIDNAME;
1081 * Initialize the out parameter
1086 * Validate the STGM flags
1088 if ( FAILED( validateSTGM(grfMode) ) ||
1089 (grfMode & STGM_DELETEONRELEASE) )
1091 WARN("bad grfMode: 0x%lx\n", grfMode);
1092 return STG_E_INVALIDFLAG;
1096 * Check that we're compatible with the parent's storage mode
1098 parent_grfMode = STGM_ACCESS_MODE( This->base.ancestorStorage->base.openFlags );
1099 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
1101 WARN("access denied\n");
1102 return STG_E_ACCESSDENIED;
1106 * Create a property enumeration and search the properties
1108 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1109 This->base.rootPropertySetIndex);
1111 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1114 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1116 if (foundPropertyIndex != PROPERTY_NULL)
1119 * An element with this name already exists
1121 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1122 IStorage_DestroyElement(iface, pwcsName);
1125 WARN("file already exists\n");
1126 return STG_E_FILEALREADYEXISTS;
1131 * memset the empty property
1133 memset(&newProperty, 0, sizeof(StgProperty));
1135 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1137 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1139 FIXME("name too long\n");
1140 return STG_E_INVALIDNAME;
1143 strcpyW(newProperty.name, pwcsName);
1145 newProperty.propertyType = PROPTYPE_STORAGE;
1146 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1147 newProperty.size.u.LowPart = 0;
1148 newProperty.size.u.HighPart = 0;
1150 newProperty.previousProperty = PROPERTY_NULL;
1151 newProperty.nextProperty = PROPERTY_NULL;
1152 newProperty.dirProperty = PROPERTY_NULL;
1154 /* call CoFileTime to get the current time
1155 newProperty.timeStampS1
1156 newProperty.timeStampD1
1157 newProperty.timeStampS2
1158 newProperty.timeStampD2
1161 /* newStorageProperty.propertyUniqueID */
1164 * Obtain a free property in the property chain
1166 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1169 * Save the new property into the new property spot
1171 StorageImpl_WriteProperty(
1172 This->base.ancestorStorage,
1177 * Find a spot in the property chain for our newly created property.
1179 updatePropertyChain(
1185 * Open it to get a pointer to return.
1187 hr = IStorage_OpenStorage(
1189 (const OLECHAR*)pwcsName,
1196 if( (hr != S_OK) || (*ppstg == NULL))
1206 /***************************************************************************
1210 * Get a free property or create a new one.
1212 static ULONG getFreeProperty(
1213 StorageImpl *storage)
1215 ULONG currentPropertyIndex = 0;
1216 ULONG newPropertyIndex = PROPERTY_NULL;
1217 BOOL readSuccessful = TRUE;
1218 StgProperty currentProperty;
1223 * Start by reading the root property
1225 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1226 currentPropertyIndex,
1230 if (currentProperty.sizeOfNameString == 0)
1233 * The property existis and is available, we found it.
1235 newPropertyIndex = currentPropertyIndex;
1241 * We exhausted the property list, we will create more space below
1243 newPropertyIndex = currentPropertyIndex;
1245 currentPropertyIndex++;
1247 } while (newPropertyIndex == PROPERTY_NULL);
1250 * grow the property chain
1252 if (! readSuccessful)
1254 StgProperty emptyProperty;
1255 ULARGE_INTEGER newSize;
1256 ULONG propertyIndex;
1257 ULONG lastProperty = 0;
1258 ULONG blockCount = 0;
1261 * obtain the new count of property blocks
1263 blockCount = BlockChainStream_GetCount(
1264 storage->base.ancestorStorage->rootBlockChain)+1;
1267 * initialize the size used by the property stream
1269 newSize.u.HighPart = 0;
1270 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1273 * add a property block to the property chain
1275 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1278 * memset the empty property in order to initialize the unused newly
1281 memset(&emptyProperty, 0, sizeof(StgProperty));
1286 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1289 propertyIndex = newPropertyIndex;
1290 propertyIndex < lastProperty;
1293 StorageImpl_WriteProperty(
1294 storage->base.ancestorStorage,
1300 return newPropertyIndex;
1303 /****************************************************************************
1307 * Case insensitive comparaison of StgProperty.name by first considering
1310 * Returns <0 when newPrpoerty < currentProperty
1311 * >0 when newPrpoerty > currentProperty
1312 * 0 when newPrpoerty == currentProperty
1314 static LONG propertyNameCmp(
1315 const OLECHAR *newProperty,
1316 const OLECHAR *currentProperty)
1318 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1323 * We compare the string themselves only when they are of the same length
1325 diff = lstrcmpiW( newProperty, currentProperty);
1331 /****************************************************************************
1335 * Properly link this new element in the property chain.
1337 static void updatePropertyChain(
1338 StorageImpl *storage,
1339 ULONG newPropertyIndex,
1340 StgProperty newProperty)
1342 StgProperty currentProperty;
1345 * Read the root property
1347 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1348 storage->base.rootPropertySetIndex,
1351 if (currentProperty.dirProperty != PROPERTY_NULL)
1354 * The root storage contains some element, therefore, start the research
1355 * for the appropriate location.
1358 ULONG current, next, previous, currentPropertyId;
1361 * Keep the StgProperty sequence number of the storage first property
1363 currentPropertyId = currentProperty.dirProperty;
1368 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1369 currentProperty.dirProperty,
1372 previous = currentProperty.previousProperty;
1373 next = currentProperty.nextProperty;
1374 current = currentPropertyId;
1378 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1382 if (previous != PROPERTY_NULL)
1384 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1391 currentProperty.previousProperty = newPropertyIndex;
1392 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1400 if (next != PROPERTY_NULL)
1402 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1409 currentProperty.nextProperty = newPropertyIndex;
1410 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1419 * Trying to insert an item with the same name in the
1420 * subtree structure.
1425 previous = currentProperty.previousProperty;
1426 next = currentProperty.nextProperty;
1432 * The root storage is empty, link the new property to its dir property
1434 currentProperty.dirProperty = newPropertyIndex;
1435 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1436 storage->base.rootPropertySetIndex,
1442 /*************************************************************************
1445 HRESULT WINAPI StorageImpl_CopyTo(
1447 DWORD ciidExclude, /* [in] */
1448 const IID* rgiidExclude, /* [size_is][unique][in] */
1449 SNB snbExclude, /* [unique][in] */
1450 IStorage* pstgDest) /* [unique][in] */
1452 IEnumSTATSTG *elements = 0;
1453 STATSTG curElement, strStat;
1455 IStorage *pstgTmp, *pstgChild;
1456 IStream *pstrTmp, *pstrChild;
1458 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1459 FIXME("Exclude option not implemented\n");
1461 TRACE("(%p, %ld, %p, %p, %p)\n",
1462 iface, ciidExclude, rgiidExclude,
1463 snbExclude, pstgDest);
1466 * Perform a sanity check
1468 if ( pstgDest == 0 )
1469 return STG_E_INVALIDPOINTER;
1472 * Enumerate the elements
1474 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1482 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1483 IStorage_SetClass( pstgDest, &curElement.clsid );
1488 * Obtain the next element
1490 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1492 if ( hr == S_FALSE )
1494 hr = S_OK; /* done, every element has been copied */
1498 if (curElement.type == STGTY_STORAGE)
1501 * open child source storage
1503 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1504 STGM_READ|STGM_SHARE_EXCLUSIVE,
1505 NULL, 0, &pstgChild );
1511 * Check if destination storage is not a child of the source
1512 * storage, which will cause an infinite loop
1514 if (pstgChild == pstgDest)
1516 IEnumSTATSTG_Release(elements);
1518 return STG_E_ACCESSDENIED;
1522 * create a new storage in destination storage
1524 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1525 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1529 * if it already exist, don't create a new one use this one
1531 if (hr == STG_E_FILEALREADYEXISTS)
1533 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1534 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1535 NULL, 0, &pstgTmp );
1543 * do the copy recursively
1545 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1546 snbExclude, pstgTmp );
1548 IStorage_Release( pstgTmp );
1549 IStorage_Release( pstgChild );
1551 else if (curElement.type == STGTY_STREAM)
1554 * create a new stream in destination storage. If the stream already
1555 * exist, it will be deleted and a new one will be created.
1557 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1558 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1565 * open child stream storage
1567 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1568 STGM_READ|STGM_SHARE_EXCLUSIVE,
1575 * Get the size of the source stream
1577 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1580 * Set the size of the destination stream.
1582 IStream_SetSize(pstrTmp, strStat.cbSize);
1587 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1590 IStream_Release( pstrTmp );
1591 IStream_Release( pstrChild );
1595 WARN("unknown element type: %ld\n", curElement.type);
1598 } while (hr == S_OK);
1603 IEnumSTATSTG_Release(elements);
1608 /*************************************************************************
1609 * MoveElementTo (IStorage)
1611 HRESULT WINAPI StorageImpl_MoveElementTo(
1613 const OLECHAR *pwcsName, /* [string][in] */
1614 IStorage *pstgDest, /* [unique][in] */
1615 const OLECHAR *pwcsNewName,/* [string][in] */
1616 DWORD grfFlags) /* [in] */
1618 FIXME("not implemented!\n");
1622 /*************************************************************************
1625 * Ensures that any changes made to a storage object open in transacted mode
1626 * are reflected in the parent storage
1629 * Wine doesn't implement transacted mode, which seems to be a basic
1630 * optimization, so we can ignore this stub for now.
1632 HRESULT WINAPI StorageImpl_Commit(
1634 DWORD grfCommitFlags)/* [in] */
1636 FIXME("(%ld): stub!\n", grfCommitFlags);
1640 /*************************************************************************
1643 * Discard all changes that have been made since the last commit operation
1645 HRESULT WINAPI StorageImpl_Revert(
1648 FIXME("not implemented!\n");
1652 /*************************************************************************
1653 * DestroyElement (IStorage)
1655 * Strategy: This implementation is built this way for simplicity not for speed.
1656 * I always delete the topmost element of the enumeration and adjust
1657 * the deleted element pointer all the time. This takes longer to
1658 * do but allow to reinvoke DestroyElement whenever we encounter a
1659 * storage object. The optimisation resides in the usage of another
1660 * enumeration strategy that would give all the leaves of a storage
1661 * first. (postfix order)
1663 HRESULT WINAPI StorageImpl_DestroyElement(
1665 const OLECHAR *pwcsName)/* [string][in] */
1667 StorageImpl* const This=(StorageImpl*)iface;
1669 IEnumSTATSTGImpl* propertyEnumeration;
1672 StgProperty propertyToDelete;
1673 StgProperty parentProperty;
1674 ULONG foundPropertyIndexToDelete;
1675 ULONG typeOfRelation;
1676 ULONG parentPropertyId;
1679 iface, debugstr_w(pwcsName));
1682 * Perform a sanity check on the parameters.
1685 return STG_E_INVALIDPOINTER;
1688 * Create a property enumeration to search the property with the given name
1690 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1691 This->base.ancestorStorage,
1692 This->base.rootPropertySetIndex);
1694 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1695 propertyEnumeration,
1699 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1701 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1703 return STG_E_FILENOTFOUND;
1707 * Find the parent property of the property to delete (the one that
1708 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1709 * the parent is This. Otherwise, the parent is one of its sibling...
1713 * First, read This's StgProperty..
1715 res = StorageImpl_ReadProperty(
1716 This->base.ancestorStorage,
1717 This->base.rootPropertySetIndex,
1723 * Second, check to see if by any chance the actual storage (This) is not
1724 * the parent of the property to delete... We never know...
1726 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1729 * Set data as it would have been done in the else part...
1731 typeOfRelation = PROPERTY_RELATION_DIR;
1732 parentPropertyId = This->base.rootPropertySetIndex;
1737 * Create a property enumeration to search the parent properties, and
1738 * delete it once done.
1740 IEnumSTATSTGImpl* propertyEnumeration2;
1742 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1743 This->base.ancestorStorage,
1744 This->base.rootPropertySetIndex);
1746 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1747 propertyEnumeration2,
1748 foundPropertyIndexToDelete,
1752 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1755 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1757 hr = deleteStorageProperty(
1759 foundPropertyIndexToDelete,
1762 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1764 hr = deleteStreamProperty(
1766 foundPropertyIndexToDelete,
1774 * Adjust the property chain
1776 hr = adjustPropertyChain(
1787 /************************************************************************
1788 * StorageImpl_Stat (IStorage)
1790 * This method will retrieve information about this storage object.
1792 * See Windows documentation for more details on IStorage methods.
1794 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1795 STATSTG* pstatstg, /* [out] */
1796 DWORD grfStatFlag) /* [in] */
1798 StorageImpl* const This = (StorageImpl*)iface;
1799 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1801 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1803 CoTaskMemFree(pstatstg->pwcsName);
1804 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1805 strcpyW(pstatstg->pwcsName, This->pwcsName);
1811 /******************************************************************************
1812 * Internal stream list handlers
1815 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1817 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1818 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1821 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1823 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1824 list_remove(&(strm->StrmListEntry));
1827 void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1829 struct list *cur, *cur2;
1830 StgStreamImpl *strm=NULL;
1832 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1833 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1834 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1835 strm->parentStorage = NULL;
1841 /*********************************************************************
1845 * Perform the deletion of a complete storage node
1848 static HRESULT deleteStorageProperty(
1849 StorageImpl *parentStorage,
1850 ULONG indexOfPropertyToDelete,
1851 StgProperty propertyToDelete)
1853 IEnumSTATSTG *elements = 0;
1854 IStorage *childStorage = 0;
1855 STATSTG currentElement;
1857 HRESULT destroyHr = S_OK;
1860 * Open the storage and enumerate it
1862 hr = StorageBaseImpl_OpenStorage(
1863 (IStorage*)parentStorage,
1864 propertyToDelete.name,
1866 STGM_SHARE_EXCLUSIVE,
1877 * Enumerate the elements
1879 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1884 * Obtain the next element
1886 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1889 destroyHr = StorageImpl_DestroyElement(
1890 (IStorage*)childStorage,
1891 (OLECHAR*)currentElement.pwcsName);
1893 CoTaskMemFree(currentElement.pwcsName);
1897 * We need to Reset the enumeration every time because we delete elements
1898 * and the enumeration could be invalid
1900 IEnumSTATSTG_Reset(elements);
1902 } while ((hr == S_OK) && (destroyHr == S_OK));
1905 * Invalidate the property by zeroing its name member.
1907 propertyToDelete.sizeOfNameString = 0;
1909 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1910 indexOfPropertyToDelete,
1913 IStorage_Release(childStorage);
1914 IEnumSTATSTG_Release(elements);
1919 /*********************************************************************
1923 * Perform the deletion of a stream node
1926 static HRESULT deleteStreamProperty(
1927 StorageImpl *parentStorage,
1928 ULONG indexOfPropertyToDelete,
1929 StgProperty propertyToDelete)
1933 ULARGE_INTEGER size;
1935 size.u.HighPart = 0;
1938 hr = StorageBaseImpl_OpenStream(
1939 (IStorage*)parentStorage,
1940 (OLECHAR*)propertyToDelete.name,
1942 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1954 hr = IStream_SetSize(pis, size);
1962 * Release the stream object.
1964 IStream_Release(pis);
1967 * Invalidate the property by zeroing its name member.
1969 propertyToDelete.sizeOfNameString = 0;
1972 * Here we should re-read the property so we get the updated pointer
1973 * but since we are here to zap it, I don't do it...
1975 StorageImpl_WriteProperty(
1976 parentStorage->base.ancestorStorage,
1977 indexOfPropertyToDelete,
1983 /*********************************************************************
1987 * Finds a placeholder for the StgProperty within the Storage
1990 static HRESULT findPlaceholder(
1991 StorageImpl *storage,
1992 ULONG propertyIndexToStore,
1993 ULONG storePropertyIndex,
1996 StgProperty storeProperty;
2001 * Read the storage property
2003 res = StorageImpl_ReadProperty(
2004 storage->base.ancestorStorage,
2013 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2015 if (storeProperty.previousProperty != PROPERTY_NULL)
2017 return findPlaceholder(
2019 propertyIndexToStore,
2020 storeProperty.previousProperty,
2025 storeProperty.previousProperty = propertyIndexToStore;
2028 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2030 if (storeProperty.nextProperty != PROPERTY_NULL)
2032 return findPlaceholder(
2034 propertyIndexToStore,
2035 storeProperty.nextProperty,
2040 storeProperty.nextProperty = propertyIndexToStore;
2043 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2045 if (storeProperty.dirProperty != PROPERTY_NULL)
2047 return findPlaceholder(
2049 propertyIndexToStore,
2050 storeProperty.dirProperty,
2055 storeProperty.dirProperty = propertyIndexToStore;
2059 hr = StorageImpl_WriteProperty(
2060 storage->base.ancestorStorage,
2072 /*************************************************************************
2076 * This method takes the previous and the next property link of a property
2077 * to be deleted and find them a place in the Storage.
2079 static HRESULT adjustPropertyChain(
2081 StgProperty propertyToDelete,
2082 StgProperty parentProperty,
2083 ULONG parentPropertyId,
2086 ULONG newLinkProperty = PROPERTY_NULL;
2087 BOOL needToFindAPlaceholder = FALSE;
2088 ULONG storeNode = PROPERTY_NULL;
2089 ULONG toStoreNode = PROPERTY_NULL;
2090 INT relationType = 0;
2094 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2096 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2099 * Set the parent previous to the property to delete previous
2101 newLinkProperty = propertyToDelete.previousProperty;
2103 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2106 * We also need to find a storage for the other link, setup variables
2107 * to do this at the end...
2109 needToFindAPlaceholder = TRUE;
2110 storeNode = propertyToDelete.previousProperty;
2111 toStoreNode = propertyToDelete.nextProperty;
2112 relationType = PROPERTY_RELATION_NEXT;
2115 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2118 * Set the parent previous to the property to delete next
2120 newLinkProperty = propertyToDelete.nextProperty;
2124 * Link it for real...
2126 parentProperty.previousProperty = newLinkProperty;
2129 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2131 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2134 * Set the parent next to the property to delete next previous
2136 newLinkProperty = propertyToDelete.previousProperty;
2138 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2141 * We also need to find a storage for the other link, setup variables
2142 * to do this at the end...
2144 needToFindAPlaceholder = TRUE;
2145 storeNode = propertyToDelete.previousProperty;
2146 toStoreNode = propertyToDelete.nextProperty;
2147 relationType = PROPERTY_RELATION_NEXT;
2150 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2153 * Set the parent next to the property to delete next
2155 newLinkProperty = propertyToDelete.nextProperty;
2159 * Link it for real...
2161 parentProperty.nextProperty = newLinkProperty;
2163 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2165 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2168 * Set the parent dir to the property to delete previous
2170 newLinkProperty = propertyToDelete.previousProperty;
2172 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2175 * We also need to find a storage for the other link, setup variables
2176 * to do this at the end...
2178 needToFindAPlaceholder = TRUE;
2179 storeNode = propertyToDelete.previousProperty;
2180 toStoreNode = propertyToDelete.nextProperty;
2181 relationType = PROPERTY_RELATION_NEXT;
2184 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2187 * Set the parent dir to the property to delete next
2189 newLinkProperty = propertyToDelete.nextProperty;
2193 * Link it for real...
2195 parentProperty.dirProperty = newLinkProperty;
2199 * Write back the parent property
2201 res = StorageImpl_WriteProperty(
2202 This->base.ancestorStorage,
2211 * If a placeholder is required for the other link, then, find one and
2212 * get out of here...
2214 if (needToFindAPlaceholder)
2216 hr = findPlaceholder(
2227 /******************************************************************************
2228 * SetElementTimes (IStorage)
2230 HRESULT WINAPI StorageImpl_SetElementTimes(
2232 const OLECHAR *pwcsName,/* [string][in] */
2233 const FILETIME *pctime, /* [in] */
2234 const FILETIME *patime, /* [in] */
2235 const FILETIME *pmtime) /* [in] */
2237 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2241 /******************************************************************************
2242 * SetStateBits (IStorage)
2244 HRESULT WINAPI StorageImpl_SetStateBits(
2246 DWORD grfStateBits,/* [in] */
2247 DWORD grfMask) /* [in] */
2249 FIXME("not implemented!\n");
2254 * Virtual function table for the IStorage32Impl class.
2256 static const IStorageVtbl Storage32Impl_Vtbl =
2258 StorageBaseImpl_QueryInterface,
2259 StorageBaseImpl_AddRef,
2260 StorageBaseImpl_Release,
2261 StorageBaseImpl_CreateStream,
2262 StorageBaseImpl_OpenStream,
2263 StorageImpl_CreateStorage,
2264 StorageBaseImpl_OpenStorage,
2266 StorageImpl_MoveElementTo,
2269 StorageBaseImpl_EnumElements,
2270 StorageImpl_DestroyElement,
2271 StorageBaseImpl_RenameElement,
2272 StorageImpl_SetElementTimes,
2273 StorageBaseImpl_SetClass,
2274 StorageImpl_SetStateBits,
2278 HRESULT StorageImpl_Construct(
2288 StgProperty currentProperty;
2289 BOOL readSuccessful;
2290 ULONG currentPropertyIndex;
2292 if ( FAILED( validateSTGM(openFlags) ))
2293 return STG_E_INVALIDFLAG;
2295 memset(This, 0, sizeof(StorageImpl));
2298 * Initialize stream list
2301 list_init(&This->base.strmHead);
2304 * Initialize the virtual function table.
2306 This->base.lpVtbl = &Storage32Impl_Vtbl;
2307 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2308 This->base.v_destructor = &StorageImpl_Destroy;
2309 This->base.openFlags = openFlags;
2312 * This is the top-level storage so initialize the ancestor pointer
2315 This->base.ancestorStorage = This;
2318 * Initialize the physical support of the storage.
2320 This->hFile = hFile;
2323 * Store copy of file path.
2326 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2327 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2328 if (!This->pwcsName)
2329 return STG_E_INSUFFICIENTMEMORY;
2330 strcpyW(This->pwcsName, pwcsName);
2334 * Initialize the big block cache.
2336 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2337 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2338 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2344 if (This->bigBlockFile == 0)
2349 ULARGE_INTEGER size;
2350 BYTE* bigBlockBuffer;
2353 * Initialize all header variables:
2354 * - The big block depot consists of one block and it is at block 0
2355 * - The properties start at block 1
2356 * - There is no small block depot
2358 memset( This->bigBlockDepotStart,
2360 sizeof(This->bigBlockDepotStart));
2362 This->bigBlockDepotCount = 1;
2363 This->bigBlockDepotStart[0] = 0;
2364 This->rootStartBlock = 1;
2365 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2366 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2367 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2368 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2369 This->extBigBlockDepotCount = 0;
2371 StorageImpl_SaveFileHeader(This);
2374 * Add one block for the big block depot and one block for the properties
2376 size.u.HighPart = 0;
2377 size.u.LowPart = This->bigBlockSize * 3;
2378 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2381 * Initialize the big block depot
2383 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2384 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2385 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2386 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2387 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2392 * Load the header for the file.
2394 hr = StorageImpl_LoadFileHeader(This);
2398 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2405 * There is no block depot cached yet.
2407 This->indexBlockDepotCached = 0xFFFFFFFF;
2410 * Start searching for free blocks with block 0.
2412 This->prevFreeBlock = 0;
2415 * Create the block chain abstractions.
2417 if(!(This->rootBlockChain =
2418 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2419 return STG_E_READFAULT;
2421 if(!(This->smallBlockDepotChain =
2422 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2424 return STG_E_READFAULT;
2427 * Write the root property (memory only)
2431 StgProperty rootProp;
2433 * Initialize the property chain
2435 memset(&rootProp, 0, sizeof(rootProp));
2436 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2437 sizeof(rootProp.name)/sizeof(WCHAR) );
2438 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2439 rootProp.propertyType = PROPTYPE_ROOT;
2440 rootProp.previousProperty = PROPERTY_NULL;
2441 rootProp.nextProperty = PROPERTY_NULL;
2442 rootProp.dirProperty = PROPERTY_NULL;
2443 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2444 rootProp.size.u.HighPart = 0;
2445 rootProp.size.u.LowPart = 0;
2447 StorageImpl_WriteProperty(This, 0, &rootProp);
2451 * Find the ID of the root in the property sets.
2453 currentPropertyIndex = 0;
2457 readSuccessful = StorageImpl_ReadProperty(
2459 currentPropertyIndex,
2464 if ( (currentProperty.sizeOfNameString != 0 ) &&
2465 (currentProperty.propertyType == PROPTYPE_ROOT) )
2467 This->base.rootPropertySetIndex = currentPropertyIndex;
2471 currentPropertyIndex++;
2473 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2475 if (!readSuccessful)
2478 return STG_E_READFAULT;
2482 * Create the block chain abstraction for the small block root chain.
2484 if(!(This->smallBlockRootChain =
2485 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2486 return STG_E_READFAULT;
2491 void StorageImpl_Destroy(StorageBaseImpl* iface)
2493 StorageImpl *This = (StorageImpl*) iface;
2494 TRACE("(%p)\n", This);
2496 StorageBaseImpl_DeleteAll(&This->base);
2498 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2500 BlockChainStream_Destroy(This->smallBlockRootChain);
2501 BlockChainStream_Destroy(This->rootBlockChain);
2502 BlockChainStream_Destroy(This->smallBlockDepotChain);
2504 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2505 HeapFree(GetProcessHeap(), 0, This);
2508 /******************************************************************************
2509 * Storage32Impl_GetNextFreeBigBlock
2511 * Returns the index of the next free big block.
2512 * If the big block depot is filled, this method will enlarge it.
2515 ULONG StorageImpl_GetNextFreeBigBlock(
2518 ULONG depotBlockIndexPos;
2520 ULONG depotBlockOffset;
2521 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2522 ULONG nextBlockIndex = BLOCK_SPECIAL;
2524 ULONG freeBlock = BLOCK_UNUSED;
2526 depotIndex = This->prevFreeBlock / blocksPerDepot;
2527 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2530 * Scan the entire big block depot until we find a block marked free
2532 while (nextBlockIndex != BLOCK_UNUSED)
2534 if (depotIndex < COUNT_BBDEPOTINHEADER)
2536 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2539 * Grow the primary depot.
2541 if (depotBlockIndexPos == BLOCK_UNUSED)
2543 depotBlockIndexPos = depotIndex*blocksPerDepot;
2546 * Add a block depot.
2548 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2549 This->bigBlockDepotCount++;
2550 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2553 * Flag it as a block depot.
2555 StorageImpl_SetNextBlockInChain(This,
2559 /* Save new header information.
2561 StorageImpl_SaveFileHeader(This);
2566 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2568 if (depotBlockIndexPos == BLOCK_UNUSED)
2571 * Grow the extended depot.
2573 ULONG extIndex = BLOCK_UNUSED;
2574 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2575 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2577 if (extBlockOffset == 0)
2579 /* We need an extended block.
2581 extIndex = Storage32Impl_AddExtBlockDepot(This);
2582 This->extBigBlockDepotCount++;
2583 depotBlockIndexPos = extIndex + 1;
2586 depotBlockIndexPos = depotIndex * blocksPerDepot;
2589 * Add a block depot and mark it in the extended block.
2591 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2592 This->bigBlockDepotCount++;
2593 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2595 /* Flag the block depot.
2597 StorageImpl_SetNextBlockInChain(This,
2601 /* If necessary, flag the extended depot block.
2603 if (extIndex != BLOCK_UNUSED)
2604 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2606 /* Save header information.
2608 StorageImpl_SaveFileHeader(This);
2612 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2614 if (depotBuffer != 0)
2616 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2617 ( nextBlockIndex != BLOCK_UNUSED))
2619 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2621 if (nextBlockIndex == BLOCK_UNUSED)
2623 freeBlock = (depotIndex * blocksPerDepot) +
2624 (depotBlockOffset/sizeof(ULONG));
2627 depotBlockOffset += sizeof(ULONG);
2630 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2634 depotBlockOffset = 0;
2637 This->prevFreeBlock = freeBlock;
2642 /******************************************************************************
2643 * Storage32Impl_AddBlockDepot
2645 * This will create a depot block, essentially it is a block initialized
2648 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2652 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2655 * Initialize blocks as free
2657 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2659 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2662 /******************************************************************************
2663 * Storage32Impl_GetExtDepotBlock
2665 * Returns the index of the block that corresponds to the specified depot
2666 * index. This method is only for depot indexes equal or greater than
2667 * COUNT_BBDEPOTINHEADER.
2669 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2671 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2672 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2673 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2674 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2675 ULONG blockIndex = BLOCK_UNUSED;
2676 ULONG extBlockIndex = This->extBigBlockDepotStart;
2678 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2680 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2681 return BLOCK_UNUSED;
2683 while (extBlockCount > 0)
2685 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2689 if (extBlockIndex != BLOCK_UNUSED)
2693 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2695 if (depotBuffer != 0)
2697 StorageUtl_ReadDWord(depotBuffer,
2698 extBlockOffset * sizeof(ULONG),
2701 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2708 /******************************************************************************
2709 * Storage32Impl_SetExtDepotBlock
2711 * Associates the specified block index to the specified depot index.
2712 * This method is only for depot indexes equal or greater than
2713 * COUNT_BBDEPOTINHEADER.
2715 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2719 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2720 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2721 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2722 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2723 ULONG extBlockIndex = This->extBigBlockDepotStart;
2725 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2727 while (extBlockCount > 0)
2729 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2733 if (extBlockIndex != BLOCK_UNUSED)
2737 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2739 if (depotBuffer != 0)
2741 StorageUtl_WriteDWord(depotBuffer,
2742 extBlockOffset * sizeof(ULONG),
2745 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2750 /******************************************************************************
2751 * Storage32Impl_AddExtBlockDepot
2753 * Creates an extended depot block.
2755 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2757 ULONG numExtBlocks = This->extBigBlockDepotCount;
2758 ULONG nextExtBlock = This->extBigBlockDepotStart;
2759 BYTE* depotBuffer = NULL;
2760 ULONG index = BLOCK_UNUSED;
2761 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2762 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2763 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2765 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2766 blocksPerDepotBlock;
2768 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2771 * The first extended block.
2773 This->extBigBlockDepotStart = index;
2779 * Follow the chain to the last one.
2781 for (i = 0; i < (numExtBlocks - 1); i++)
2783 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2787 * Add the new extended block to the chain.
2789 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2790 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2791 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2795 * Initialize this block.
2797 depotBuffer = StorageImpl_GetBigBlock(This, index);
2798 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2799 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2804 /******************************************************************************
2805 * Storage32Impl_FreeBigBlock
2807 * This method will flag the specified block as free in the big block depot.
2809 void StorageImpl_FreeBigBlock(
2813 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2815 if (blockIndex < This->prevFreeBlock)
2816 This->prevFreeBlock = blockIndex;
2819 /************************************************************************
2820 * Storage32Impl_GetNextBlockInChain
2822 * This method will retrieve the block index of the next big block in
2825 * Params: This - Pointer to the Storage object.
2826 * blockIndex - Index of the block to retrieve the chain
2828 * nextBlockIndex - receives the return value.
2830 * Returns: This method returns the index of the next block in the chain.
2831 * It will return the constants:
2832 * BLOCK_SPECIAL - If the block given was not part of a
2834 * BLOCK_END_OF_CHAIN - If the block given was the last in
2836 * BLOCK_UNUSED - If the block given was not past of a chain
2838 * BLOCK_EXTBBDEPOT - This block is part of the extended
2841 * See Windows documentation for more details on IStorage methods.
2843 HRESULT StorageImpl_GetNextBlockInChain(
2846 ULONG* nextBlockIndex)
2848 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2849 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2850 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2852 ULONG depotBlockIndexPos;
2855 *nextBlockIndex = BLOCK_SPECIAL;
2857 if(depotBlockCount >= This->bigBlockDepotCount)
2859 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2860 This->bigBlockDepotCount);
2861 return STG_E_READFAULT;
2865 * Cache the currently accessed depot block.
2867 if (depotBlockCount != This->indexBlockDepotCached)
2869 This->indexBlockDepotCached = depotBlockCount;
2871 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2873 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2878 * We have to look in the extended depot.
2880 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2883 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2886 return STG_E_READFAULT;
2888 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2890 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2891 This->blockDepotCached[index] = *nextBlockIndex;
2893 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2896 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2901 /******************************************************************************
2902 * Storage32Impl_GetNextExtendedBlock
2904 * Given an extended block this method will return the next extended block.
2907 * The last ULONG of an extended block is the block index of the next
2908 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2912 * - The index of the next extended block
2913 * - BLOCK_UNUSED: there is no next extended block.
2914 * - Any other return values denotes failure.
2916 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2918 ULONG nextBlockIndex = BLOCK_SPECIAL;
2919 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2922 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2926 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2928 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2931 return nextBlockIndex;
2934 /******************************************************************************
2935 * Storage32Impl_SetNextBlockInChain
2937 * This method will write the index of the specified block's next block
2938 * in the big block depot.
2940 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2943 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2944 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2945 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2948 void StorageImpl_SetNextBlockInChain(
2953 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2954 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2955 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2956 ULONG depotBlockIndexPos;
2959 assert(depotBlockCount < This->bigBlockDepotCount);
2960 assert(blockIndex != nextBlock);
2962 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2964 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2969 * We have to look in the extended depot.
2971 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2974 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2978 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2979 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2983 * Update the cached block depot, if necessary.
2985 if (depotBlockCount == This->indexBlockDepotCached)
2987 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2991 /******************************************************************************
2992 * Storage32Impl_LoadFileHeader
2994 * This method will read in the file header, i.e. big block index -1.
2996 HRESULT StorageImpl_LoadFileHeader(
2999 HRESULT hr = STG_E_FILENOTFOUND;
3000 void* headerBigBlock = NULL;
3005 * Get a pointer to the big block of data containing the header.
3007 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
3010 * Extract the information from the header.
3012 if (headerBigBlock!=0)
3015 * Check for the "magic number" signature and return an error if it is not
3018 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3020 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3021 return STG_E_OLDFORMAT;
3024 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3026 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3027 return STG_E_INVALIDHEADER;
3030 StorageUtl_ReadWord(
3032 OFFSET_BIGBLOCKSIZEBITS,
3033 &This->bigBlockSizeBits);
3035 StorageUtl_ReadWord(
3037 OFFSET_SMALLBLOCKSIZEBITS,
3038 &This->smallBlockSizeBits);
3040 StorageUtl_ReadDWord(
3042 OFFSET_BBDEPOTCOUNT,
3043 &This->bigBlockDepotCount);
3045 StorageUtl_ReadDWord(
3047 OFFSET_ROOTSTARTBLOCK,
3048 &This->rootStartBlock);
3050 StorageUtl_ReadDWord(
3052 OFFSET_SBDEPOTSTART,
3053 &This->smallBlockDepotStart);
3055 StorageUtl_ReadDWord(
3057 OFFSET_EXTBBDEPOTSTART,
3058 &This->extBigBlockDepotStart);
3060 StorageUtl_ReadDWord(
3062 OFFSET_EXTBBDEPOTCOUNT,
3063 &This->extBigBlockDepotCount);
3065 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3067 StorageUtl_ReadDWord(
3069 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3070 &(This->bigBlockDepotStart[index]));
3074 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3078 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3079 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3083 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3084 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3088 * Right now, the code is making some assumptions about the size of the
3089 * blocks, just make sure they are what we're expecting.
3091 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3092 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3094 WARN("Broken OLE storage file\n");
3095 hr = STG_E_INVALIDHEADER;
3101 * Release the block.
3103 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3109 /******************************************************************************
3110 * Storage32Impl_SaveFileHeader
3112 * This method will save to the file the header, i.e. big block -1.
3114 void StorageImpl_SaveFileHeader(
3117 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3122 * Get a pointer to the big block of data containing the header.
3124 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3127 * If the block read failed, the file is probably new.
3132 * Initialize for all unknown fields.
3134 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3137 * Initialize the magic number.
3139 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3142 * And a bunch of things we don't know what they mean
3144 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3145 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3146 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3147 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3151 * Write the information to the header.
3153 StorageUtl_WriteWord(
3155 OFFSET_BIGBLOCKSIZEBITS,
3156 This->bigBlockSizeBits);
3158 StorageUtl_WriteWord(
3160 OFFSET_SMALLBLOCKSIZEBITS,
3161 This->smallBlockSizeBits);
3163 StorageUtl_WriteDWord(
3165 OFFSET_BBDEPOTCOUNT,
3166 This->bigBlockDepotCount);
3168 StorageUtl_WriteDWord(
3170 OFFSET_ROOTSTARTBLOCK,
3171 This->rootStartBlock);
3173 StorageUtl_WriteDWord(
3175 OFFSET_SBDEPOTSTART,
3176 This->smallBlockDepotStart);
3178 StorageUtl_WriteDWord(
3180 OFFSET_SBDEPOTCOUNT,
3181 This->smallBlockDepotChain ?
3182 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3184 StorageUtl_WriteDWord(
3186 OFFSET_EXTBBDEPOTSTART,
3187 This->extBigBlockDepotStart);
3189 StorageUtl_WriteDWord(
3191 OFFSET_EXTBBDEPOTCOUNT,
3192 This->extBigBlockDepotCount);
3194 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3196 StorageUtl_WriteDWord(
3198 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3199 (This->bigBlockDepotStart[index]));
3203 * Write the big block back to the file.
3205 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3208 /******************************************************************************
3209 * Storage32Impl_ReadProperty
3211 * This method will read the specified property from the property chain.
3213 BOOL StorageImpl_ReadProperty(
3216 StgProperty* buffer)
3218 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3219 ULARGE_INTEGER offsetInPropSet;
3220 BOOL readSuccessful;
3223 offsetInPropSet.u.HighPart = 0;
3224 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3226 readSuccessful = BlockChainStream_ReadAt(
3227 This->rootBlockChain,
3235 /* replace the name of root entry (often "Root Entry") by the file name */
3236 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3237 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3239 memset(buffer->name, 0, sizeof(buffer->name));
3243 PROPERTY_NAME_BUFFER_LEN );
3244 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3246 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3248 StorageUtl_ReadWord(
3250 OFFSET_PS_NAMELENGTH,
3251 &buffer->sizeOfNameString);
3253 StorageUtl_ReadDWord(
3255 OFFSET_PS_PREVIOUSPROP,
3256 &buffer->previousProperty);
3258 StorageUtl_ReadDWord(
3261 &buffer->nextProperty);
3263 StorageUtl_ReadDWord(
3266 &buffer->dirProperty);
3268 StorageUtl_ReadGUID(
3271 &buffer->propertyUniqueID);
3273 StorageUtl_ReadDWord(
3276 &buffer->timeStampS1);
3278 StorageUtl_ReadDWord(
3281 &buffer->timeStampD1);
3283 StorageUtl_ReadDWord(
3286 &buffer->timeStampS2);
3288 StorageUtl_ReadDWord(
3291 &buffer->timeStampD2);
3293 StorageUtl_ReadDWord(
3295 OFFSET_PS_STARTBLOCK,
3296 &buffer->startingBlock);
3298 StorageUtl_ReadDWord(
3301 &buffer->size.u.LowPart);
3303 buffer->size.u.HighPart = 0;
3306 return readSuccessful;
3309 /*********************************************************************
3310 * Write the specified property into the property chain
3312 BOOL StorageImpl_WriteProperty(
3315 StgProperty* buffer)
3317 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3318 ULARGE_INTEGER offsetInPropSet;
3319 BOOL writeSuccessful;
3322 offsetInPropSet.u.HighPart = 0;
3323 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3325 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3328 currentProperty + OFFSET_PS_NAME,
3330 PROPERTY_NAME_BUFFER_LEN );
3332 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3334 StorageUtl_WriteWord(
3336 OFFSET_PS_NAMELENGTH,
3337 buffer->sizeOfNameString);
3339 StorageUtl_WriteDWord(
3341 OFFSET_PS_PREVIOUSPROP,
3342 buffer->previousProperty);
3344 StorageUtl_WriteDWord(
3347 buffer->nextProperty);
3349 StorageUtl_WriteDWord(
3352 buffer->dirProperty);
3354 StorageUtl_WriteGUID(
3357 &buffer->propertyUniqueID);
3359 StorageUtl_WriteDWord(
3362 buffer->timeStampS1);
3364 StorageUtl_WriteDWord(
3367 buffer->timeStampD1);
3369 StorageUtl_WriteDWord(
3372 buffer->timeStampS2);
3374 StorageUtl_WriteDWord(
3377 buffer->timeStampD2);
3379 StorageUtl_WriteDWord(
3381 OFFSET_PS_STARTBLOCK,
3382 buffer->startingBlock);
3384 StorageUtl_WriteDWord(
3387 buffer->size.u.LowPart);
3389 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3394 return writeSuccessful;
3397 BOOL StorageImpl_ReadBigBlock(
3402 void* bigBlockBuffer;
3404 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3406 if (bigBlockBuffer!=0)
3408 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3410 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3418 BOOL StorageImpl_WriteBigBlock(
3423 void* bigBlockBuffer;
3425 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3427 if (bigBlockBuffer!=0)
3429 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3431 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3439 void* StorageImpl_GetROBigBlock(
3443 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3446 void* StorageImpl_GetBigBlock(
3450 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3453 void StorageImpl_ReleaseBigBlock(
3457 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3460 /******************************************************************************
3461 * Storage32Impl_SmallBlocksToBigBlocks
3463 * This method will convert a small block chain to a big block chain.
3464 * The small block chain will be destroyed.
3466 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3468 SmallBlockChainStream** ppsbChain)
3470 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3471 ULARGE_INTEGER size, offset;
3472 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3473 ULONG propertyIndex;
3475 HRESULT successRead;
3476 StgProperty chainProperty;
3478 BlockChainStream *bbTempChain = NULL;
3479 BlockChainStream *bigBlockChain = NULL;
3482 * Create a temporary big block chain that doesn't have
3483 * an associated property. This temporary chain will be
3484 * used to copy data from small blocks to big blocks.
3486 bbTempChain = BlockChainStream_Construct(This,
3489 if(!bbTempChain) return NULL;
3491 * Grow the big block chain.
3493 size = SmallBlockChainStream_GetSize(*ppsbChain);
3494 BlockChainStream_SetSize(bbTempChain, size);
3497 * Copy the contents of the small block chain to the big block chain
3498 * by small block size increments.
3500 offset.u.LowPart = 0;
3501 offset.u.HighPart = 0;
3505 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3508 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3510 DEF_SMALL_BLOCK_SIZE,
3513 if (FAILED(successRead))
3518 cbTotalRead += cbRead;
3520 successWrite = BlockChainStream_WriteAt(bbTempChain,
3529 cbTotalWritten += cbWritten;
3530 offset.u.LowPart += This->smallBlockSize;
3532 } while (cbRead > 0);
3533 HeapFree(GetProcessHeap(),0,buffer);
3535 assert(cbTotalRead == cbTotalWritten);
3538 * Destroy the small block chain.
3540 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3541 size.u.HighPart = 0;
3543 SmallBlockChainStream_SetSize(*ppsbChain, size);
3544 SmallBlockChainStream_Destroy(*ppsbChain);
3548 * Change the property information. This chain is now a big block chain
3549 * and it doesn't reside in the small blocks chain anymore.
3551 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3553 chainProperty.startingBlock = bbHeadOfChain;
3555 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3558 * Destroy the temporary propertyless big block chain.
3559 * Create a new big block chain associated with this property.
3561 BlockChainStream_Destroy(bbTempChain);
3562 bigBlockChain = BlockChainStream_Construct(This,
3566 return bigBlockChain;
3569 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3571 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3573 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3574 HeapFree(GetProcessHeap(), 0, This);
3577 /******************************************************************************
3579 ** Storage32InternalImpl_Commit
3581 ** The non-root storages cannot be opened in transacted mode thus this function
3584 HRESULT WINAPI StorageInternalImpl_Commit(
3586 DWORD grfCommitFlags) /* [in] */
3591 /******************************************************************************
3593 ** Storage32InternalImpl_Revert
3595 ** The non-root storages cannot be opened in transacted mode thus this function
3598 HRESULT WINAPI StorageInternalImpl_Revert(
3604 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3606 IStorage_Release((IStorage*)This->parentStorage);
3607 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3608 HeapFree(GetProcessHeap(), 0, This);
3611 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3612 IEnumSTATSTG* iface,
3616 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3619 * Perform a sanity check on the parameters.
3622 return E_INVALIDARG;
3625 * Initialize the return parameter.
3630 * Compare the riid with the interface IDs implemented by this object.
3632 if (IsEqualGUID(&IID_IUnknown, riid) ||
3633 IsEqualGUID(&IID_IStorage, riid))
3635 *ppvObject = (IEnumSTATSTG*)This;
3636 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3640 return E_NOINTERFACE;
3643 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3644 IEnumSTATSTG* iface)
3646 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3647 return InterlockedIncrement(&This->ref);
3650 ULONG WINAPI IEnumSTATSTGImpl_Release(
3651 IEnumSTATSTG* iface)
3653 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3657 newRef = InterlockedDecrement(&This->ref);
3660 * If the reference count goes down to 0, perform suicide.
3664 IEnumSTATSTGImpl_Destroy(This);
3670 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3671 IEnumSTATSTG* iface,
3674 ULONG* pceltFetched)
3676 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3678 StgProperty currentProperty;
3679 STATSTG* currentReturnStruct = rgelt;
3680 ULONG objectFetched = 0;
3681 ULONG currentSearchNode;
3684 * Perform a sanity check on the parameters.
3686 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3687 return E_INVALIDARG;
3690 * To avoid the special case, get another pointer to a ULONG value if
3691 * the caller didn't supply one.
3693 if (pceltFetched==0)
3694 pceltFetched = &objectFetched;
3697 * Start the iteration, we will iterate until we hit the end of the
3698 * linked list or until we hit the number of items to iterate through
3703 * Start with the node at the top of the stack.
3705 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3707 while ( ( *pceltFetched < celt) &&
3708 ( currentSearchNode!=PROPERTY_NULL) )
3711 * Remove the top node from the stack
3713 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3716 * Read the property from the storage.
3718 StorageImpl_ReadProperty(This->parentStorage,
3723 * Copy the information to the return buffer.
3725 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3730 * Step to the next item in the iteration
3733 currentReturnStruct++;
3736 * Push the next search node in the search stack.
3738 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3741 * continue the iteration.
3743 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3746 if (*pceltFetched == celt)
3753 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3754 IEnumSTATSTG* iface,
3757 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3759 StgProperty currentProperty;
3760 ULONG objectFetched = 0;
3761 ULONG currentSearchNode;
3764 * Start with the node at the top of the stack.
3766 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3768 while ( (objectFetched < celt) &&
3769 (currentSearchNode!=PROPERTY_NULL) )
3772 * Remove the top node from the stack
3774 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3777 * Read the property from the storage.
3779 StorageImpl_ReadProperty(This->parentStorage,
3784 * Step to the next item in the iteration
3789 * Push the next search node in the search stack.
3791 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3794 * continue the iteration.
3796 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3799 if (objectFetched == celt)
3805 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3806 IEnumSTATSTG* iface)
3808 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3810 StgProperty rootProperty;
3811 BOOL readSuccessful;
3814 * Re-initialize the search stack to an empty stack
3816 This->stackSize = 0;
3819 * Read the root property from the storage.
3821 readSuccessful = StorageImpl_ReadProperty(
3822 This->parentStorage,
3823 This->firstPropertyNode,
3828 assert(rootProperty.sizeOfNameString!=0);
3831 * Push the search node in the search stack.
3833 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3839 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3840 IEnumSTATSTG* iface,
3841 IEnumSTATSTG** ppenum)
3843 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3845 IEnumSTATSTGImpl* newClone;
3848 * Perform a sanity check on the parameters.
3851 return E_INVALIDARG;
3853 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3854 This->firstPropertyNode);
3858 * The new clone enumeration must point to the same current node as
3861 newClone->stackSize = This->stackSize ;
3862 newClone->stackMaxSize = This->stackMaxSize ;
3863 newClone->stackToVisit =
3864 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3867 newClone->stackToVisit,
3869 sizeof(ULONG) * newClone->stackSize);
3871 *ppenum = (IEnumSTATSTG*)newClone;
3874 * Don't forget to nail down a reference to the clone before
3877 IEnumSTATSTGImpl_AddRef(*ppenum);
3882 INT IEnumSTATSTGImpl_FindParentProperty(
3883 IEnumSTATSTGImpl *This,
3884 ULONG childProperty,
3885 StgProperty *currentProperty,
3888 ULONG currentSearchNode;
3892 * To avoid the special case, get another pointer to a ULONG value if
3893 * the caller didn't supply one.
3896 thisNodeId = &foundNode;
3899 * Start with the node at the top of the stack.
3901 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3904 while (currentSearchNode!=PROPERTY_NULL)
3907 * Store the current node in the returned parameters
3909 *thisNodeId = currentSearchNode;
3912 * Remove the top node from the stack
3914 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3917 * Read the property from the storage.
3919 StorageImpl_ReadProperty(
3920 This->parentStorage,
3924 if (currentProperty->previousProperty == childProperty)
3925 return PROPERTY_RELATION_PREVIOUS;
3927 else if (currentProperty->nextProperty == childProperty)
3928 return PROPERTY_RELATION_NEXT;
3930 else if (currentProperty->dirProperty == childProperty)
3931 return PROPERTY_RELATION_DIR;
3934 * Push the next search node in the search stack.
3936 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3939 * continue the iteration.
3941 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3944 return PROPERTY_NULL;
3947 ULONG IEnumSTATSTGImpl_FindProperty(
3948 IEnumSTATSTGImpl* This,
3949 const OLECHAR* lpszPropName,
3950 StgProperty* currentProperty)
3952 ULONG currentSearchNode;
3955 * Start with the node at the top of the stack.
3957 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3959 while (currentSearchNode!=PROPERTY_NULL)
3962 * Remove the top node from the stack
3964 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3967 * Read the property from the storage.
3969 StorageImpl_ReadProperty(This->parentStorage,
3973 if ( propertyNameCmp(
3974 (const OLECHAR*)currentProperty->name,
3975 (const OLECHAR*)lpszPropName) == 0)
3976 return currentSearchNode;
3979 * Push the next search node in the search stack.
3981 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3984 * continue the iteration.
3986 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3989 return PROPERTY_NULL;
3992 void IEnumSTATSTGImpl_PushSearchNode(
3993 IEnumSTATSTGImpl* This,
3996 StgProperty rootProperty;
3997 BOOL readSuccessful;
4000 * First, make sure we're not trying to push an unexisting node.
4002 if (nodeToPush==PROPERTY_NULL)
4006 * First push the node to the stack
4008 if (This->stackSize == This->stackMaxSize)
4010 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4012 This->stackToVisit = HeapReAlloc(
4016 sizeof(ULONG) * This->stackMaxSize);
4019 This->stackToVisit[This->stackSize] = nodeToPush;
4023 * Read the root property from the storage.
4025 readSuccessful = StorageImpl_ReadProperty(
4026 This->parentStorage,
4032 assert(rootProperty.sizeOfNameString!=0);
4035 * Push the previous search node in the search stack.
4037 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4041 ULONG IEnumSTATSTGImpl_PopSearchNode(
4042 IEnumSTATSTGImpl* This,
4047 if (This->stackSize == 0)
4048 return PROPERTY_NULL;
4050 topNode = This->stackToVisit[This->stackSize-1];
4059 * Virtual function table for the IEnumSTATSTGImpl class.
4061 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4063 IEnumSTATSTGImpl_QueryInterface,
4064 IEnumSTATSTGImpl_AddRef,
4065 IEnumSTATSTGImpl_Release,
4066 IEnumSTATSTGImpl_Next,
4067 IEnumSTATSTGImpl_Skip,
4068 IEnumSTATSTGImpl_Reset,
4069 IEnumSTATSTGImpl_Clone
4072 /******************************************************************************
4073 ** IEnumSTATSTGImpl implementation
4076 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4077 StorageImpl* parentStorage,
4078 ULONG firstPropertyNode)
4080 IEnumSTATSTGImpl* newEnumeration;
4082 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4084 if (newEnumeration!=0)
4087 * Set-up the virtual function table and reference count.
4089 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4090 newEnumeration->ref = 0;
4093 * We want to nail-down the reference to the storage in case the
4094 * enumeration out-lives the storage in the client application.
4096 newEnumeration->parentStorage = parentStorage;
4097 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4099 newEnumeration->firstPropertyNode = firstPropertyNode;
4102 * Initialize the search stack
4104 newEnumeration->stackSize = 0;
4105 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4106 newEnumeration->stackToVisit =
4107 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4110 * Make sure the current node of the iterator is the first one.
4112 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4115 return newEnumeration;
4119 * Virtual function table for the Storage32InternalImpl class.
4121 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4123 StorageBaseImpl_QueryInterface,
4124 StorageBaseImpl_AddRef,
4125 StorageBaseImpl_Release,
4126 StorageBaseImpl_CreateStream,
4127 StorageBaseImpl_OpenStream,
4128 StorageImpl_CreateStorage,
4129 StorageBaseImpl_OpenStorage,
4131 StorageImpl_MoveElementTo,
4132 StorageInternalImpl_Commit,
4133 StorageInternalImpl_Revert,
4134 StorageBaseImpl_EnumElements,
4135 StorageImpl_DestroyElement,
4136 StorageBaseImpl_RenameElement,
4137 StorageImpl_SetElementTimes,
4138 StorageBaseImpl_SetClass,
4139 StorageImpl_SetStateBits,
4140 StorageBaseImpl_Stat
4143 /******************************************************************************
4144 ** Storage32InternalImpl implementation
4147 StorageInternalImpl* StorageInternalImpl_Construct(
4148 StorageImpl* ancestorStorage,
4150 ULONG rootPropertyIndex)
4152 StorageInternalImpl* newStorage;
4155 * Allocate space for the new storage object
4157 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4161 memset(newStorage, 0, sizeof(StorageInternalImpl));
4164 * Initialize the stream list
4167 list_init(&newStorage->base.strmHead);
4170 * Initialize the virtual function table.
4172 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4173 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4174 newStorage->base.openFlags = openFlags;
4177 * Keep the ancestor storage pointer and nail a reference to it.
4179 newStorage->base.ancestorStorage = ancestorStorage;
4180 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4183 * Keep the index of the root property set for this storage,
4185 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4193 /******************************************************************************
4194 ** StorageUtl implementation
4197 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4201 memcpy(&tmp, buffer+offset, sizeof(WORD));
4202 *value = le16toh(tmp);
4205 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4207 value = htole16(value);
4208 memcpy(buffer+offset, &value, sizeof(WORD));
4211 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4215 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4216 *value = le32toh(tmp);
4219 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4221 value = htole32(value);
4222 memcpy(buffer+offset, &value, sizeof(DWORD));
4225 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4226 ULARGE_INTEGER* value)
4228 #ifdef WORDS_BIGENDIAN
4231 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4232 value->u.LowPart = htole32(tmp.u.HighPart);
4233 value->u.HighPart = htole32(tmp.u.LowPart);
4235 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4239 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4240 const ULARGE_INTEGER *value)
4242 #ifdef WORDS_BIGENDIAN
4245 tmp.u.LowPart = htole32(value->u.HighPart);
4246 tmp.u.HighPart = htole32(value->u.LowPart);
4247 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4249 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4253 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4255 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4256 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4257 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4259 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4262 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4264 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4265 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4266 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4268 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4271 void StorageUtl_CopyPropertyToSTATSTG(
4272 STATSTG* destination,
4273 StgProperty* source,
4277 * The copy of the string occurs only when the flag is not set
4279 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4280 (source->name == NULL) ||
4281 (source->name[0] == 0) )
4283 destination->pwcsName = 0;
4287 destination->pwcsName =
4288 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4290 strcpyW((LPWSTR)destination->pwcsName, source->name);
4293 switch (source->propertyType)
4295 case PROPTYPE_STORAGE:
4297 destination->type = STGTY_STORAGE;
4299 case PROPTYPE_STREAM:
4300 destination->type = STGTY_STREAM;
4303 destination->type = STGTY_STREAM;
4307 destination->cbSize = source->size;
4309 currentReturnStruct->mtime = {0}; TODO
4310 currentReturnStruct->ctime = {0};
4311 currentReturnStruct->atime = {0};
4313 destination->grfMode = 0;
4314 destination->grfLocksSupported = 0;
4315 destination->clsid = source->propertyUniqueID;
4316 destination->grfStateBits = 0;
4317 destination->reserved = 0;
4320 /******************************************************************************
4321 ** BlockChainStream implementation
4324 BlockChainStream* BlockChainStream_Construct(
4325 StorageImpl* parentStorage,
4326 ULONG* headOfStreamPlaceHolder,
4327 ULONG propertyIndex)
4329 BlockChainStream* newStream;
4332 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4334 newStream->parentStorage = parentStorage;
4335 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4336 newStream->ownerPropertyIndex = propertyIndex;
4337 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4338 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4339 newStream->numBlocks = 0;
4341 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4343 while (blockIndex != BLOCK_END_OF_CHAIN)
4345 newStream->numBlocks++;
4346 newStream->tailIndex = blockIndex;
4348 if(FAILED(StorageImpl_GetNextBlockInChain(
4353 HeapFree(GetProcessHeap(), 0, newStream);
4361 void BlockChainStream_Destroy(BlockChainStream* This)
4363 HeapFree(GetProcessHeap(), 0, This);
4366 /******************************************************************************
4367 * BlockChainStream_GetHeadOfChain
4369 * Returns the head of this stream chain.
4370 * Some special chains don't have properties, their heads are kept in
4371 * This->headOfStreamPlaceHolder.
4374 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4376 StgProperty chainProperty;
4377 BOOL readSuccessful;
4379 if (This->headOfStreamPlaceHolder != 0)
4380 return *(This->headOfStreamPlaceHolder);
4382 if (This->ownerPropertyIndex != PROPERTY_NULL)
4384 readSuccessful = StorageImpl_ReadProperty(
4385 This->parentStorage,
4386 This->ownerPropertyIndex,
4391 return chainProperty.startingBlock;
4395 return BLOCK_END_OF_CHAIN;
4398 /******************************************************************************
4399 * BlockChainStream_GetCount
4401 * Returns the number of blocks that comprises this chain.
4402 * This is not the size of the stream as the last block may not be full!
4405 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4410 blockIndex = BlockChainStream_GetHeadOfChain(This);
4412 while (blockIndex != BLOCK_END_OF_CHAIN)
4416 if(FAILED(StorageImpl_GetNextBlockInChain(
4417 This->parentStorage,
4426 /******************************************************************************
4427 * BlockChainStream_ReadAt
4429 * Reads a specified number of bytes from this chain at the specified offset.
4430 * bytesRead may be NULL.
4431 * Failure will be returned if the specified number of bytes has not been read.
4433 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4434 ULARGE_INTEGER offset,
4439 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4440 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4441 ULONG bytesToReadInBuffer;
4444 BYTE* bigBlockBuffer;
4447 * Find the first block in the stream that contains part of the buffer.
4449 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4450 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4451 (blockNoInSequence < This->lastBlockNoInSequence) )
4453 blockIndex = BlockChainStream_GetHeadOfChain(This);
4454 This->lastBlockNoInSequence = blockNoInSequence;
4458 ULONG temp = blockNoInSequence;
4460 blockIndex = This->lastBlockNoInSequenceIndex;
4461 blockNoInSequence -= This->lastBlockNoInSequence;
4462 This->lastBlockNoInSequence = temp;
4465 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4467 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4469 blockNoInSequence--;
4472 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4473 return FALSE; /* We failed to find the starting block */
4475 This->lastBlockNoInSequenceIndex = blockIndex;
4478 * Start reading the buffer.
4481 bufferWalker = buffer;
4483 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4486 * Calculate how many bytes we can copy from this big block.
4488 bytesToReadInBuffer =
4489 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4492 * Copy those bytes to the buffer
4495 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4496 if (!bigBlockBuffer)
4499 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4501 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4504 * Step to the next big block.
4506 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4509 bufferWalker += bytesToReadInBuffer;
4510 size -= bytesToReadInBuffer;
4511 *bytesRead += bytesToReadInBuffer;
4512 offsetInBlock = 0; /* There is no offset on the next block */
4519 /******************************************************************************
4520 * BlockChainStream_WriteAt
4522 * Writes the specified number of bytes to this chain at the specified offset.
4523 * bytesWritten may be NULL.
4524 * Will fail if not all specified number of bytes have been written.
4526 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4527 ULARGE_INTEGER offset,
4530 ULONG* bytesWritten)
4532 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4533 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4536 const BYTE* bufferWalker;
4537 BYTE* bigBlockBuffer;
4540 * Find the first block in the stream that contains part of the buffer.
4542 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4543 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4544 (blockNoInSequence < This->lastBlockNoInSequence) )
4546 blockIndex = BlockChainStream_GetHeadOfChain(This);
4547 This->lastBlockNoInSequence = blockNoInSequence;
4551 ULONG temp = blockNoInSequence;
4553 blockIndex = This->lastBlockNoInSequenceIndex;
4554 blockNoInSequence -= This->lastBlockNoInSequence;
4555 This->lastBlockNoInSequence = temp;
4558 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4560 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4563 blockNoInSequence--;
4566 This->lastBlockNoInSequenceIndex = blockIndex;
4569 * Here, I'm casting away the constness on the buffer variable
4570 * This is OK since we don't intend to modify that buffer.
4573 bufferWalker = (const BYTE*)buffer;
4575 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4578 * Calculate how many bytes we can copy from this big block.
4581 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4584 * Copy those bytes to the buffer
4586 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4588 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4590 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4593 * Step to the next big block.
4595 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4598 bufferWalker += bytesToWrite;
4599 size -= bytesToWrite;
4600 *bytesWritten += bytesToWrite;
4601 offsetInBlock = 0; /* There is no offset on the next block */
4607 /******************************************************************************
4608 * BlockChainStream_Shrink
4610 * Shrinks this chain in the big block depot.
4612 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4613 ULARGE_INTEGER newSize)
4615 ULONG blockIndex, extraBlock;
4620 * Reset the last accessed block cache.
4622 This->lastBlockNoInSequence = 0xFFFFFFFF;
4623 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4626 * Figure out how many blocks are needed to contain the new size
4628 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4630 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4633 blockIndex = BlockChainStream_GetHeadOfChain(This);
4636 * Go to the new end of chain
4638 while (count < numBlocks)
4640 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4646 /* Get the next block before marking the new end */
4647 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4651 /* Mark the new end of chain */
4652 StorageImpl_SetNextBlockInChain(
4653 This->parentStorage,
4655 BLOCK_END_OF_CHAIN);
4657 This->tailIndex = blockIndex;
4658 This->numBlocks = numBlocks;
4661 * Mark the extra blocks as free
4663 while (extraBlock != BLOCK_END_OF_CHAIN)
4665 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4668 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4669 extraBlock = blockIndex;
4675 /******************************************************************************
4676 * BlockChainStream_Enlarge
4678 * Grows this chain in the big block depot.
4680 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4681 ULARGE_INTEGER newSize)
4683 ULONG blockIndex, currentBlock;
4685 ULONG oldNumBlocks = 0;
4687 blockIndex = BlockChainStream_GetHeadOfChain(This);
4690 * Empty chain. Create the head.
4692 if (blockIndex == BLOCK_END_OF_CHAIN)
4694 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4695 StorageImpl_SetNextBlockInChain(This->parentStorage,
4697 BLOCK_END_OF_CHAIN);
4699 if (This->headOfStreamPlaceHolder != 0)
4701 *(This->headOfStreamPlaceHolder) = blockIndex;
4705 StgProperty chainProp;
4706 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4708 StorageImpl_ReadProperty(
4709 This->parentStorage,
4710 This->ownerPropertyIndex,
4713 chainProp.startingBlock = blockIndex;
4715 StorageImpl_WriteProperty(
4716 This->parentStorage,
4717 This->ownerPropertyIndex,
4721 This->tailIndex = blockIndex;
4722 This->numBlocks = 1;
4726 * Figure out how many blocks are needed to contain this stream
4728 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4730 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4734 * Go to the current end of chain
4736 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4738 currentBlock = blockIndex;
4740 while (blockIndex != BLOCK_END_OF_CHAIN)
4743 currentBlock = blockIndex;
4745 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4750 This->tailIndex = currentBlock;
4753 currentBlock = This->tailIndex;
4754 oldNumBlocks = This->numBlocks;
4757 * Add new blocks to the chain
4759 if (oldNumBlocks < newNumBlocks)
4761 while (oldNumBlocks < newNumBlocks)
4763 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4765 StorageImpl_SetNextBlockInChain(
4766 This->parentStorage,
4770 StorageImpl_SetNextBlockInChain(
4771 This->parentStorage,
4773 BLOCK_END_OF_CHAIN);
4775 currentBlock = blockIndex;
4779 This->tailIndex = blockIndex;
4780 This->numBlocks = newNumBlocks;
4786 /******************************************************************************
4787 * BlockChainStream_SetSize
4789 * Sets the size of this stream. The big block depot will be updated.
4790 * The file will grow if we grow the chain.
4792 * TODO: Free the actual blocks in the file when we shrink the chain.
4793 * Currently, the blocks are still in the file. So the file size
4794 * doesn't shrink even if we shrink streams.
4796 BOOL BlockChainStream_SetSize(
4797 BlockChainStream* This,
4798 ULARGE_INTEGER newSize)
4800 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4802 if (newSize.u.LowPart == size.u.LowPart)
4805 if (newSize.u.LowPart < size.u.LowPart)
4807 BlockChainStream_Shrink(This, newSize);
4811 ULARGE_INTEGER fileSize =
4812 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4814 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4817 * Make sure the file stays a multiple of blocksize
4819 if ((diff % This->parentStorage->bigBlockSize) != 0)
4820 diff += (This->parentStorage->bigBlockSize -
4821 (diff % This->parentStorage->bigBlockSize) );
4823 fileSize.u.LowPart += diff;
4824 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4826 BlockChainStream_Enlarge(This, newSize);
4832 /******************************************************************************
4833 * BlockChainStream_GetSize
4835 * Returns the size of this chain.
4836 * Will return the block count if this chain doesn't have a property.
4838 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4840 StgProperty chainProperty;
4842 if(This->headOfStreamPlaceHolder == NULL)
4845 * This chain is a data stream read the property and return
4846 * the appropriate size
4848 StorageImpl_ReadProperty(
4849 This->parentStorage,
4850 This->ownerPropertyIndex,
4853 return chainProperty.size;
4858 * this chain is a chain that does not have a property, figure out the
4859 * size by making the product number of used blocks times the
4862 ULARGE_INTEGER result;
4863 result.u.HighPart = 0;
4866 BlockChainStream_GetCount(This) *
4867 This->parentStorage->bigBlockSize;
4873 /******************************************************************************
4874 ** SmallBlockChainStream implementation
4877 SmallBlockChainStream* SmallBlockChainStream_Construct(
4878 StorageImpl* parentStorage,
4879 ULONG propertyIndex)
4881 SmallBlockChainStream* newStream;
4883 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4885 newStream->parentStorage = parentStorage;
4886 newStream->ownerPropertyIndex = propertyIndex;
4891 void SmallBlockChainStream_Destroy(
4892 SmallBlockChainStream* This)
4894 HeapFree(GetProcessHeap(), 0, This);
4897 /******************************************************************************
4898 * SmallBlockChainStream_GetHeadOfChain
4900 * Returns the head of this chain of small blocks.
4902 ULONG SmallBlockChainStream_GetHeadOfChain(
4903 SmallBlockChainStream* This)
4905 StgProperty chainProperty;
4906 BOOL readSuccessful;
4908 if (This->ownerPropertyIndex)
4910 readSuccessful = StorageImpl_ReadProperty(
4911 This->parentStorage,
4912 This->ownerPropertyIndex,
4917 return chainProperty.startingBlock;
4922 return BLOCK_END_OF_CHAIN;
4925 /******************************************************************************
4926 * SmallBlockChainStream_GetNextBlockInChain
4928 * Returns the index of the next small block in this chain.
4931 * - BLOCK_END_OF_CHAIN: end of this chain
4932 * - BLOCK_UNUSED: small block 'blockIndex' is free
4934 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4935 SmallBlockChainStream* This,
4937 ULONG* nextBlockInChain)
4939 ULARGE_INTEGER offsetOfBlockInDepot;
4944 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4946 offsetOfBlockInDepot.u.HighPart = 0;
4947 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4950 * Read those bytes in the buffer from the small block file.
4952 success = BlockChainStream_ReadAt(
4953 This->parentStorage->smallBlockDepotChain,
4954 offsetOfBlockInDepot,
4961 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4965 return STG_E_READFAULT;
4968 /******************************************************************************
4969 * SmallBlockChainStream_SetNextBlockInChain
4971 * Writes the index of the next block of the specified block in the small
4973 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4974 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4976 void SmallBlockChainStream_SetNextBlockInChain(
4977 SmallBlockChainStream* This,
4981 ULARGE_INTEGER offsetOfBlockInDepot;
4985 offsetOfBlockInDepot.u.HighPart = 0;
4986 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4988 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4991 * Read those bytes in the buffer from the small block file.
4993 BlockChainStream_WriteAt(
4994 This->parentStorage->smallBlockDepotChain,
4995 offsetOfBlockInDepot,
5001 /******************************************************************************
5002 * SmallBlockChainStream_FreeBlock
5004 * Flag small block 'blockIndex' as free in the small block depot.
5006 void SmallBlockChainStream_FreeBlock(
5007 SmallBlockChainStream* This,
5010 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5013 /******************************************************************************
5014 * SmallBlockChainStream_GetNextFreeBlock
5016 * Returns the index of a free small block. The small block depot will be
5017 * enlarged if necessary. The small block chain will also be enlarged if
5020 ULONG SmallBlockChainStream_GetNextFreeBlock(
5021 SmallBlockChainStream* This)
5023 ULARGE_INTEGER offsetOfBlockInDepot;
5026 ULONG blockIndex = 0;
5027 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5028 BOOL success = TRUE;
5029 ULONG smallBlocksPerBigBlock;
5031 offsetOfBlockInDepot.u.HighPart = 0;
5034 * Scan the small block depot for a free block
5036 while (nextBlockIndex != BLOCK_UNUSED)
5038 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5040 success = BlockChainStream_ReadAt(
5041 This->parentStorage->smallBlockDepotChain,
5042 offsetOfBlockInDepot,
5048 * If we run out of space for the small block depot, enlarge it
5052 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5054 if (nextBlockIndex != BLOCK_UNUSED)
5060 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5062 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5063 ULONG nextBlock, newsbdIndex;
5064 BYTE* smallBlockDepot;
5066 nextBlock = sbdIndex;
5067 while (nextBlock != BLOCK_END_OF_CHAIN)
5069 sbdIndex = nextBlock;
5070 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5073 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5074 if (sbdIndex != BLOCK_END_OF_CHAIN)
5075 StorageImpl_SetNextBlockInChain(
5076 This->parentStorage,
5080 StorageImpl_SetNextBlockInChain(
5081 This->parentStorage,
5083 BLOCK_END_OF_CHAIN);
5086 * Initialize all the small blocks to free
5089 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5091 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5092 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5097 * We have just created the small block depot.
5099 StgProperty rootProp;
5103 * Save it in the header
5105 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5106 StorageImpl_SaveFileHeader(This->parentStorage);
5109 * And allocate the first big block that will contain small blocks
5112 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5114 StorageImpl_SetNextBlockInChain(
5115 This->parentStorage,
5117 BLOCK_END_OF_CHAIN);
5119 StorageImpl_ReadProperty(
5120 This->parentStorage,
5121 This->parentStorage->base.rootPropertySetIndex,
5124 rootProp.startingBlock = sbStartIndex;
5125 rootProp.size.u.HighPart = 0;
5126 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5128 StorageImpl_WriteProperty(
5129 This->parentStorage,
5130 This->parentStorage->base.rootPropertySetIndex,
5136 smallBlocksPerBigBlock =
5137 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5140 * Verify if we have to allocate big blocks to contain small blocks
5142 if (blockIndex % smallBlocksPerBigBlock == 0)
5144 StgProperty rootProp;
5145 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5147 StorageImpl_ReadProperty(
5148 This->parentStorage,
5149 This->parentStorage->base.rootPropertySetIndex,
5152 if (rootProp.size.u.LowPart <
5153 (blocksRequired * This->parentStorage->bigBlockSize))
5155 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5157 BlockChainStream_SetSize(
5158 This->parentStorage->smallBlockRootChain,
5161 StorageImpl_WriteProperty(
5162 This->parentStorage,
5163 This->parentStorage->base.rootPropertySetIndex,
5171 /******************************************************************************
5172 * SmallBlockChainStream_ReadAt
5174 * Reads a specified number of bytes from this chain at the specified offset.
5175 * bytesRead may be NULL.
5176 * Failure will be returned if the specified number of bytes has not been read.
5178 HRESULT SmallBlockChainStream_ReadAt(
5179 SmallBlockChainStream* This,
5180 ULARGE_INTEGER offset,
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 bytesToReadInBuffer;
5193 ULONG bytesReadFromBigBlockFile;
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 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5211 blockNoInSequence--;
5215 * Start reading the buffer.
5218 bufferWalker = buffer;
5220 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5223 * Calculate how many bytes we can copy from this small block.
5225 bytesToReadInBuffer =
5226 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5229 * Calculate the offset of the small block in the small block file.
5231 offsetInBigBlockFile.u.HighPart = 0;
5232 offsetInBigBlockFile.u.LowPart =
5233 blockIndex * This->parentStorage->smallBlockSize;
5235 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5238 * Read those bytes in the buffer from the small block file.
5239 * The small block has already been identified so it shouldn't fail
5240 * unless the file is corrupt.
5242 if (!BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5243 offsetInBigBlockFile,
5244 bytesToReadInBuffer,
5246 &bytesReadFromBigBlockFile))
5247 return STG_E_DOCFILECORRUPT;
5249 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5252 * Step to the next big block.
5254 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5258 bufferWalker += bytesToReadInBuffer;
5259 size -= bytesToReadInBuffer;
5260 *bytesRead += bytesToReadInBuffer;
5261 offsetInBlock = 0; /* There is no offset on the next block */
5267 /******************************************************************************
5268 * SmallBlockChainStream_WriteAt
5270 * Writes the specified number of bytes to this chain at the specified offset.
5271 * bytesWritten may be NULL.
5272 * Will fail if not all specified number of bytes have been written.
5274 BOOL SmallBlockChainStream_WriteAt(
5275 SmallBlockChainStream* This,
5276 ULARGE_INTEGER offset,
5279 ULONG* bytesWritten)
5281 ULARGE_INTEGER offsetInBigBlockFile;
5282 ULONG blockNoInSequence =
5283 offset.u.LowPart / This->parentStorage->smallBlockSize;
5285 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5286 ULONG bytesToWriteInBuffer;
5288 ULONG bytesWrittenFromBigBlockFile;
5289 const BYTE* bufferWalker;
5292 * This should never happen on a small block file.
5294 assert(offset.u.HighPart==0);
5297 * Find the first block in the stream that contains part of the buffer.
5299 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5301 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5303 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5305 blockNoInSequence--;
5309 * Start writing the buffer.
5311 * Here, I'm casting away the constness on the buffer variable
5312 * This is OK since we don't intend to modify that buffer.
5315 bufferWalker = (const BYTE*)buffer;
5316 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5319 * Calculate how many bytes we can copy to this small block.
5321 bytesToWriteInBuffer =
5322 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5325 * Calculate the offset of the small block in the small block file.
5327 offsetInBigBlockFile.u.HighPart = 0;
5328 offsetInBigBlockFile.u.LowPart =
5329 blockIndex * This->parentStorage->smallBlockSize;
5331 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5334 * Write those bytes in the buffer to the small block file.
5336 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5337 offsetInBigBlockFile,
5338 bytesToWriteInBuffer,
5340 &bytesWrittenFromBigBlockFile);
5342 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5345 * Step to the next big block.
5347 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5350 bufferWalker += bytesToWriteInBuffer;
5351 size -= bytesToWriteInBuffer;
5352 *bytesWritten += bytesToWriteInBuffer;
5353 offsetInBlock = 0; /* There is no offset on the next block */
5359 /******************************************************************************
5360 * SmallBlockChainStream_Shrink
5362 * Shrinks this chain in the small block depot.
5364 BOOL SmallBlockChainStream_Shrink(
5365 SmallBlockChainStream* This,
5366 ULARGE_INTEGER newSize)
5368 ULONG blockIndex, extraBlock;
5372 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5374 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5377 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5380 * Go to the new end of chain
5382 while (count < numBlocks)
5384 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5391 * If the count is 0, we have a special case, the head of the chain was
5396 StgProperty chainProp;
5398 StorageImpl_ReadProperty(This->parentStorage,
5399 This->ownerPropertyIndex,
5402 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5404 StorageImpl_WriteProperty(This->parentStorage,
5405 This->ownerPropertyIndex,
5409 * We start freeing the chain at the head block.
5411 extraBlock = blockIndex;
5415 /* Get the next block before marking the new end */
5416 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5420 /* Mark the new end of chain */
5421 SmallBlockChainStream_SetNextBlockInChain(
5424 BLOCK_END_OF_CHAIN);
5428 * Mark the extra blocks as free
5430 while (extraBlock != BLOCK_END_OF_CHAIN)
5432 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5435 SmallBlockChainStream_FreeBlock(This, extraBlock);
5436 extraBlock = blockIndex;
5442 /******************************************************************************
5443 * SmallBlockChainStream_Enlarge
5445 * Grows this chain in the small block depot.
5447 BOOL SmallBlockChainStream_Enlarge(
5448 SmallBlockChainStream* This,
5449 ULARGE_INTEGER newSize)
5451 ULONG blockIndex, currentBlock;
5453 ULONG oldNumBlocks = 0;
5455 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5460 if (blockIndex == BLOCK_END_OF_CHAIN)
5463 StgProperty chainProp;
5465 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5468 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5470 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5473 blockIndex = chainProp.startingBlock;
5474 SmallBlockChainStream_SetNextBlockInChain(
5477 BLOCK_END_OF_CHAIN);
5480 currentBlock = blockIndex;
5483 * Figure out how many blocks are needed to contain this stream
5485 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5487 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5491 * Go to the current end of chain
5493 while (blockIndex != BLOCK_END_OF_CHAIN)
5496 currentBlock = blockIndex;
5497 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5502 * Add new blocks to the chain
5504 while (oldNumBlocks < newNumBlocks)
5506 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5507 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5509 SmallBlockChainStream_SetNextBlockInChain(
5512 BLOCK_END_OF_CHAIN);
5514 currentBlock = blockIndex;
5521 /******************************************************************************
5522 * SmallBlockChainStream_GetCount
5524 * Returns the number of blocks that comprises this chain.
5525 * This is not the size of this chain as the last block may not be full!
5527 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5532 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5534 while (blockIndex != BLOCK_END_OF_CHAIN)
5538 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5545 /******************************************************************************
5546 * SmallBlockChainStream_SetSize
5548 * Sets the size of this stream.
5549 * The file will grow if we grow the chain.
5551 * TODO: Free the actual blocks in the file when we shrink the chain.
5552 * Currently, the blocks are still in the file. So the file size
5553 * doesn't shrink even if we shrink streams.
5555 BOOL SmallBlockChainStream_SetSize(
5556 SmallBlockChainStream* This,
5557 ULARGE_INTEGER newSize)
5559 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5561 if (newSize.u.LowPart == size.u.LowPart)
5564 if (newSize.u.LowPart < size.u.LowPart)
5566 SmallBlockChainStream_Shrink(This, newSize);
5570 SmallBlockChainStream_Enlarge(This, newSize);
5576 /******************************************************************************
5577 * SmallBlockChainStream_GetSize
5579 * Returns the size of this chain.
5581 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5583 StgProperty chainProperty;
5585 StorageImpl_ReadProperty(
5586 This->parentStorage,
5587 This->ownerPropertyIndex,
5590 return chainProperty.size;
5593 /******************************************************************************
5594 * StgCreateDocfile [OLE32.@]
5595 * Creates a new compound file storage object
5598 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5599 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5600 * reserved [ ?] unused?, usually 0
5601 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5604 * S_OK if the file was successfully created
5605 * some STG_E_ value if error
5607 * if pwcsName is NULL, create file with new unique name
5608 * the function can returns
5609 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5612 HRESULT WINAPI StgCreateDocfile(
5616 IStorage **ppstgOpen)
5618 StorageImpl* newStorage = 0;
5619 HANDLE hFile = INVALID_HANDLE_VALUE;
5620 HRESULT hr = STG_E_INVALIDFLAG;
5624 DWORD fileAttributes;
5625 WCHAR tempFileName[MAX_PATH];
5627 TRACE("(%s, %lx, %ld, %p)\n",
5628 debugstr_w(pwcsName), grfMode,
5629 reserved, ppstgOpen);
5632 * Validate the parameters
5635 return STG_E_INVALIDPOINTER;
5637 return STG_E_INVALIDPARAMETER;
5640 * Validate the STGM flags
5642 if ( FAILED( validateSTGM(grfMode) ))
5645 /* StgCreateDocFile always opens for write */
5646 switch(STGM_ACCESS_MODE(grfMode))
5649 case STGM_READWRITE:
5655 /* can't share write */
5656 switch(STGM_SHARE_MODE(grfMode))
5658 case STGM_SHARE_EXCLUSIVE:
5659 case STGM_SHARE_DENY_WRITE:
5665 /* shared reading requires transacted mode */
5666 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5667 !(grfMode&STGM_TRANSACTED) )
5671 * Generate a unique name.
5675 WCHAR tempPath[MAX_PATH];
5676 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5678 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5681 memset(tempPath, 0, sizeof(tempPath));
5682 memset(tempFileName, 0, sizeof(tempFileName));
5684 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5687 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5688 pwcsName = tempFileName;
5691 hr = STG_E_INSUFFICIENTMEMORY;
5695 creationMode = TRUNCATE_EXISTING;
5699 creationMode = GetCreationModeFromSTGM(grfMode);
5703 * Interpret the STGM value grfMode
5705 shareMode = GetShareModeFromSTGM(grfMode);
5706 accessMode = GetAccessModeFromSTGM(grfMode);
5708 if (grfMode & STGM_DELETEONRELEASE)
5709 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5711 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5713 if (grfMode & STGM_TRANSACTED)
5714 FIXME("Transacted mode not implemented.\n");
5717 * Initialize the "out" parameter.
5721 hFile = CreateFileW(pwcsName,
5729 if (hFile == INVALID_HANDLE_VALUE)
5731 if(GetLastError() == ERROR_FILE_EXISTS)
5732 hr = STG_E_FILEALREADYEXISTS;
5739 * Allocate and initialize the new IStorage32object.
5741 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5743 if (newStorage == 0)
5745 hr = STG_E_INSUFFICIENTMEMORY;
5749 hr = StorageImpl_Construct(
5760 HeapFree(GetProcessHeap(), 0, newStorage);
5765 * Get an "out" pointer for the caller.
5767 hr = StorageBaseImpl_QueryInterface(
5768 (IStorage*)newStorage,
5769 (REFIID)&IID_IStorage,
5772 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5777 /******************************************************************************
5778 * StgCreateStorageEx [OLE32.@]
5780 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5782 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5783 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5785 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5787 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5788 return STG_E_INVALIDPARAMETER;
5791 if (stgfmt != STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5793 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5794 return STG_E_INVALIDPARAMETER;
5797 if (stgfmt == STGFMT_FILE)
5799 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5800 return STG_E_INVALIDPARAMETER;
5803 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5805 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5806 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5809 ERR("Invalid stgfmt argument\n");
5810 return STG_E_INVALIDPARAMETER;
5813 /******************************************************************************
5814 * StgCreatePropSetStg [OLE32.@]
5816 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5817 IPropertySetStorage **ppPropSetStg)
5821 TRACE("(%p, 0x%lx, %p)\n", pstg, reserved, ppPropSetStg);
5823 hr = STG_E_INVALIDPARAMETER;
5825 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5826 (void**)ppPropSetStg);
5830 /******************************************************************************
5831 * StgOpenStorageEx [OLE32.@]
5833 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5835 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5836 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5838 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5840 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5841 return STG_E_INVALIDPARAMETER;
5844 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5846 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5847 return STG_E_INVALIDPARAMETER;
5850 if (stgfmt == STGFMT_FILE)
5852 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5853 return STG_E_INVALIDPARAMETER;
5856 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)
5858 if (stgfmt == STGFMT_ANY)
5859 WARN("STGFMT_ANY assuming storage\n");
5860 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5861 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5864 ERR("Invalid stgfmt argument\n");
5865 return STG_E_INVALIDPARAMETER;
5869 /******************************************************************************
5870 * StgOpenStorage [OLE32.@]
5872 HRESULT WINAPI StgOpenStorage(
5873 const OLECHAR *pwcsName,
5874 IStorage *pstgPriority,
5878 IStorage **ppstgOpen)
5880 StorageImpl* newStorage = 0;
5885 WCHAR fullname[MAX_PATH];
5888 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5889 debugstr_w(pwcsName), pstgPriority, grfMode,
5890 snbExclude, reserved, ppstgOpen);
5893 * Perform sanity checks
5897 hr = STG_E_INVALIDNAME;
5903 hr = STG_E_INVALIDPOINTER;
5909 hr = STG_E_INVALIDPARAMETER;
5913 /* STGM_PRIORITY implies exclusive access */
5914 if (grfMode & STGM_PRIORITY)
5916 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5917 return STG_E_INVALIDFLAG;
5918 if (grfMode & STGM_DELETEONRELEASE)
5919 return STG_E_INVALIDFUNCTION;
5920 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5921 return STG_E_INVALIDFLAG;
5922 grfMode &= ~0xf0; /* remove the existing sharing mode */
5923 grfMode |= STGM_SHARE_DENY_WRITE;
5927 * Validate the sharing mode
5929 if (!(grfMode & STGM_TRANSACTED))
5930 switch(STGM_SHARE_MODE(grfMode))
5932 case STGM_SHARE_EXCLUSIVE:
5933 case STGM_SHARE_DENY_WRITE:
5936 hr = STG_E_INVALIDFLAG;
5941 * Validate the STGM flags
5943 if ( FAILED( validateSTGM(grfMode) ) ||
5944 (grfMode&STGM_CREATE))
5946 hr = STG_E_INVALIDFLAG;
5950 /* shared reading requires transacted mode */
5951 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5952 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5953 !(grfMode&STGM_TRANSACTED) )
5955 hr = STG_E_INVALIDFLAG;
5960 * Interpret the STGM value grfMode
5962 shareMode = GetShareModeFromSTGM(grfMode);
5963 accessMode = GetAccessModeFromSTGM(grfMode);
5966 * Initialize the "out" parameter.
5970 hFile = CreateFileW( pwcsName,
5975 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5978 if (hFile==INVALID_HANDLE_VALUE)
5980 DWORD last_error = GetLastError();
5986 case ERROR_FILE_NOT_FOUND:
5987 hr = STG_E_FILENOTFOUND;
5990 case ERROR_PATH_NOT_FOUND:
5991 hr = STG_E_PATHNOTFOUND;
5994 case ERROR_ACCESS_DENIED:
5995 case ERROR_WRITE_PROTECT:
5996 hr = STG_E_ACCESSDENIED;
5999 case ERROR_SHARING_VIOLATION:
6000 hr = STG_E_SHAREVIOLATION;
6011 * Refuse to open the file if it's too small to be a structured storage file
6012 * FIXME: verify the file when reading instead of here
6014 length = GetFileSize(hFile, NULL);
6018 hr = STG_E_FILEALREADYEXISTS;
6023 * Allocate and initialize the new IStorage32object.
6025 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6027 if (newStorage == 0)
6029 hr = STG_E_INSUFFICIENTMEMORY;
6033 /* if the file's length was zero, initialize the storage */
6034 hr = StorageImpl_Construct(
6045 HeapFree(GetProcessHeap(), 0, newStorage);
6047 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6049 if(hr == STG_E_INVALIDHEADER)
6050 hr = STG_E_FILEALREADYEXISTS;
6054 /* prepare the file name string given in lieu of the root property name */
6055 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6056 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6057 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6060 * Get an "out" pointer for the caller.
6062 hr = StorageBaseImpl_QueryInterface(
6063 (IStorage*)newStorage,
6064 (REFIID)&IID_IStorage,
6068 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6072 /******************************************************************************
6073 * StgCreateDocfileOnILockBytes [OLE32.@]
6075 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6079 IStorage** ppstgOpen)
6081 StorageImpl* newStorage = 0;
6085 * Validate the parameters
6087 if ((ppstgOpen == 0) || (plkbyt == 0))
6088 return STG_E_INVALIDPOINTER;
6091 * Allocate and initialize the new IStorage object.
6093 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6095 if (newStorage == 0)
6096 return STG_E_INSUFFICIENTMEMORY;
6098 hr = StorageImpl_Construct(
6109 HeapFree(GetProcessHeap(), 0, newStorage);
6114 * Get an "out" pointer for the caller.
6116 hr = StorageBaseImpl_QueryInterface(
6117 (IStorage*)newStorage,
6118 (REFIID)&IID_IStorage,
6124 /******************************************************************************
6125 * StgOpenStorageOnILockBytes [OLE32.@]
6127 HRESULT WINAPI StgOpenStorageOnILockBytes(
6129 IStorage *pstgPriority,
6133 IStorage **ppstgOpen)
6135 StorageImpl* newStorage = 0;
6139 * Perform a sanity check
6141 if ((plkbyt == 0) || (ppstgOpen == 0))
6142 return STG_E_INVALIDPOINTER;
6145 * Validate the STGM flags
6147 if ( FAILED( validateSTGM(grfMode) ))
6148 return STG_E_INVALIDFLAG;
6151 * Initialize the "out" parameter.
6156 * Allocate and initialize the new IStorage object.
6158 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6160 if (newStorage == 0)
6161 return STG_E_INSUFFICIENTMEMORY;
6163 hr = StorageImpl_Construct(
6174 HeapFree(GetProcessHeap(), 0, newStorage);
6179 * Get an "out" pointer for the caller.
6181 hr = StorageBaseImpl_QueryInterface(
6182 (IStorage*)newStorage,
6183 (REFIID)&IID_IStorage,
6189 /******************************************************************************
6190 * StgSetTimes [ole32.@]
6191 * StgSetTimes [OLE32.@]
6195 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6196 FILETIME const *patime, FILETIME const *pmtime)
6198 IStorage *stg = NULL;
6201 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6203 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6207 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6208 IStorage_Release(stg);
6214 /******************************************************************************
6215 * StgIsStorageILockBytes [OLE32.@]
6217 * Determines if the ILockBytes contains a storage object.
6219 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6222 ULARGE_INTEGER offset;
6224 offset.u.HighPart = 0;
6225 offset.u.LowPart = 0;
6227 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6229 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6235 /******************************************************************************
6236 * WriteClassStg [OLE32.@]
6238 * This method will store the specified CLSID in the specified storage object
6240 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6245 return E_INVALIDARG;
6247 hRes = IStorage_SetClass(pStg, rclsid);
6252 /***********************************************************************
6253 * ReadClassStg (OLE32.@)
6255 * This method reads the CLSID previously written to a storage object with
6256 * the WriteClassStg.
6259 * pstg [I] IStorage pointer
6260 * pclsid [O] Pointer to where the CLSID is written
6264 * Failure: HRESULT code.
6266 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6271 TRACE("(%p, %p)\n", pstg, pclsid);
6273 if(!pstg || !pclsid)
6274 return E_INVALIDARG;
6277 * read a STATSTG structure (contains the clsid) from the storage
6279 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6282 *pclsid=pstatstg.clsid;
6287 /***********************************************************************
6288 * OleLoadFromStream (OLE32.@)
6290 * This function loads an object from stream
6292 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6296 LPPERSISTSTREAM xstm;
6298 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6300 res=ReadClassStm(pStm,&clsid);
6301 if (!SUCCEEDED(res))
6303 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6304 if (!SUCCEEDED(res))
6306 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6307 if (!SUCCEEDED(res)) {
6308 IUnknown_Release((IUnknown*)*ppvObj);
6311 res=IPersistStream_Load(xstm,pStm);
6312 IPersistStream_Release(xstm);
6313 /* FIXME: all refcounts ok at this point? I think they should be:
6316 * xstm : 0 (released)
6321 /***********************************************************************
6322 * OleSaveToStream (OLE32.@)
6324 * This function saves an object with the IPersistStream interface on it
6325 * to the specified stream.
6327 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6333 TRACE("(%p,%p)\n",pPStm,pStm);
6335 res=IPersistStream_GetClassID(pPStm,&clsid);
6337 if (SUCCEEDED(res)){
6339 res=WriteClassStm(pStm,&clsid);
6343 res=IPersistStream_Save(pPStm,pStm,TRUE);
6346 TRACE("Finished Save\n");
6350 /****************************************************************************
6351 * This method validate a STGM parameter that can contain the values below
6353 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6354 * The stgm values contained in 0xffff0000 are bitmasks.
6356 * STGM_DIRECT 0x00000000
6357 * STGM_TRANSACTED 0x00010000
6358 * STGM_SIMPLE 0x08000000
6360 * STGM_READ 0x00000000
6361 * STGM_WRITE 0x00000001
6362 * STGM_READWRITE 0x00000002
6364 * STGM_SHARE_DENY_NONE 0x00000040
6365 * STGM_SHARE_DENY_READ 0x00000030
6366 * STGM_SHARE_DENY_WRITE 0x00000020
6367 * STGM_SHARE_EXCLUSIVE 0x00000010
6369 * STGM_PRIORITY 0x00040000
6370 * STGM_DELETEONRELEASE 0x04000000
6372 * STGM_CREATE 0x00001000
6373 * STGM_CONVERT 0x00020000
6374 * STGM_FAILIFTHERE 0x00000000
6376 * STGM_NOSCRATCH 0x00100000
6377 * STGM_NOSNAPSHOT 0x00200000
6379 static HRESULT validateSTGM(DWORD stgm)
6381 DWORD access = STGM_ACCESS_MODE(stgm);
6382 DWORD share = STGM_SHARE_MODE(stgm);
6383 DWORD create = STGM_CREATE_MODE(stgm);
6385 if (stgm&~STGM_KNOWN_FLAGS)
6387 ERR("unknown flags %08lx\n", stgm);
6395 case STGM_READWRITE:
6403 case STGM_SHARE_DENY_NONE:
6404 case STGM_SHARE_DENY_READ:
6405 case STGM_SHARE_DENY_WRITE:
6406 case STGM_SHARE_EXCLUSIVE:
6415 case STGM_FAILIFTHERE:
6422 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6424 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6428 * STGM_CREATE | STGM_CONVERT
6429 * if both are false, STGM_FAILIFTHERE is set to TRUE
6431 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6435 * STGM_NOSCRATCH requires STGM_TRANSACTED
6437 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6441 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6442 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6444 if ( (stgm & STGM_NOSNAPSHOT) &&
6445 (!(stgm & STGM_TRANSACTED) ||
6446 share == STGM_SHARE_EXCLUSIVE ||
6447 share == STGM_SHARE_DENY_WRITE) )
6453 /****************************************************************************
6454 * GetShareModeFromSTGM
6456 * This method will return a share mode flag from a STGM value.
6457 * The STGM value is assumed valid.
6459 static DWORD GetShareModeFromSTGM(DWORD stgm)
6461 switch (STGM_SHARE_MODE(stgm))
6463 case STGM_SHARE_DENY_NONE:
6464 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6465 case STGM_SHARE_DENY_READ:
6466 return FILE_SHARE_WRITE;
6467 case STGM_SHARE_DENY_WRITE:
6468 return FILE_SHARE_READ;
6469 case STGM_SHARE_EXCLUSIVE:
6472 ERR("Invalid share mode!\n");
6477 /****************************************************************************
6478 * GetAccessModeFromSTGM
6480 * This method will return an access mode flag from a STGM value.
6481 * The STGM value is assumed valid.
6483 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6485 switch (STGM_ACCESS_MODE(stgm))
6488 return GENERIC_READ;
6490 case STGM_READWRITE:
6491 return GENERIC_READ | GENERIC_WRITE;
6493 ERR("Invalid access mode!\n");
6498 /****************************************************************************
6499 * GetCreationModeFromSTGM
6501 * This method will return a creation mode flag from a STGM value.
6502 * The STGM value is assumed valid.
6504 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6506 switch(STGM_CREATE_MODE(stgm))
6509 return CREATE_ALWAYS;
6511 FIXME("STGM_CONVERT not implemented!\n");
6513 case STGM_FAILIFTHERE:
6516 ERR("Invalid create mode!\n");
6522 /*************************************************************************
6523 * OLECONVERT_LoadOLE10 [Internal]
6525 * Loads the OLE10 STREAM to memory
6528 * pOleStream [I] The OLESTREAM
6529 * pData [I] Data Structure for the OLESTREAM Data
6533 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6534 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6537 * This function is used by OleConvertOLESTREAMToIStorage only.
6539 * Memory allocated for pData must be freed by the caller
6541 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6544 HRESULT hRes = S_OK;
6548 pData->pData = NULL;
6549 pData->pstrOleObjFileName = (CHAR *) NULL;
6551 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6554 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6555 if(dwSize != sizeof(pData->dwOleID))
6557 hRes = CONVERT10_E_OLESTREAM_GET;
6559 else if(pData->dwOleID != OLESTREAM_ID)
6561 hRes = CONVERT10_E_OLESTREAM_FMT;
6572 /* Get the TypeID...more info needed for this field */
6573 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6574 if(dwSize != sizeof(pData->dwTypeID))
6576 hRes = CONVERT10_E_OLESTREAM_GET;
6581 if(pData->dwTypeID != 0)
6583 /* Get the length of the OleTypeName */
6584 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6585 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6587 hRes = CONVERT10_E_OLESTREAM_GET;
6592 if(pData->dwOleTypeNameLength > 0)
6594 /* Get the OleTypeName */
6595 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6596 if(dwSize != pData->dwOleTypeNameLength)
6598 hRes = CONVERT10_E_OLESTREAM_GET;
6604 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6605 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6607 hRes = CONVERT10_E_OLESTREAM_GET;
6611 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6612 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6613 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6614 if(pData->pstrOleObjFileName)
6616 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6617 if(dwSize != pData->dwOleObjFileNameLength)
6619 hRes = CONVERT10_E_OLESTREAM_GET;
6623 hRes = CONVERT10_E_OLESTREAM_GET;
6628 /* Get the Width of the Metafile */
6629 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6630 if(dwSize != sizeof(pData->dwMetaFileWidth))
6632 hRes = CONVERT10_E_OLESTREAM_GET;
6636 /* Get the Height of the Metafile */
6637 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6638 if(dwSize != sizeof(pData->dwMetaFileHeight))
6640 hRes = CONVERT10_E_OLESTREAM_GET;
6646 /* Get the Length of the Data */
6647 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6648 if(dwSize != sizeof(pData->dwDataLength))
6650 hRes = CONVERT10_E_OLESTREAM_GET;
6654 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6656 if(!bStrem1) /* if it is a second OLE stream data */
6658 pData->dwDataLength -= 8;
6659 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6660 if(dwSize != sizeof(pData->strUnknown))
6662 hRes = CONVERT10_E_OLESTREAM_GET;
6668 if(pData->dwDataLength > 0)
6670 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6672 /* Get Data (ex. IStorage, Metafile, or BMP) */
6675 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6676 if(dwSize != pData->dwDataLength)
6678 hRes = CONVERT10_E_OLESTREAM_GET;
6683 hRes = CONVERT10_E_OLESTREAM_GET;
6692 /*************************************************************************
6693 * OLECONVERT_SaveOLE10 [Internal]
6695 * Saves the OLE10 STREAM From memory
6698 * pData [I] Data Structure for the OLESTREAM Data
6699 * pOleStream [I] The OLESTREAM to save
6703 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6706 * This function is used by OleConvertIStorageToOLESTREAM only.
6709 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6712 HRESULT hRes = S_OK;
6716 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6717 if(dwSize != sizeof(pData->dwOleID))
6719 hRes = CONVERT10_E_OLESTREAM_PUT;
6724 /* Set the TypeID */
6725 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6726 if(dwSize != sizeof(pData->dwTypeID))
6728 hRes = CONVERT10_E_OLESTREAM_PUT;
6732 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6734 /* Set the Length of the OleTypeName */
6735 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6736 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6738 hRes = CONVERT10_E_OLESTREAM_PUT;
6743 if(pData->dwOleTypeNameLength > 0)
6745 /* Set the OleTypeName */
6746 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6747 if(dwSize != pData->dwOleTypeNameLength)
6749 hRes = CONVERT10_E_OLESTREAM_PUT;
6756 /* Set the width of the Metafile */
6757 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6758 if(dwSize != sizeof(pData->dwMetaFileWidth))
6760 hRes = CONVERT10_E_OLESTREAM_PUT;
6766 /* Set the height of the Metafile */
6767 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6768 if(dwSize != sizeof(pData->dwMetaFileHeight))
6770 hRes = CONVERT10_E_OLESTREAM_PUT;
6776 /* Set the length of the Data */
6777 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6778 if(dwSize != sizeof(pData->dwDataLength))
6780 hRes = CONVERT10_E_OLESTREAM_PUT;
6786 if(pData->dwDataLength > 0)
6788 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6789 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6790 if(dwSize != pData->dwDataLength)
6792 hRes = CONVERT10_E_OLESTREAM_PUT;
6800 /*************************************************************************
6801 * OLECONVERT_GetOLE20FromOLE10[Internal]
6803 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6804 * opens it, and copies the content to the dest IStorage for
6805 * OleConvertOLESTREAMToIStorage
6809 * pDestStorage [I] The IStorage to copy the data to
6810 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6811 * nBufferLength [I] The size of the buffer
6820 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6824 IStorage *pTempStorage;
6825 DWORD dwNumOfBytesWritten;
6826 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6827 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6829 /* Create a temp File */
6830 GetTempPathW(MAX_PATH, wstrTempDir);
6831 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6832 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6834 if(hFile != INVALID_HANDLE_VALUE)
6836 /* Write IStorage Data to File */
6837 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6840 /* Open and copy temp storage to the Dest Storage */
6841 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6844 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6845 StorageBaseImpl_Release(pTempStorage);
6847 DeleteFileW(wstrTempFile);
6852 /*************************************************************************
6853 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6855 * Saves the OLE10 STREAM From memory
6858 * pStorage [I] The Src IStorage to copy
6859 * pData [I] The Dest Memory to write to.
6862 * The size in bytes allocated for pData
6865 * Memory allocated for pData must be freed by the caller
6867 * Used by OleConvertIStorageToOLESTREAM only.
6870 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6874 DWORD nDataLength = 0;
6875 IStorage *pTempStorage;
6876 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6877 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6881 /* Create temp Storage */
6882 GetTempPathW(MAX_PATH, wstrTempDir);
6883 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6884 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6888 /* Copy Src Storage to the Temp Storage */
6889 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6890 StorageBaseImpl_Release(pTempStorage);
6892 /* Open Temp Storage as a file and copy to memory */
6893 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6894 if(hFile != INVALID_HANDLE_VALUE)
6896 nDataLength = GetFileSize(hFile, NULL);
6897 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6898 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6901 DeleteFileW(wstrTempFile);
6906 /*************************************************************************
6907 * OLECONVERT_CreateOleStream [Internal]
6909 * Creates the "\001OLE" stream in the IStorage if necessary.
6912 * pStorage [I] Dest storage to create the stream in
6918 * This function is used by OleConvertOLESTREAMToIStorage only.
6920 * This stream is still unknown, MS Word seems to have extra data
6921 * but since the data is stored in the OLESTREAM there should be
6922 * no need to recreate the stream. If the stream is manually
6923 * deleted it will create it with this default data.
6926 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6930 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6931 BYTE pOleStreamHeader [] =
6933 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6935 0x00, 0x00, 0x00, 0x00
6938 /* Create stream if not present */
6939 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6940 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6944 /* Write default Data */
6945 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6946 IStream_Release(pStream);
6950 /* write a string to a stream, preceded by its length */
6951 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6958 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6959 r = IStream_Write( stm, &len, sizeof(len), NULL);
6964 str = CoTaskMemAlloc( len );
6965 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6966 r = IStream_Write( stm, str, len, NULL);
6967 CoTaskMemFree( str );
6971 /* read a string preceded by its length from a stream */
6972 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6975 DWORD len, count = 0;
6979 r = IStream_Read( stm, &len, sizeof(len), &count );
6982 if( count != sizeof(len) )
6983 return E_OUTOFMEMORY;
6985 TRACE("%ld bytes\n",len);
6987 str = CoTaskMemAlloc( len );
6989 return E_OUTOFMEMORY;
6991 r = IStream_Read( stm, str, len, &count );
6996 CoTaskMemFree( str );
6997 return E_OUTOFMEMORY;
7000 TRACE("Read string %s\n",debugstr_an(str,len));
7002 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7003 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7005 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7006 CoTaskMemFree( str );
7014 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7015 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7019 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7021 static const BYTE unknown1[12] =
7022 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7023 0xFF, 0xFF, 0xFF, 0xFF};
7024 static const BYTE unknown2[16] =
7025 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7026 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7028 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7029 debugstr_w(lpszUserType), debugstr_w(szClipName),
7030 debugstr_w(szProgIDName));
7032 /* Create a CompObj stream if it doesn't exist */
7033 r = IStorage_CreateStream(pstg, szwStreamName,
7034 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7038 /* Write CompObj Structure to stream */
7039 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7041 if( SUCCEEDED( r ) )
7042 r = WriteClassStm( pstm, clsid );
7044 if( SUCCEEDED( r ) )
7045 r = STREAM_WriteString( pstm, lpszUserType );
7046 if( SUCCEEDED( r ) )
7047 r = STREAM_WriteString( pstm, szClipName );
7048 if( SUCCEEDED( r ) )
7049 r = STREAM_WriteString( pstm, szProgIDName );
7050 if( SUCCEEDED( r ) )
7051 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7053 IStream_Release( pstm );
7058 /***********************************************************************
7059 * WriteFmtUserTypeStg (OLE32.@)
7061 HRESULT WINAPI WriteFmtUserTypeStg(
7062 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7065 WCHAR szwClipName[0x40];
7066 CLSID clsid = CLSID_NULL;
7067 LPWSTR wstrProgID = NULL;
7070 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7072 /* get the clipboard format name */
7073 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7076 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7078 /* FIXME: There's room to save a CLSID and its ProgID, but
7079 the CLSID is not looked up in the registry and in all the
7080 tests I wrote it was CLSID_NULL. Where does it come from?
7083 /* get the real program ID. This may fail, but that's fine */
7084 ProgIDFromCLSID(&clsid, &wstrProgID);
7086 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7088 r = STORAGE_WriteCompObj( pstg, &clsid,
7089 lpszUserType, szwClipName, wstrProgID );
7091 CoTaskMemFree(wstrProgID);
7097 /******************************************************************************
7098 * ReadFmtUserTypeStg [OLE32.@]
7100 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7104 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7105 unsigned char unknown1[12];
7106 unsigned char unknown2[16];
7108 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7111 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7113 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7114 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7117 WARN("Failed to open stream r = %08lx\n", r);
7121 /* read the various parts of the structure */
7122 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7123 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7125 r = ReadClassStm( stm, &clsid );
7129 r = STREAM_ReadString( stm, &szCLSIDName );
7133 r = STREAM_ReadString( stm, &szOleTypeName );
7137 r = STREAM_ReadString( stm, &szProgIDName );
7141 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7142 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7145 /* ok, success... now we just need to store what we found */
7147 *pcf = RegisterClipboardFormatW( szOleTypeName );
7148 CoTaskMemFree( szOleTypeName );
7150 if( lplpszUserType )
7151 *lplpszUserType = szCLSIDName;
7152 CoTaskMemFree( szProgIDName );
7155 IStream_Release( stm );
7161 /*************************************************************************
7162 * OLECONVERT_CreateCompObjStream [Internal]
7164 * Creates a "\001CompObj" is the destination IStorage if necessary.
7167 * pStorage [I] The dest IStorage to create the CompObj Stream
7169 * strOleTypeName [I] The ProgID
7173 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7176 * This function is used by OleConvertOLESTREAMToIStorage only.
7178 * The stream data is stored in the OLESTREAM and there should be
7179 * no need to recreate the stream. If the stream is manually
7180 * deleted it will attempt to create it by querying the registry.
7184 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7187 HRESULT hStorageRes, hRes = S_OK;
7188 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7189 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7190 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7192 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7193 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7195 /* Initialize the CompObj structure */
7196 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7197 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7198 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7201 /* Create a CompObj stream if it doesn't exist */
7202 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7203 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7204 if(hStorageRes == S_OK)
7206 /* copy the OleTypeName to the compobj struct */
7207 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7208 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7210 /* copy the OleTypeName to the compobj struct */
7211 /* Note: in the test made, these were Identical */
7212 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7213 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7216 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7217 bufferW, OLESTREAM_MAX_STR_LEN );
7218 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7224 /* Get the CLSID Default Name from the Registry */
7225 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7226 if(hErr == ERROR_SUCCESS)
7228 char strTemp[OLESTREAM_MAX_STR_LEN];
7229 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7230 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7231 if(hErr == ERROR_SUCCESS)
7233 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7239 /* Write CompObj Structure to stream */
7240 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7242 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7244 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7245 if(IStorageCompObj.dwCLSIDNameLength > 0)
7247 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7249 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7250 if(IStorageCompObj.dwOleTypeNameLength > 0)
7252 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7254 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7255 if(IStorageCompObj.dwProgIDNameLength > 0)
7257 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7259 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7260 IStream_Release(pStream);
7266 /*************************************************************************
7267 * OLECONVERT_CreateOlePresStream[Internal]
7269 * Creates the "\002OlePres000" Stream with the Metafile data
7272 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7273 * dwExtentX [I] Width of the Metafile
7274 * dwExtentY [I] Height of the Metafile
7275 * pData [I] Metafile data
7276 * dwDataLength [I] Size of the Metafile data
7280 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7283 * This function is used by OleConvertOLESTREAMToIStorage only.
7286 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7290 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7291 BYTE pOlePresStreamHeader [] =
7293 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7294 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7295 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7296 0x00, 0x00, 0x00, 0x00
7299 BYTE pOlePresStreamHeaderEmpty [] =
7301 0x00, 0x00, 0x00, 0x00,
7302 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7303 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7304 0x00, 0x00, 0x00, 0x00
7307 /* Create the OlePres000 Stream */
7308 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7309 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7314 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7316 memset(&OlePres, 0, sizeof(OlePres));
7317 /* Do we have any metafile data to save */
7318 if(dwDataLength > 0)
7320 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7321 nHeaderSize = sizeof(pOlePresStreamHeader);
7325 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7326 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7328 /* Set width and height of the metafile */
7329 OlePres.dwExtentX = dwExtentX;
7330 OlePres.dwExtentY = -dwExtentY;
7332 /* Set Data and Length */
7333 if(dwDataLength > sizeof(METAFILEPICT16))
7335 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7336 OlePres.pData = &(pData[8]);
7338 /* Save OlePres000 Data to Stream */
7339 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7340 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7341 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7342 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7343 if(OlePres.dwSize > 0)
7345 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7347 IStream_Release(pStream);
7351 /*************************************************************************
7352 * OLECONVERT_CreateOle10NativeStream [Internal]
7354 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7357 * pStorage [I] Dest storage to create the stream in
7358 * pData [I] Ole10 Native Data (ex. bmp)
7359 * dwDataLength [I] Size of the Ole10 Native Data
7365 * This function is used by OleConvertOLESTREAMToIStorage only.
7367 * Might need to verify the data and return appropriate error message
7370 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7374 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7376 /* Create the Ole10Native Stream */
7377 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7378 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7382 /* Write info to stream */
7383 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7384 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7385 IStream_Release(pStream);
7390 /*************************************************************************
7391 * OLECONVERT_GetOLE10ProgID [Internal]
7393 * Finds the ProgID (or OleTypeID) from the IStorage
7396 * pStorage [I] The Src IStorage to get the ProgID
7397 * strProgID [I] the ProgID string to get
7398 * dwSize [I] the size of the string
7402 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7405 * This function is used by OleConvertIStorageToOLESTREAM only.
7409 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7413 LARGE_INTEGER iSeekPos;
7414 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7415 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7417 /* Open the CompObj Stream */
7418 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7419 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7423 /*Get the OleType from the CompObj Stream */
7424 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7425 iSeekPos.u.HighPart = 0;
7427 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7428 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7429 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7430 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7431 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7432 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7433 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7435 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7438 IStream_Read(pStream, strProgID, *dwSize, NULL);
7440 IStream_Release(pStream);
7445 LPOLESTR wstrProgID;
7447 /* Get the OleType from the registry */
7448 REFCLSID clsid = &(stat.clsid);
7449 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7450 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7453 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7460 /*************************************************************************
7461 * OLECONVERT_GetOle10PresData [Internal]
7463 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7466 * pStorage [I] Src IStroage
7467 * pOleStream [I] Dest OleStream Mem Struct
7473 * This function is used by OleConvertIStorageToOLESTREAM only.
7475 * Memory allocated for pData must be freed by the caller
7479 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7484 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7486 /* Initialize Default data for OLESTREAM */
7487 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7488 pOleStreamData[0].dwTypeID = 2;
7489 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7490 pOleStreamData[1].dwTypeID = 0;
7491 pOleStreamData[0].dwMetaFileWidth = 0;
7492 pOleStreamData[0].dwMetaFileHeight = 0;
7493 pOleStreamData[0].pData = NULL;
7494 pOleStreamData[1].pData = NULL;
7496 /* Open Ole10Native Stream */
7497 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7498 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7502 /* Read Size and Data */
7503 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7504 if(pOleStreamData->dwDataLength > 0)
7506 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7507 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7509 IStream_Release(pStream);
7515 /*************************************************************************
7516 * OLECONVERT_GetOle20PresData[Internal]
7518 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7521 * pStorage [I] Src IStroage
7522 * pOleStreamData [I] Dest OleStream Mem Struct
7528 * This function is used by OleConvertIStorageToOLESTREAM only.
7530 * Memory allocated for pData must be freed by the caller
7532 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7536 OLECONVERT_ISTORAGE_OLEPRES olePress;
7537 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7539 /* Initialize Default data for OLESTREAM */
7540 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7541 pOleStreamData[0].dwTypeID = 2;
7542 pOleStreamData[0].dwMetaFileWidth = 0;
7543 pOleStreamData[0].dwMetaFileHeight = 0;
7544 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7545 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7546 pOleStreamData[1].dwTypeID = 0;
7547 pOleStreamData[1].dwOleTypeNameLength = 0;
7548 pOleStreamData[1].strOleTypeName[0] = 0;
7549 pOleStreamData[1].dwMetaFileWidth = 0;
7550 pOleStreamData[1].dwMetaFileHeight = 0;
7551 pOleStreamData[1].pData = NULL;
7552 pOleStreamData[1].dwDataLength = 0;
7555 /* Open OlePress000 stream */
7556 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7557 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7560 LARGE_INTEGER iSeekPos;
7561 METAFILEPICT16 MetaFilePict;
7562 static const char strMetafilePictName[] = "METAFILEPICT";
7564 /* Set the TypeID for a Metafile */
7565 pOleStreamData[1].dwTypeID = 5;
7567 /* Set the OleTypeName to Metafile */
7568 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7569 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7571 iSeekPos.u.HighPart = 0;
7572 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7574 /* Get Presentation Data */
7575 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7576 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7577 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7578 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7580 /*Set width and Height */
7581 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7582 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7583 if(olePress.dwSize > 0)
7586 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7588 /* Set MetaFilePict struct */
7589 MetaFilePict.mm = 8;
7590 MetaFilePict.xExt = olePress.dwExtentX;
7591 MetaFilePict.yExt = olePress.dwExtentY;
7592 MetaFilePict.hMF = 0;
7594 /* Get Metafile Data */
7595 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7596 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7597 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7599 IStream_Release(pStream);
7603 /*************************************************************************
7604 * OleConvertOLESTREAMToIStorage [OLE32.@]
7609 * DVTARGETDEVICE paramenter is not handled
7610 * Still unsure of some mem fields for OLE 10 Stream
7611 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7612 * and "\001OLE" streams
7615 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7616 LPOLESTREAM pOleStream,
7618 const DVTARGETDEVICE* ptd)
7622 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7624 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7628 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7631 if(pstg == NULL || pOleStream == NULL)
7633 hRes = E_INVALIDARG;
7638 /* Load the OLESTREAM to Memory */
7639 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7644 /* Load the OLESTREAM to Memory (part 2)*/
7645 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7651 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7653 /* Do we have the IStorage Data in the OLESTREAM */
7654 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7656 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7657 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7661 /* It must be an original OLE 1.0 source */
7662 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7667 /* It must be an original OLE 1.0 source */
7668 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7671 /* Create CompObj Stream if necessary */
7672 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7675 /*Create the Ole Stream if necessary */
7676 OLECONVERT_CreateOleStream(pstg);
7681 /* Free allocated memory */
7682 for(i=0; i < 2; i++)
7684 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7685 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7686 pOleStreamData[i].pstrOleObjFileName = NULL;
7691 /*************************************************************************
7692 * OleConvertIStorageToOLESTREAM [OLE32.@]
7699 * Still unsure of some mem fields for OLE 10 Stream
7700 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7701 * and "\001OLE" streams.
7704 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7706 LPOLESTREAM pOleStream)
7709 HRESULT hRes = S_OK;
7711 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7712 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7715 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7717 if(pstg == NULL || pOleStream == NULL)
7719 hRes = E_INVALIDARG;
7723 /* Get the ProgID */
7724 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7725 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7729 /* Was it originally Ole10 */
7730 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7733 IStream_Release(pStream);
7734 /* Get Presentation Data for Ole10Native */
7735 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7739 /* Get Presentation Data (OLE20) */
7740 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7743 /* Save OLESTREAM */
7744 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7747 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7752 /* Free allocated memory */
7753 for(i=0; i < 2; i++)
7755 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7761 /***********************************************************************
7762 * GetConvertStg (OLE32.@)
7764 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7765 FIXME("unimplemented stub!\n");
7769 /******************************************************************************
7770 * StgIsStorageFile [OLE32.@]
7771 * Verify if the file contains a storage object
7777 * S_OK if file has magic bytes as a storage object
7778 * S_FALSE if file is not storage
7781 StgIsStorageFile(LPCOLESTR fn)
7787 TRACE("(\'%s\')\n", debugstr_w(fn));
7788 hf = CreateFileW(fn, GENERIC_READ,
7789 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7790 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7792 if (hf == INVALID_HANDLE_VALUE)
7793 return STG_E_FILENOTFOUND;
7795 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7797 WARN(" unable to read file\n");
7804 if (bytes_read != 8) {
7805 WARN(" too short\n");
7809 if (!memcmp(magic,STORAGE_magic,8)) {
7814 WARN(" -> Invalid header.\n");