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
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
42 #include "storage32.h"
43 #include "ole2.h" /* For Write/ReadClassStm */
46 #include "wine/wingdi16.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(storage);
53 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
54 #define OLESTREAM_ID 0x501
55 #define OLESTREAM_MAX_STR_LEN 255
57 static const char rootPropertyName[] = "Root Entry";
60 /* OLESTREAM memory structure to use for Get and Put Routines */
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
66 DWORD dwOleTypeNameLength;
67 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
68 CHAR *pstrOleObjFileName;
69 DWORD dwOleObjFileNameLength;
70 DWORD dwMetaFileWidth;
71 DWORD dwMetaFileHeight;
72 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
75 }OLECONVERT_OLESTREAM_DATA;
77 /* CompObj Stream structure */
78 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
83 DWORD dwCLSIDNameLength;
84 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
85 DWORD dwOleTypeNameLength;
86 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
87 DWORD dwProgIDNameLength;
88 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
90 }OLECONVERT_ISTORAGE_COMPOBJ;
93 /* Ole Presention Stream structure */
94 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
102 }OLECONVERT_ISTORAGE_OLEPRES;
106 /***********************************************************************
107 * Forward declaration of internal functions used by the method DestroyElement
109 static HRESULT deleteStorageProperty(
110 StorageImpl *parentStorage,
111 ULONG foundPropertyIndexToDelete,
112 StgProperty propertyToDelete);
114 static HRESULT deleteStreamProperty(
115 StorageImpl *parentStorage,
116 ULONG foundPropertyIndexToDelete,
117 StgProperty propertyToDelete);
119 static HRESULT findPlaceholder(
120 StorageImpl *storage,
121 ULONG propertyIndexToStore,
122 ULONG storagePropertyIndex,
125 static HRESULT adjustPropertyChain(
127 StgProperty propertyToDelete,
128 StgProperty parentProperty,
129 ULONG parentPropertyId,
132 /***********************************************************************
133 * Declaration of the functions used to manipulate StgProperty
136 static ULONG getFreeProperty(
137 StorageImpl *storage);
139 static void updatePropertyChain(
140 StorageImpl *storage,
141 ULONG newPropertyIndex,
142 StgProperty newProperty);
144 static LONG propertyNameCmp(
145 OLECHAR *newProperty,
146 OLECHAR *currentProperty);
149 /***********************************************************************
150 * Declaration of miscellaneous functions...
152 static HRESULT validateSTGM(DWORD stgmValue);
154 static DWORD GetShareModeFromSTGM(DWORD stgm);
155 static DWORD GetAccessModeFromSTGM(DWORD stgm);
156 static DWORD GetCreationModeFromSTGM(DWORD stgm);
159 * Virtual function table for the IStorage32Impl class.
161 static IStorageVtbl Storage32Impl_Vtbl =
163 StorageBaseImpl_QueryInterface,
164 StorageBaseImpl_AddRef,
165 StorageBaseImpl_Release,
166 StorageBaseImpl_CreateStream,
167 StorageBaseImpl_OpenStream,
168 StorageImpl_CreateStorage,
169 StorageBaseImpl_OpenStorage,
171 StorageImpl_MoveElementTo,
174 StorageBaseImpl_EnumElements,
175 StorageImpl_DestroyElement,
176 StorageBaseImpl_RenameElement,
177 StorageImpl_SetElementTimes,
178 StorageBaseImpl_SetClass,
179 StorageImpl_SetStateBits,
184 * Virtual function table for the Storage32InternalImpl class.
186 static IStorageVtbl Storage32InternalImpl_Vtbl =
188 StorageBaseImpl_QueryInterface,
189 StorageBaseImpl_AddRef,
190 StorageBaseImpl_Release,
191 StorageBaseImpl_CreateStream,
192 StorageBaseImpl_OpenStream,
193 StorageImpl_CreateStorage,
194 StorageBaseImpl_OpenStorage,
196 StorageImpl_MoveElementTo,
197 StorageInternalImpl_Commit,
198 StorageInternalImpl_Revert,
199 StorageBaseImpl_EnumElements,
200 StorageImpl_DestroyElement,
201 StorageBaseImpl_RenameElement,
202 StorageImpl_SetElementTimes,
203 StorageBaseImpl_SetClass,
204 StorageImpl_SetStateBits,
209 * Virtual function table for the IEnumSTATSTGImpl class.
211 static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
213 IEnumSTATSTGImpl_QueryInterface,
214 IEnumSTATSTGImpl_AddRef,
215 IEnumSTATSTGImpl_Release,
216 IEnumSTATSTGImpl_Next,
217 IEnumSTATSTGImpl_Skip,
218 IEnumSTATSTGImpl_Reset,
219 IEnumSTATSTGImpl_Clone
226 /************************************************************************
227 ** Storage32BaseImpl implementatiion
230 /************************************************************************
231 * Storage32BaseImpl_QueryInterface (IUnknown)
233 * This method implements the common QueryInterface for all IStorage32
234 * implementations contained in this file.
236 * See Windows documentation for more details on IUnknown methods.
238 HRESULT WINAPI StorageBaseImpl_QueryInterface(
243 ICOM_THIS(StorageBaseImpl,iface);
245 * Perform a sanity check on the parameters.
247 if ( (This==0) || (ppvObject==0) )
251 * Initialize the return parameter.
256 * Compare the riid with the interface IDs implemented by this object.
258 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
260 *ppvObject = (IStorage*)This;
262 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
264 *ppvObject = (IStorage*)This;
268 * Check that we obtained an interface.
271 return E_NOINTERFACE;
274 * Query Interface always increases the reference count by one when it is
277 StorageBaseImpl_AddRef(iface);
282 /************************************************************************
283 * Storage32BaseImpl_AddRef (IUnknown)
285 * This method implements the common AddRef for all IStorage32
286 * implementations contained in this file.
288 * See Windows documentation for more details on IUnknown methods.
290 ULONG WINAPI StorageBaseImpl_AddRef(
293 ICOM_THIS(StorageBaseImpl,iface);
299 /************************************************************************
300 * Storage32BaseImpl_Release (IUnknown)
302 * This method implements the common Release for all IStorage32
303 * implementations contained in this file.
305 * See Windows documentation for more details on IUnknown methods.
307 ULONG WINAPI StorageBaseImpl_Release(
310 ICOM_THIS(StorageBaseImpl,iface);
312 * Decrease the reference count on this object.
317 * If the reference count goes down to 0, perform suicide.
322 * Since we are using a system of base-classes, we want to call the
323 * destructor of the appropriate derived class. To do this, we are
324 * using virtual functions to implement the destructor.
326 This->v_destructor(This);
334 /************************************************************************
335 * Storage32BaseImpl_OpenStream (IStorage)
337 * This method will open the specified stream object from the current storage.
339 * See Windows documentation for more details on IStorage methods.
341 HRESULT WINAPI StorageBaseImpl_OpenStream(
343 const OLECHAR* pwcsName, /* [string][in] */
344 void* reserved1, /* [unique][in] */
345 DWORD grfMode, /* [in] */
346 DWORD reserved2, /* [in] */
347 IStream** ppstm) /* [out] */
349 ICOM_THIS(StorageBaseImpl,iface);
350 IEnumSTATSTGImpl* propertyEnumeration;
351 StgStreamImpl* newStream;
352 StgProperty currentProperty;
353 ULONG foundPropertyIndex;
354 HRESULT res = STG_E_UNKNOWN;
356 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
357 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
360 * Perform a sanity check on the parameters.
362 if ( (pwcsName==NULL) || (ppstm==0) )
369 * Initialize the out parameter
374 * Validate the STGM flags
376 if ( FAILED( validateSTGM(grfMode) ))
378 res = STG_E_INVALIDFLAG;
385 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
386 (grfMode & STGM_DELETEONRELEASE) ||
387 (grfMode & STGM_TRANSACTED) )
389 res = STG_E_INVALIDFUNCTION;
394 * Create a property enumeration to search the properties
396 propertyEnumeration = IEnumSTATSTGImpl_Construct(
397 This->ancestorStorage,
398 This->rootPropertySetIndex);
401 * Search the enumeration for the property with the given name
403 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
409 * Delete the property enumeration since we don't need it anymore
411 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
414 * If it was found, construct the stream object and return a pointer to it.
416 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
417 (currentProperty.propertyType==PROPTYPE_STREAM) )
419 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
423 newStream->grfMode = grfMode;
424 *ppstm = (IStream*)newStream;
427 * Since we are returning a pointer to the interface, we have to
428 * nail down the reference.
430 StgStreamImpl_AddRef(*ppstm);
440 res = STG_E_FILENOTFOUND;
444 TRACE("<-- IStream %p\n", *ppstm);
445 TRACE("<-- %08lx\n", res);
449 /************************************************************************
450 * Storage32BaseImpl_OpenStorage (IStorage)
452 * This method will open a new storage object from the current storage.
454 * See Windows documentation for more details on IStorage methods.
456 HRESULT WINAPI StorageBaseImpl_OpenStorage(
458 const OLECHAR* pwcsName, /* [string][unique][in] */
459 IStorage* pstgPriority, /* [unique][in] */
460 DWORD grfMode, /* [in] */
461 SNB snbExclude, /* [unique][in] */
462 DWORD reserved, /* [in] */
463 IStorage** ppstg) /* [out] */
465 ICOM_THIS(StorageBaseImpl,iface);
466 StorageInternalImpl* newStorage;
467 IEnumSTATSTGImpl* propertyEnumeration;
468 StgProperty currentProperty;
469 ULONG foundPropertyIndex;
470 HRESULT res = STG_E_UNKNOWN;
472 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
473 iface, debugstr_w(pwcsName), pstgPriority,
474 grfMode, snbExclude, reserved, ppstg);
477 * Perform a sanity check on the parameters.
479 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
486 if (snbExclude != NULL)
488 res = STG_E_INVALIDPARAMETER;
493 * Validate the STGM flags
495 if ( FAILED( validateSTGM(grfMode) ))
497 res = STG_E_INVALIDFLAG;
504 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
505 (grfMode & STGM_DELETEONRELEASE) ||
506 (grfMode & STGM_PRIORITY) )
508 res = STG_E_INVALIDFUNCTION;
513 * Initialize the out parameter
518 * Create a property enumeration to search the properties
520 propertyEnumeration = IEnumSTATSTGImpl_Construct(
521 This->ancestorStorage,
522 This->rootPropertySetIndex);
525 * Search the enumeration for the property with the given name
527 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
533 * Delete the property enumeration since we don't need it anymore
535 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
538 * If it was found, construct the stream object and return a pointer to it.
540 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
541 (currentProperty.propertyType==PROPTYPE_STORAGE) )
544 * Construct a new Storage object
546 newStorage = StorageInternalImpl_Construct(
547 This->ancestorStorage,
552 *ppstg = (IStorage*)newStorage;
555 * Since we are returning a pointer to the interface,
556 * we have to nail down the reference.
558 StorageBaseImpl_AddRef(*ppstg);
564 res = STG_E_INSUFFICIENTMEMORY;
568 res = STG_E_FILENOTFOUND;
571 TRACE("<-- %08lx\n", res);
575 /************************************************************************
576 * Storage32BaseImpl_EnumElements (IStorage)
578 * This method will create an enumerator object that can be used to
579 * retrieve informatino about all the properties in the storage object.
581 * See Windows documentation for more details on IStorage methods.
583 HRESULT WINAPI StorageBaseImpl_EnumElements(
585 DWORD reserved1, /* [in] */
586 void* reserved2, /* [size_is][unique][in] */
587 DWORD reserved3, /* [in] */
588 IEnumSTATSTG** ppenum) /* [out] */
590 ICOM_THIS(StorageBaseImpl,iface);
591 IEnumSTATSTGImpl* newEnum;
593 TRACE("(%p, %ld, %p, %ld, %p)\n",
594 iface, reserved1, reserved2, reserved3, ppenum);
597 * Perform a sanity check on the parameters.
599 if ( (This==0) || (ppenum==0))
603 * Construct the enumerator.
605 newEnum = IEnumSTATSTGImpl_Construct(
606 This->ancestorStorage,
607 This->rootPropertySetIndex);
611 *ppenum = (IEnumSTATSTG*)newEnum;
614 * Don't forget to nail down a reference to the new object before
617 IEnumSTATSTGImpl_AddRef(*ppenum);
622 return E_OUTOFMEMORY;
625 /************************************************************************
626 * Storage32BaseImpl_Stat (IStorage)
628 * This method will retrieve information about this storage object.
630 * See Windows documentation for more details on IStorage methods.
632 HRESULT WINAPI StorageBaseImpl_Stat(
634 STATSTG* pstatstg, /* [out] */
635 DWORD grfStatFlag) /* [in] */
637 ICOM_THIS(StorageBaseImpl,iface);
638 StgProperty curProperty;
640 HRESULT res = STG_E_UNKNOWN;
642 TRACE("(%p, %p, %lx)\n",
643 iface, pstatstg, grfStatFlag);
646 * Perform a sanity check on the parameters.
648 if ( (This==0) || (pstatstg==0))
655 * Read the information from the property.
657 readSuccessful = StorageImpl_ReadProperty(
658 This->ancestorStorage,
659 This->rootPropertySetIndex,
664 StorageUtl_CopyPropertyToSTATSTG(
678 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);
680 TRACE("<-- %08lx\n", res);
684 /************************************************************************
685 * Storage32BaseImpl_RenameElement (IStorage)
687 * This method will rename the specified element.
689 * See Windows documentation for more details on IStorage methods.
691 * Implementation notes: The method used to rename consists of creating a clone
692 * of the deleted StgProperty object setting it with the new name and to
693 * perform a DestroyElement of the old StgProperty.
695 HRESULT WINAPI StorageBaseImpl_RenameElement(
697 const OLECHAR* pwcsOldName, /* [in] */
698 const OLECHAR* pwcsNewName) /* [in] */
700 ICOM_THIS(StorageBaseImpl,iface);
701 IEnumSTATSTGImpl* propertyEnumeration;
702 StgProperty currentProperty;
703 ULONG foundPropertyIndex;
705 TRACE("(%p, %s, %s)\n",
706 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
709 * Create a property enumeration to search the properties
711 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
712 This->rootPropertySetIndex);
715 * Search the enumeration for the new property name
717 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
721 if (foundPropertyIndex != PROPERTY_NULL)
724 * There is already a property with the new name
726 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
727 return STG_E_FILEALREADYEXISTS;
730 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
733 * Search the enumeration for the old property name
735 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
740 * Delete the property enumeration since we don't need it anymore
742 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
744 if (foundPropertyIndex != PROPERTY_NULL)
746 StgProperty renamedProperty;
747 ULONG renamedPropertyIndex;
750 * Setup a new property for the renamed property
752 renamedProperty.sizeOfNameString =
753 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
755 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
756 return STG_E_INVALIDNAME;
758 strcpyW(renamedProperty.name, pwcsNewName);
760 renamedProperty.propertyType = currentProperty.propertyType;
761 renamedProperty.startingBlock = currentProperty.startingBlock;
762 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
763 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
765 renamedProperty.previousProperty = PROPERTY_NULL;
766 renamedProperty.nextProperty = PROPERTY_NULL;
769 * Bring the dirProperty link in case it is a storage and in which
770 * case the renamed storage elements don't require to be reorganized.
772 renamedProperty.dirProperty = currentProperty.dirProperty;
774 /* call CoFileTime to get the current time
775 renamedProperty.timeStampS1
776 renamedProperty.timeStampD1
777 renamedProperty.timeStampS2
778 renamedProperty.timeStampD2
779 renamedProperty.propertyUniqueID
783 * Obtain a free property in the property chain
785 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
788 * Save the new property into the new property spot
790 StorageImpl_WriteProperty(
791 This->ancestorStorage,
792 renamedPropertyIndex,
796 * Find a spot in the property chain for our newly created property.
800 renamedPropertyIndex,
804 * At this point the renamed property has been inserted in the tree,
805 * now, before to Destroy the old property we must zeroed it's dirProperty
806 * otherwise the DestroyProperty below will zap it all and we do not want
808 * Also, we fake that the old property is a storage so the DestroyProperty
809 * will not do a SetSize(0) on the stream data.
811 * This means that we need to tweek the StgProperty if it is a stream or a
814 StorageImpl_ReadProperty(This->ancestorStorage,
818 currentProperty.dirProperty = PROPERTY_NULL;
819 currentProperty.propertyType = PROPTYPE_STORAGE;
820 StorageImpl_WriteProperty(
821 This->ancestorStorage,
826 * Invoke Destroy to get rid of the ole property and automatically redo
827 * the linking of it's previous and next members...
829 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
835 * There is no property with the old name
837 return STG_E_FILENOTFOUND;
843 /************************************************************************
844 * Storage32BaseImpl_CreateStream (IStorage)
846 * This method will create a stream object within this storage
848 * See Windows documentation for more details on IStorage methods.
850 HRESULT WINAPI StorageBaseImpl_CreateStream(
852 const OLECHAR* pwcsName, /* [string][in] */
853 DWORD grfMode, /* [in] */
854 DWORD reserved1, /* [in] */
855 DWORD reserved2, /* [in] */
856 IStream** ppstm) /* [out] */
858 ICOM_THIS(StorageBaseImpl,iface);
859 IEnumSTATSTGImpl* propertyEnumeration;
860 StgStreamImpl* newStream;
861 StgProperty currentProperty, newStreamProperty;
862 ULONG foundPropertyIndex, newPropertyIndex;
864 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
865 iface, debugstr_w(pwcsName), grfMode,
866 reserved1, reserved2, ppstm);
869 * Validate parameters
872 return STG_E_INVALIDPOINTER;
875 return STG_E_INVALIDNAME;
878 * Validate the STGM flags
880 if ( FAILED( validateSTGM(grfMode) ))
881 return STG_E_INVALIDFLAG;
886 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
887 (grfMode & STGM_DELETEONRELEASE) ||
888 (grfMode & STGM_TRANSACTED) )
889 return STG_E_INVALIDFUNCTION;
892 * Initialize the out parameter
897 * Create a property enumeration to search the properties
899 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
900 This->rootPropertySetIndex);
902 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
906 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
908 if (foundPropertyIndex != PROPERTY_NULL)
911 * An element with this name already exists
913 if (grfMode & STGM_CREATE)
915 IStorage_DestroyElement(iface, pwcsName);
918 return STG_E_FILEALREADYEXISTS;
922 * memset the empty property
924 memset(&newStreamProperty, 0, sizeof(StgProperty));
926 newStreamProperty.sizeOfNameString =
927 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
929 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
930 return STG_E_INVALIDNAME;
932 strcpyW(newStreamProperty.name, pwcsName);
934 newStreamProperty.propertyType = PROPTYPE_STREAM;
935 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
936 newStreamProperty.size.u.LowPart = 0;
937 newStreamProperty.size.u.HighPart = 0;
939 newStreamProperty.previousProperty = PROPERTY_NULL;
940 newStreamProperty.nextProperty = PROPERTY_NULL;
941 newStreamProperty.dirProperty = PROPERTY_NULL;
943 /* call CoFileTime to get the current time
944 newStreamProperty.timeStampS1
945 newStreamProperty.timeStampD1
946 newStreamProperty.timeStampS2
947 newStreamProperty.timeStampD2
950 /* newStreamProperty.propertyUniqueID */
953 * Get a free property or create a new one
955 newPropertyIndex = getFreeProperty(This->ancestorStorage);
958 * Save the new property into the new property spot
960 StorageImpl_WriteProperty(
961 This->ancestorStorage,
966 * Find a spot in the property chain for our newly created property.
974 * Open the stream to return it.
976 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
980 *ppstm = (IStream*)newStream;
983 * Since we are returning a pointer to the interface, we have to nail down
986 StgStreamImpl_AddRef(*ppstm);
990 return STG_E_INSUFFICIENTMEMORY;
996 /************************************************************************
997 * Storage32BaseImpl_SetClass (IStorage)
999 * This method will write the specified CLSID in the property of this
1002 * See Windows documentation for more details on IStorage methods.
1004 HRESULT WINAPI StorageBaseImpl_SetClass(
1006 REFCLSID clsid) /* [in] */
1008 ICOM_THIS(StorageBaseImpl,iface);
1009 HRESULT hRes = E_FAIL;
1010 StgProperty curProperty;
1013 TRACE("(%p, %p)\n", iface, clsid);
1015 success = StorageImpl_ReadProperty(This->ancestorStorage,
1016 This->rootPropertySetIndex,
1020 curProperty.propertyUniqueID = *clsid;
1022 success = StorageImpl_WriteProperty(This->ancestorStorage,
1023 This->rootPropertySetIndex,
1032 /************************************************************************
1033 ** Storage32Impl implementation
1036 /************************************************************************
1037 * Storage32Impl_CreateStorage (IStorage)
1039 * This method will create the storage object within the provided storage.
1041 * See Windows documentation for more details on IStorage methods.
1043 HRESULT WINAPI StorageImpl_CreateStorage(
1045 const OLECHAR *pwcsName, /* [string][in] */
1046 DWORD grfMode, /* [in] */
1047 DWORD reserved1, /* [in] */
1048 DWORD reserved2, /* [in] */
1049 IStorage **ppstg) /* [out] */
1051 StorageImpl* const This=(StorageImpl*)iface;
1053 IEnumSTATSTGImpl *propertyEnumeration;
1054 StgProperty currentProperty;
1055 StgProperty newProperty;
1056 ULONG foundPropertyIndex;
1057 ULONG newPropertyIndex;
1060 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1061 iface, debugstr_w(pwcsName), grfMode,
1062 reserved1, reserved2, ppstg);
1065 * Validate parameters
1068 return STG_E_INVALIDPOINTER;
1071 return STG_E_INVALIDNAME;
1074 * Validate the STGM flags
1076 if ( FAILED( validateSTGM(grfMode) ) ||
1077 (grfMode & STGM_DELETEONRELEASE) )
1078 return STG_E_INVALIDFLAG;
1081 * Initialize the out parameter
1086 * Create a property enumeration and search the properties
1088 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1089 This->rootPropertySetIndex);
1091 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1094 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1096 if (foundPropertyIndex != PROPERTY_NULL)
1099 * An element with this name already exists
1101 if (grfMode & STGM_CREATE)
1102 IStorage_DestroyElement(iface, pwcsName);
1104 return STG_E_FILEALREADYEXISTS;
1108 * memset the empty property
1110 memset(&newProperty, 0, sizeof(StgProperty));
1112 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1114 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1115 return STG_E_INVALIDNAME;
1117 strcpyW(newProperty.name, pwcsName);
1119 newProperty.propertyType = PROPTYPE_STORAGE;
1120 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1121 newProperty.size.u.LowPart = 0;
1122 newProperty.size.u.HighPart = 0;
1124 newProperty.previousProperty = PROPERTY_NULL;
1125 newProperty.nextProperty = PROPERTY_NULL;
1126 newProperty.dirProperty = PROPERTY_NULL;
1128 /* call CoFileTime to get the current time
1129 newProperty.timeStampS1
1130 newProperty.timeStampD1
1131 newProperty.timeStampS2
1132 newProperty.timeStampD2
1135 /* newStorageProperty.propertyUniqueID */
1138 * Obtain a free property in the property chain
1140 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1143 * Save the new property into the new property spot
1145 StorageImpl_WriteProperty(
1146 This->ancestorStorage,
1151 * Find a spot in the property chain for our newly created property.
1153 updatePropertyChain(
1159 * Open it to get a pointer to return.
1161 hr = IStorage_OpenStorage(
1170 if( (hr != S_OK) || (*ppstg == NULL))
1180 /***************************************************************************
1184 * Get a free property or create a new one.
1186 static ULONG getFreeProperty(
1187 StorageImpl *storage)
1189 ULONG currentPropertyIndex = 0;
1190 ULONG newPropertyIndex = PROPERTY_NULL;
1191 BOOL readSuccessful = TRUE;
1192 StgProperty currentProperty;
1197 * Start by reading the root property
1199 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1200 currentPropertyIndex,
1204 if (currentProperty.sizeOfNameString == 0)
1207 * The property existis and is available, we found it.
1209 newPropertyIndex = currentPropertyIndex;
1215 * We exhausted the property list, we will create more space below
1217 newPropertyIndex = currentPropertyIndex;
1219 currentPropertyIndex++;
1221 } while (newPropertyIndex == PROPERTY_NULL);
1224 * grow the property chain
1226 if (! readSuccessful)
1228 StgProperty emptyProperty;
1229 ULARGE_INTEGER newSize;
1230 ULONG propertyIndex;
1231 ULONG lastProperty = 0;
1232 ULONG blockCount = 0;
1235 * obtain the new count of property blocks
1237 blockCount = BlockChainStream_GetCount(
1238 storage->ancestorStorage->rootBlockChain)+1;
1241 * initialize the size used by the property stream
1243 newSize.u.HighPart = 0;
1244 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1247 * add a property block to the property chain
1249 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1252 * memset the empty property in order to initialize the unused newly
1255 memset(&emptyProperty, 0, sizeof(StgProperty));
1260 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1263 propertyIndex = newPropertyIndex;
1264 propertyIndex < lastProperty;
1267 StorageImpl_WriteProperty(
1268 storage->ancestorStorage,
1274 return newPropertyIndex;
1277 /****************************************************************************
1281 * Case insensitive comparaison of StgProperty.name by first considering
1284 * Returns <0 when newPrpoerty < currentProperty
1285 * >0 when newPrpoerty > currentProperty
1286 * 0 when newPrpoerty == currentProperty
1288 static LONG propertyNameCmp(
1289 OLECHAR *newProperty,
1290 OLECHAR *currentProperty)
1292 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1297 * We compare the string themselves only when they are of the same length
1299 diff = lstrcmpiW( newProperty, currentProperty);
1305 /****************************************************************************
1309 * Properly link this new element in the property chain.
1311 static void updatePropertyChain(
1312 StorageImpl *storage,
1313 ULONG newPropertyIndex,
1314 StgProperty newProperty)
1316 StgProperty currentProperty;
1319 * Read the root property
1321 StorageImpl_ReadProperty(storage->ancestorStorage,
1322 storage->rootPropertySetIndex,
1325 if (currentProperty.dirProperty != PROPERTY_NULL)
1328 * The root storage contains some element, therefore, start the research
1329 * for the appropriate location.
1332 ULONG current, next, previous, currentPropertyId;
1335 * Keep the StgProperty sequence number of the storage first property
1337 currentPropertyId = currentProperty.dirProperty;
1342 StorageImpl_ReadProperty(storage->ancestorStorage,
1343 currentProperty.dirProperty,
1346 previous = currentProperty.previousProperty;
1347 next = currentProperty.nextProperty;
1348 current = currentPropertyId;
1352 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1356 if (previous != PROPERTY_NULL)
1358 StorageImpl_ReadProperty(storage->ancestorStorage,
1365 currentProperty.previousProperty = newPropertyIndex;
1366 StorageImpl_WriteProperty(storage->ancestorStorage,
1374 if (next != PROPERTY_NULL)
1376 StorageImpl_ReadProperty(storage->ancestorStorage,
1383 currentProperty.nextProperty = newPropertyIndex;
1384 StorageImpl_WriteProperty(storage->ancestorStorage,
1393 * Trying to insert an item with the same name in the
1394 * subtree structure.
1399 previous = currentProperty.previousProperty;
1400 next = currentProperty.nextProperty;
1406 * The root storage is empty, link the new property to it's dir property
1408 currentProperty.dirProperty = newPropertyIndex;
1409 StorageImpl_WriteProperty(storage->ancestorStorage,
1410 storage->rootPropertySetIndex,
1416 /*************************************************************************
1419 HRESULT WINAPI StorageImpl_CopyTo(
1421 DWORD ciidExclude, /* [in] */
1422 const IID* rgiidExclude, /* [size_is][unique][in] */
1423 SNB snbExclude, /* [unique][in] */
1424 IStorage* pstgDest) /* [unique][in] */
1426 IEnumSTATSTG *elements = 0;
1427 STATSTG curElement, strStat;
1429 IStorage *pstgTmp, *pstgChild;
1430 IStream *pstrTmp, *pstrChild;
1432 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1433 FIXME("Exclude option not implemented\n");
1435 TRACE("(%p, %ld, %p, %p, %p)\n",
1436 iface, ciidExclude, rgiidExclude,
1437 snbExclude, pstgDest);
1440 * Perform a sanity check
1442 if ( pstgDest == 0 )
1443 return STG_E_INVALIDPOINTER;
1446 * Enumerate the elements
1448 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1456 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1457 IStorage_SetClass( pstgDest, &curElement.clsid );
1462 * Obtain the next element
1464 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1466 if ( hr == S_FALSE )
1468 hr = S_OK; /* done, every element has been copied */
1472 if (curElement.type == STGTY_STORAGE)
1475 * open child source storage
1477 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1478 STGM_READ|STGM_SHARE_EXCLUSIVE,
1479 NULL, 0, &pstgChild );
1485 * Check if destination storage is not a child of the source
1486 * storage, which will cause an infinite loop
1488 if (pstgChild == pstgDest)
1490 IEnumSTATSTG_Release(elements);
1492 return STG_E_ACCESSDENIED;
1496 * create a new storage in destination storage
1498 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1499 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1503 * if it already exist, don't create a new one use this one
1505 if (hr == STG_E_FILEALREADYEXISTS)
1507 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1508 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1509 NULL, 0, &pstgTmp );
1517 * do the copy recursively
1519 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1520 snbExclude, pstgTmp );
1522 IStorage_Release( pstgTmp );
1523 IStorage_Release( pstgChild );
1525 else if (curElement.type == STGTY_STREAM)
1528 * create a new stream in destination storage. If the stream already
1529 * exist, it will be deleted and a new one will be created.
1531 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1532 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1539 * open child stream storage
1541 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1542 STGM_READ|STGM_SHARE_EXCLUSIVE,
1549 * Get the size of the source stream
1551 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1554 * Set the size of the destination stream.
1556 IStream_SetSize(pstrTmp, strStat.cbSize);
1561 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1564 IStream_Release( pstrTmp );
1565 IStream_Release( pstrChild );
1569 WARN("unknown element type: %ld\n", curElement.type);
1572 } while (hr == S_OK);
1577 IEnumSTATSTG_Release(elements);
1582 /*************************************************************************
1583 * MoveElementTo (IStorage)
1585 HRESULT WINAPI StorageImpl_MoveElementTo(
1587 const OLECHAR *pwcsName, /* [string][in] */
1588 IStorage *pstgDest, /* [unique][in] */
1589 const OLECHAR *pwcsNewName,/* [string][in] */
1590 DWORD grfFlags) /* [in] */
1592 FIXME("not implemented!\n");
1596 /*************************************************************************
1599 HRESULT WINAPI StorageImpl_Commit(
1601 DWORD grfCommitFlags)/* [in] */
1603 FIXME("(%ld): stub!\n", grfCommitFlags);
1607 /*************************************************************************
1610 HRESULT WINAPI StorageImpl_Revert(
1613 FIXME("not implemented!\n");
1617 /*************************************************************************
1618 * DestroyElement (IStorage)
1620 * Stategy: This implementation is build this way for simplicity not for speed.
1621 * I always delete the top most element of the enumeration and adjust
1622 * the deleted element pointer all the time. This takes longer to
1623 * do but allow to reinvoke DestroyElement whenever we encounter a
1624 * storage object. The optimisation reside in the usage of another
1625 * enumeration stategy that would give all the leaves of a storage
1626 * first. (postfix order)
1628 HRESULT WINAPI StorageImpl_DestroyElement(
1630 const OLECHAR *pwcsName)/* [string][in] */
1632 StorageImpl* const This=(StorageImpl*)iface;
1634 IEnumSTATSTGImpl* propertyEnumeration;
1637 StgProperty propertyToDelete;
1638 StgProperty parentProperty;
1639 ULONG foundPropertyIndexToDelete;
1640 ULONG typeOfRelation;
1641 ULONG parentPropertyId;
1644 iface, debugstr_w(pwcsName));
1647 * Perform a sanity check on the parameters.
1650 return STG_E_INVALIDPOINTER;
1653 * Create a property enumeration to search the property with the given name
1655 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1656 This->ancestorStorage,
1657 This->rootPropertySetIndex);
1659 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1660 propertyEnumeration,
1664 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1666 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1668 return STG_E_FILENOTFOUND;
1672 * Find the parent property of the property to delete (the one that
1673 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1674 * the parent is This. Otherwise, the parent is one of it's sibling...
1678 * First, read This's StgProperty..
1680 res = StorageImpl_ReadProperty(
1681 This->ancestorStorage,
1682 This->rootPropertySetIndex,
1688 * Second, check to see if by any chance the actual storage (This) is not
1689 * the parent of the property to delete... We never know...
1691 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1694 * Set data as it would have been done in the else part...
1696 typeOfRelation = PROPERTY_RELATION_DIR;
1697 parentPropertyId = This->rootPropertySetIndex;
1702 * Create a property enumeration to search the parent properties, and
1703 * delete it once done.
1705 IEnumSTATSTGImpl* propertyEnumeration2;
1707 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1708 This->ancestorStorage,
1709 This->rootPropertySetIndex);
1711 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1712 propertyEnumeration2,
1713 foundPropertyIndexToDelete,
1717 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1720 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1722 hr = deleteStorageProperty(
1724 foundPropertyIndexToDelete,
1727 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1729 hr = deleteStreamProperty(
1731 foundPropertyIndexToDelete,
1739 * Adjust the property chain
1741 hr = adjustPropertyChain(
1752 /************************************************************************
1753 * StorageImpl_Stat (IStorage)
1755 * This method will retrieve information about this storage object.
1757 * See Windows documentation for more details on IStorage methods.
1759 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1760 STATSTG* pstatstg, /* [out] */
1761 DWORD grfStatFlag) /* [in] */
1763 StorageImpl* const This = (StorageImpl*)iface;
1764 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1766 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1768 CoTaskMemFree(pstatstg->pwcsName);
1769 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1770 strcpyW(pstatstg->pwcsName, This->pwcsName);
1778 /*********************************************************************
1782 * Perform the deletion of a complete storage node
1785 static HRESULT deleteStorageProperty(
1786 StorageImpl *parentStorage,
1787 ULONG indexOfPropertyToDelete,
1788 StgProperty propertyToDelete)
1790 IEnumSTATSTG *elements = 0;
1791 IStorage *childStorage = 0;
1792 STATSTG currentElement;
1794 HRESULT destroyHr = S_OK;
1797 * Open the storage and enumerate it
1799 hr = StorageBaseImpl_OpenStorage(
1800 (IStorage*)parentStorage,
1801 propertyToDelete.name,
1803 STGM_SHARE_EXCLUSIVE,
1814 * Enumerate the elements
1816 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1821 * Obtain the next element
1823 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1826 destroyHr = StorageImpl_DestroyElement(
1827 (IStorage*)childStorage,
1828 (OLECHAR*)currentElement.pwcsName);
1830 CoTaskMemFree(currentElement.pwcsName);
1834 * We need to Reset the enumeration every time because we delete elements
1835 * and the enumeration could be invalid
1837 IEnumSTATSTG_Reset(elements);
1839 } while ((hr == S_OK) && (destroyHr == S_OK));
1842 * Invalidate the property by zeroing it's name member.
1844 propertyToDelete.sizeOfNameString = 0;
1846 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1847 indexOfPropertyToDelete,
1850 IStorage_Release(childStorage);
1851 IEnumSTATSTG_Release(elements);
1856 /*********************************************************************
1860 * Perform the deletion of a stream node
1863 static HRESULT deleteStreamProperty(
1864 StorageImpl *parentStorage,
1865 ULONG indexOfPropertyToDelete,
1866 StgProperty propertyToDelete)
1870 ULARGE_INTEGER size;
1872 size.u.HighPart = 0;
1875 hr = StorageBaseImpl_OpenStream(
1876 (IStorage*)parentStorage,
1877 (OLECHAR*)propertyToDelete.name,
1879 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1891 hr = IStream_SetSize(pis, size);
1899 * Release the stream object.
1901 IStream_Release(pis);
1904 * Invalidate the property by zeroing it's name member.
1906 propertyToDelete.sizeOfNameString = 0;
1909 * Here we should re-read the property so we get the updated pointer
1910 * but since we are here to zap it, I don't do it...
1912 StorageImpl_WriteProperty(
1913 parentStorage->ancestorStorage,
1914 indexOfPropertyToDelete,
1920 /*********************************************************************
1924 * Finds a placeholder for the StgProperty within the Storage
1927 static HRESULT findPlaceholder(
1928 StorageImpl *storage,
1929 ULONG propertyIndexToStore,
1930 ULONG storePropertyIndex,
1933 StgProperty storeProperty;
1938 * Read the storage property
1940 res = StorageImpl_ReadProperty(
1941 storage->ancestorStorage,
1950 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1952 if (storeProperty.previousProperty != PROPERTY_NULL)
1954 return findPlaceholder(
1956 propertyIndexToStore,
1957 storeProperty.previousProperty,
1962 storeProperty.previousProperty = propertyIndexToStore;
1965 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1967 if (storeProperty.nextProperty != PROPERTY_NULL)
1969 return findPlaceholder(
1971 propertyIndexToStore,
1972 storeProperty.nextProperty,
1977 storeProperty.nextProperty = propertyIndexToStore;
1980 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1982 if (storeProperty.dirProperty != PROPERTY_NULL)
1984 return findPlaceholder(
1986 propertyIndexToStore,
1987 storeProperty.dirProperty,
1992 storeProperty.dirProperty = propertyIndexToStore;
1996 hr = StorageImpl_WriteProperty(
1997 storage->ancestorStorage,
2009 /*************************************************************************
2013 * This method takes the previous and the next property link of a property
2014 * to be deleted and find them a place in the Storage.
2016 static HRESULT adjustPropertyChain(
2018 StgProperty propertyToDelete,
2019 StgProperty parentProperty,
2020 ULONG parentPropertyId,
2023 ULONG newLinkProperty = PROPERTY_NULL;
2024 BOOL needToFindAPlaceholder = FALSE;
2025 ULONG storeNode = PROPERTY_NULL;
2026 ULONG toStoreNode = PROPERTY_NULL;
2027 INT relationType = 0;
2031 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2033 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2036 * Set the parent previous to the property to delete previous
2038 newLinkProperty = propertyToDelete.previousProperty;
2040 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2043 * We also need to find a storage for the other link, setup variables
2044 * to do this at the end...
2046 needToFindAPlaceholder = TRUE;
2047 storeNode = propertyToDelete.previousProperty;
2048 toStoreNode = propertyToDelete.nextProperty;
2049 relationType = PROPERTY_RELATION_NEXT;
2052 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2055 * Set the parent previous to the property to delete next
2057 newLinkProperty = propertyToDelete.nextProperty;
2061 * Link it for real...
2063 parentProperty.previousProperty = newLinkProperty;
2066 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2068 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2071 * Set the parent next to the property to delete next previous
2073 newLinkProperty = propertyToDelete.previousProperty;
2075 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2078 * We also need to find a storage for the other link, setup variables
2079 * to do this at the end...
2081 needToFindAPlaceholder = TRUE;
2082 storeNode = propertyToDelete.previousProperty;
2083 toStoreNode = propertyToDelete.nextProperty;
2084 relationType = PROPERTY_RELATION_NEXT;
2087 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2090 * Set the parent next to the property to delete next
2092 newLinkProperty = propertyToDelete.nextProperty;
2096 * Link it for real...
2098 parentProperty.nextProperty = newLinkProperty;
2100 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2102 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2105 * Set the parent dir to the property to delete previous
2107 newLinkProperty = propertyToDelete.previousProperty;
2109 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2112 * We also need to find a storage for the other link, setup variables
2113 * to do this at the end...
2115 needToFindAPlaceholder = TRUE;
2116 storeNode = propertyToDelete.previousProperty;
2117 toStoreNode = propertyToDelete.nextProperty;
2118 relationType = PROPERTY_RELATION_NEXT;
2121 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2124 * Set the parent dir to the property to delete next
2126 newLinkProperty = propertyToDelete.nextProperty;
2130 * Link it for real...
2132 parentProperty.dirProperty = newLinkProperty;
2136 * Write back the parent property
2138 res = StorageImpl_WriteProperty(
2139 This->ancestorStorage,
2148 * If a placeholder is required for the other link, then, find one and
2149 * get out of here...
2151 if (needToFindAPlaceholder)
2153 hr = findPlaceholder(
2164 /******************************************************************************
2165 * SetElementTimes (IStorage)
2167 HRESULT WINAPI StorageImpl_SetElementTimes(
2169 const OLECHAR *pwcsName,/* [string][in] */
2170 const FILETIME *pctime, /* [in] */
2171 const FILETIME *patime, /* [in] */
2172 const FILETIME *pmtime) /* [in] */
2174 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2178 /******************************************************************************
2179 * SetStateBits (IStorage)
2181 HRESULT WINAPI StorageImpl_SetStateBits(
2183 DWORD grfStateBits,/* [in] */
2184 DWORD grfMask) /* [in] */
2186 FIXME("not implemented!\n");
2190 HRESULT StorageImpl_Construct(
2200 StgProperty currentProperty;
2201 BOOL readSuccessful;
2202 ULONG currentPropertyIndex;
2204 if ( FAILED( validateSTGM(openFlags) ))
2205 return STG_E_INVALIDFLAG;
2207 memset(This, 0, sizeof(StorageImpl));
2210 * Initialize the virtual function table.
2212 This->lpVtbl = &Storage32Impl_Vtbl;
2213 This->v_destructor = &StorageImpl_Destroy;
2216 * This is the top-level storage so initialize the ancestor pointer
2219 This->ancestorStorage = This;
2222 * Initialize the physical support of the storage.
2224 This->hFile = hFile;
2227 * Store copy of file path.
2230 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2231 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2232 if (!This->pwcsName)
2233 return STG_E_INSUFFICIENTMEMORY;
2234 strcpyW(This->pwcsName, pwcsName);
2238 * Initialize the big block cache.
2240 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2241 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2242 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2248 if (This->bigBlockFile == 0)
2253 ULARGE_INTEGER size;
2254 BYTE* bigBlockBuffer;
2257 * Initialize all header variables:
2258 * - The big block depot consists of one block and it is at block 0
2259 * - The properties start at block 1
2260 * - There is no small block depot
2262 memset( This->bigBlockDepotStart,
2264 sizeof(This->bigBlockDepotStart));
2266 This->bigBlockDepotCount = 1;
2267 This->bigBlockDepotStart[0] = 0;
2268 This->rootStartBlock = 1;
2269 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2270 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2271 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2272 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2273 This->extBigBlockDepotCount = 0;
2275 StorageImpl_SaveFileHeader(This);
2278 * Add one block for the big block depot and one block for the properties
2280 size.u.HighPart = 0;
2281 size.u.LowPart = This->bigBlockSize * 3;
2282 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2285 * Initialize the big block depot
2287 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2288 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2289 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2290 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2291 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2296 * Load the header for the file.
2298 hr = StorageImpl_LoadFileHeader(This);
2302 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2309 * There is no block depot cached yet.
2311 This->indexBlockDepotCached = 0xFFFFFFFF;
2314 * Start searching for free blocks with block 0.
2316 This->prevFreeBlock = 0;
2319 * Create the block chain abstractions.
2321 if(!(This->rootBlockChain =
2322 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2323 return STG_E_READFAULT;
2325 if(!(This->smallBlockDepotChain =
2326 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2328 return STG_E_READFAULT;
2331 * Write the root property
2335 StgProperty rootProp;
2337 * Initialize the property chain
2339 memset(&rootProp, 0, sizeof(rootProp));
2340 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2341 sizeof(rootProp.name)/sizeof(WCHAR) );
2342 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2343 rootProp.propertyType = PROPTYPE_ROOT;
2344 rootProp.previousProperty = PROPERTY_NULL;
2345 rootProp.nextProperty = PROPERTY_NULL;
2346 rootProp.dirProperty = PROPERTY_NULL;
2347 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2348 rootProp.size.u.HighPart = 0;
2349 rootProp.size.u.LowPart = 0;
2351 StorageImpl_WriteProperty(This, 0, &rootProp);
2355 * Find the ID of the root in the property sets.
2357 currentPropertyIndex = 0;
2361 readSuccessful = StorageImpl_ReadProperty(
2363 currentPropertyIndex,
2368 if ( (currentProperty.sizeOfNameString != 0 ) &&
2369 (currentProperty.propertyType == PROPTYPE_ROOT) )
2371 This->rootPropertySetIndex = currentPropertyIndex;
2375 currentPropertyIndex++;
2377 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2379 if (!readSuccessful)
2382 return STG_E_READFAULT;
2386 * Create the block chain abstraction for the small block root chain.
2388 if(!(This->smallBlockRootChain =
2389 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2390 return STG_E_READFAULT;
2395 void StorageImpl_Destroy(
2398 TRACE("(%p)\n", This);
2401 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2403 BlockChainStream_Destroy(This->smallBlockRootChain);
2404 BlockChainStream_Destroy(This->rootBlockChain);
2405 BlockChainStream_Destroy(This->smallBlockDepotChain);
2407 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2411 /******************************************************************************
2412 * Storage32Impl_GetNextFreeBigBlock
2414 * Returns the index of the next free big block.
2415 * If the big block depot is filled, this method will enlarge it.
2418 ULONG StorageImpl_GetNextFreeBigBlock(
2421 ULONG depotBlockIndexPos;
2423 ULONG depotBlockOffset;
2424 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2425 ULONG nextBlockIndex = BLOCK_SPECIAL;
2427 ULONG freeBlock = BLOCK_UNUSED;
2429 depotIndex = This->prevFreeBlock / blocksPerDepot;
2430 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2433 * Scan the entire big block depot until we find a block marked free
2435 while (nextBlockIndex != BLOCK_UNUSED)
2437 if (depotIndex < COUNT_BBDEPOTINHEADER)
2439 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2442 * Grow the primary depot.
2444 if (depotBlockIndexPos == BLOCK_UNUSED)
2446 depotBlockIndexPos = depotIndex*blocksPerDepot;
2449 * Add a block depot.
2451 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2452 This->bigBlockDepotCount++;
2453 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2456 * Flag it as a block depot.
2458 StorageImpl_SetNextBlockInChain(This,
2462 /* Save new header information.
2464 StorageImpl_SaveFileHeader(This);
2469 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2471 if (depotBlockIndexPos == BLOCK_UNUSED)
2474 * Grow the extended depot.
2476 ULONG extIndex = BLOCK_UNUSED;
2477 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2478 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2480 if (extBlockOffset == 0)
2482 /* We need an extended block.
2484 extIndex = Storage32Impl_AddExtBlockDepot(This);
2485 This->extBigBlockDepotCount++;
2486 depotBlockIndexPos = extIndex + 1;
2489 depotBlockIndexPos = depotIndex * blocksPerDepot;
2492 * Add a block depot and mark it in the extended block.
2494 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2495 This->bigBlockDepotCount++;
2496 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2498 /* Flag the block depot.
2500 StorageImpl_SetNextBlockInChain(This,
2504 /* If necessary, flag the extended depot block.
2506 if (extIndex != BLOCK_UNUSED)
2507 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2509 /* Save header information.
2511 StorageImpl_SaveFileHeader(This);
2515 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2517 if (depotBuffer != 0)
2519 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2520 ( nextBlockIndex != BLOCK_UNUSED))
2522 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2524 if (nextBlockIndex == BLOCK_UNUSED)
2526 freeBlock = (depotIndex * blocksPerDepot) +
2527 (depotBlockOffset/sizeof(ULONG));
2530 depotBlockOffset += sizeof(ULONG);
2533 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2537 depotBlockOffset = 0;
2540 This->prevFreeBlock = freeBlock;
2545 /******************************************************************************
2546 * Storage32Impl_AddBlockDepot
2548 * This will create a depot block, essentially it is a block initialized
2551 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2555 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2558 * Initialize blocks as free
2560 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2562 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2565 /******************************************************************************
2566 * Storage32Impl_GetExtDepotBlock
2568 * Returns the index of the block that corresponds to the specified depot
2569 * index. This method is only for depot indexes equal or greater than
2570 * COUNT_BBDEPOTINHEADER.
2572 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2574 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2575 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2576 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2577 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2578 ULONG blockIndex = BLOCK_UNUSED;
2579 ULONG extBlockIndex = This->extBigBlockDepotStart;
2581 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2583 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2584 return BLOCK_UNUSED;
2586 while (extBlockCount > 0)
2588 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2592 if (extBlockIndex != BLOCK_UNUSED)
2596 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2598 if (depotBuffer != 0)
2600 StorageUtl_ReadDWord(depotBuffer,
2601 extBlockOffset * sizeof(ULONG),
2604 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2611 /******************************************************************************
2612 * Storage32Impl_SetExtDepotBlock
2614 * Associates the specified block index to the specified depot index.
2615 * This method is only for depot indexes equal or greater than
2616 * COUNT_BBDEPOTINHEADER.
2618 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2622 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2623 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2624 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2625 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2626 ULONG extBlockIndex = This->extBigBlockDepotStart;
2628 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2630 while (extBlockCount > 0)
2632 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2636 if (extBlockIndex != BLOCK_UNUSED)
2640 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2642 if (depotBuffer != 0)
2644 StorageUtl_WriteDWord(depotBuffer,
2645 extBlockOffset * sizeof(ULONG),
2648 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2653 /******************************************************************************
2654 * Storage32Impl_AddExtBlockDepot
2656 * Creates an extended depot block.
2658 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2660 ULONG numExtBlocks = This->extBigBlockDepotCount;
2661 ULONG nextExtBlock = This->extBigBlockDepotStart;
2662 BYTE* depotBuffer = NULL;
2663 ULONG index = BLOCK_UNUSED;
2664 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2665 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2666 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2668 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2669 blocksPerDepotBlock;
2671 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2674 * The first extended block.
2676 This->extBigBlockDepotStart = index;
2682 * Follow the chain to the last one.
2684 for (i = 0; i < (numExtBlocks - 1); i++)
2686 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2690 * Add the new extended block to the chain.
2692 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2693 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2694 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2698 * Initialize this block.
2700 depotBuffer = StorageImpl_GetBigBlock(This, index);
2701 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2702 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2707 /******************************************************************************
2708 * Storage32Impl_FreeBigBlock
2710 * This method will flag the specified block as free in the big block depot.
2712 void StorageImpl_FreeBigBlock(
2716 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2718 if (blockIndex < This->prevFreeBlock)
2719 This->prevFreeBlock = blockIndex;
2722 /************************************************************************
2723 * Storage32Impl_GetNextBlockInChain
2725 * This method will retrieve the block index of the next big block in
2728 * Params: This - Pointer to the Storage object.
2729 * blockIndex - Index of the block to retrieve the chain
2731 * nextBlockIndex - receives the return value.
2733 * Returns: This method returns the index of the next block in the chain.
2734 * It will return the constants:
2735 * BLOCK_SPECIAL - If the block given was not part of a
2737 * BLOCK_END_OF_CHAIN - If the block given was the last in
2739 * BLOCK_UNUSED - If the block given was not past of a chain
2741 * BLOCK_EXTBBDEPOT - This block is part of the extended
2744 * See Windows documentation for more details on IStorage methods.
2746 HRESULT StorageImpl_GetNextBlockInChain(
2749 ULONG* nextBlockIndex)
2751 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2752 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2753 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2755 ULONG depotBlockIndexPos;
2758 *nextBlockIndex = BLOCK_SPECIAL;
2760 if(depotBlockCount >= This->bigBlockDepotCount)
2762 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2763 This->bigBlockDepotCount);
2764 return STG_E_READFAULT;
2768 * Cache the currently accessed depot block.
2770 if (depotBlockCount != This->indexBlockDepotCached)
2772 This->indexBlockDepotCached = depotBlockCount;
2774 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2776 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2781 * We have to look in the extended depot.
2783 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2786 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2789 return STG_E_READFAULT;
2791 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2793 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2794 This->blockDepotCached[index] = *nextBlockIndex;
2796 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2799 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2804 /******************************************************************************
2805 * Storage32Impl_GetNextExtendedBlock
2807 * Given an extended block this method will return the next extended block.
2810 * The last ULONG of an extended block is the block index of the next
2811 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2815 * - The index of the next extended block
2816 * - BLOCK_UNUSED: there is no next extended block.
2817 * - Any other return values denotes failure.
2819 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2821 ULONG nextBlockIndex = BLOCK_SPECIAL;
2822 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2825 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2829 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2831 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2834 return nextBlockIndex;
2837 /******************************************************************************
2838 * Storage32Impl_SetNextBlockInChain
2840 * This method will write the index of the specified block's next block
2841 * in the big block depot.
2843 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2846 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2847 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2848 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2851 void StorageImpl_SetNextBlockInChain(
2856 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2857 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2858 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2859 ULONG depotBlockIndexPos;
2862 assert(depotBlockCount < This->bigBlockDepotCount);
2863 assert(blockIndex != nextBlock);
2865 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2867 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2872 * We have to look in the extended depot.
2874 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2877 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2881 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2882 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2886 * Update the cached block depot, if necessary.
2888 if (depotBlockCount == This->indexBlockDepotCached)
2890 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2894 /******************************************************************************
2895 * Storage32Impl_LoadFileHeader
2897 * This method will read in the file header, i.e. big block index -1.
2899 HRESULT StorageImpl_LoadFileHeader(
2902 HRESULT hr = STG_E_FILENOTFOUND;
2903 void* headerBigBlock = NULL;
2907 * Get a pointer to the big block of data containing the header.
2909 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2912 * Extract the information from the header.
2914 if (headerBigBlock!=0)
2917 * Check for the "magic number" signature and return an error if it is not
2920 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2922 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2923 return STG_E_OLDFORMAT;
2926 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2928 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2929 return STG_E_INVALIDHEADER;
2932 StorageUtl_ReadWord(
2934 OFFSET_BIGBLOCKSIZEBITS,
2935 &This->bigBlockSizeBits);
2937 StorageUtl_ReadWord(
2939 OFFSET_SMALLBLOCKSIZEBITS,
2940 &This->smallBlockSizeBits);
2942 StorageUtl_ReadDWord(
2944 OFFSET_BBDEPOTCOUNT,
2945 &This->bigBlockDepotCount);
2947 StorageUtl_ReadDWord(
2949 OFFSET_ROOTSTARTBLOCK,
2950 &This->rootStartBlock);
2952 StorageUtl_ReadDWord(
2954 OFFSET_SBDEPOTSTART,
2955 &This->smallBlockDepotStart);
2957 StorageUtl_ReadDWord(
2959 OFFSET_EXTBBDEPOTSTART,
2960 &This->extBigBlockDepotStart);
2962 StorageUtl_ReadDWord(
2964 OFFSET_EXTBBDEPOTCOUNT,
2965 &This->extBigBlockDepotCount);
2967 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2969 StorageUtl_ReadDWord(
2971 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2972 &(This->bigBlockDepotStart[index]));
2976 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2980 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2981 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2985 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2986 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2990 * Right now, the code is making some assumptions about the size of the
2991 * blocks, just make sure they are what we're expecting.
2993 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2994 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2996 WARN("Broken OLE storage file\n");
2997 hr = STG_E_INVALIDHEADER;
3003 * Release the block.
3005 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3011 /******************************************************************************
3012 * Storage32Impl_SaveFileHeader
3014 * This method will save to the file the header, i.e. big block -1.
3016 void StorageImpl_SaveFileHeader(
3019 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3024 * Get a pointer to the big block of data containing the header.
3026 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3029 * If the block read failed, the file is probably new.
3034 * Initialize for all unknown fields.
3036 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3039 * Initialize the magic number.
3041 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3044 * And a bunch of things we don't know what they mean
3046 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3047 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3048 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3049 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3053 * Write the information to the header.
3055 StorageUtl_WriteWord(
3057 OFFSET_BIGBLOCKSIZEBITS,
3058 This->bigBlockSizeBits);
3060 StorageUtl_WriteWord(
3062 OFFSET_SMALLBLOCKSIZEBITS,
3063 This->smallBlockSizeBits);
3065 StorageUtl_WriteDWord(
3067 OFFSET_BBDEPOTCOUNT,
3068 This->bigBlockDepotCount);
3070 StorageUtl_WriteDWord(
3072 OFFSET_ROOTSTARTBLOCK,
3073 This->rootStartBlock);
3075 StorageUtl_WriteDWord(
3077 OFFSET_SBDEPOTSTART,
3078 This->smallBlockDepotStart);
3080 StorageUtl_WriteDWord(
3082 OFFSET_SBDEPOTCOUNT,
3083 This->smallBlockDepotChain ?
3084 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3086 StorageUtl_WriteDWord(
3088 OFFSET_EXTBBDEPOTSTART,
3089 This->extBigBlockDepotStart);
3091 StorageUtl_WriteDWord(
3093 OFFSET_EXTBBDEPOTCOUNT,
3094 This->extBigBlockDepotCount);
3096 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3098 StorageUtl_WriteDWord(
3100 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3101 (This->bigBlockDepotStart[index]));
3105 * Write the big block back to the file.
3107 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3110 /******************************************************************************
3111 * Storage32Impl_ReadProperty
3113 * This method will read the specified property from the property chain.
3115 BOOL StorageImpl_ReadProperty(
3118 StgProperty* buffer)
3120 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3121 ULARGE_INTEGER offsetInPropSet;
3122 BOOL readSuccessful;
3125 offsetInPropSet.u.HighPart = 0;
3126 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3128 readSuccessful = BlockChainStream_ReadAt(
3129 This->rootBlockChain,
3137 /* replace the name of root entry (often "Root Entry") by the file name */
3138 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3139 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3141 memset(buffer->name, 0, sizeof(buffer->name));
3145 PROPERTY_NAME_BUFFER_LEN );
3146 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3148 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3150 StorageUtl_ReadWord(
3152 OFFSET_PS_NAMELENGTH,
3153 &buffer->sizeOfNameString);
3155 StorageUtl_ReadDWord(
3157 OFFSET_PS_PREVIOUSPROP,
3158 &buffer->previousProperty);
3160 StorageUtl_ReadDWord(
3163 &buffer->nextProperty);
3165 StorageUtl_ReadDWord(
3168 &buffer->dirProperty);
3170 StorageUtl_ReadGUID(
3173 &buffer->propertyUniqueID);
3175 StorageUtl_ReadDWord(
3178 &buffer->timeStampS1);
3180 StorageUtl_ReadDWord(
3183 &buffer->timeStampD1);
3185 StorageUtl_ReadDWord(
3188 &buffer->timeStampS2);
3190 StorageUtl_ReadDWord(
3193 &buffer->timeStampD2);
3195 StorageUtl_ReadDWord(
3197 OFFSET_PS_STARTBLOCK,
3198 &buffer->startingBlock);
3200 StorageUtl_ReadDWord(
3203 &buffer->size.u.LowPart);
3205 buffer->size.u.HighPart = 0;
3208 return readSuccessful;
3211 /*********************************************************************
3212 * Write the specified property into the property chain
3214 BOOL StorageImpl_WriteProperty(
3217 StgProperty* buffer)
3219 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3220 ULARGE_INTEGER offsetInPropSet;
3221 BOOL writeSuccessful;
3224 offsetInPropSet.u.HighPart = 0;
3225 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3227 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3230 currentProperty + OFFSET_PS_NAME,
3232 PROPERTY_NAME_BUFFER_LEN );
3234 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3236 StorageUtl_WriteWord(
3238 OFFSET_PS_NAMELENGTH,
3239 buffer->sizeOfNameString);
3241 StorageUtl_WriteDWord(
3243 OFFSET_PS_PREVIOUSPROP,
3244 buffer->previousProperty);
3246 StorageUtl_WriteDWord(
3249 buffer->nextProperty);
3251 StorageUtl_WriteDWord(
3254 buffer->dirProperty);
3256 StorageUtl_WriteGUID(
3259 &buffer->propertyUniqueID);
3261 StorageUtl_WriteDWord(
3264 buffer->timeStampS1);
3266 StorageUtl_WriteDWord(
3269 buffer->timeStampD1);
3271 StorageUtl_WriteDWord(
3274 buffer->timeStampS2);
3276 StorageUtl_WriteDWord(
3279 buffer->timeStampD2);
3281 StorageUtl_WriteDWord(
3283 OFFSET_PS_STARTBLOCK,
3284 buffer->startingBlock);
3286 StorageUtl_WriteDWord(
3289 buffer->size.u.LowPart);
3291 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3296 return writeSuccessful;
3299 BOOL StorageImpl_ReadBigBlock(
3304 void* bigBlockBuffer;
3306 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3308 if (bigBlockBuffer!=0)
3310 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3312 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3320 BOOL StorageImpl_WriteBigBlock(
3325 void* bigBlockBuffer;
3327 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3329 if (bigBlockBuffer!=0)
3331 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3333 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3341 void* StorageImpl_GetROBigBlock(
3345 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3348 void* StorageImpl_GetBigBlock(
3352 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3355 void StorageImpl_ReleaseBigBlock(
3359 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3362 /******************************************************************************
3363 * Storage32Impl_SmallBlocksToBigBlocks
3365 * This method will convert a small block chain to a big block chain.
3366 * The small block chain will be destroyed.
3368 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3370 SmallBlockChainStream** ppsbChain)
3372 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3373 ULARGE_INTEGER size, offset;
3374 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3375 ULONG propertyIndex;
3376 BOOL successRead, successWrite;
3377 StgProperty chainProperty;
3379 BlockChainStream *bbTempChain = NULL;
3380 BlockChainStream *bigBlockChain = NULL;
3383 * Create a temporary big block chain that doesn't have
3384 * an associated property. This temporary chain will be
3385 * used to copy data from small blocks to big blocks.
3387 bbTempChain = BlockChainStream_Construct(This,
3390 if(!bbTempChain) return NULL;
3392 * Grow the big block chain.
3394 size = SmallBlockChainStream_GetSize(*ppsbChain);
3395 BlockChainStream_SetSize(bbTempChain, size);
3398 * Copy the contents of the small block chain to the big block chain
3399 * by small block size increments.
3401 offset.u.LowPart = 0;
3402 offset.u.HighPart = 0;
3406 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3409 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3411 DEF_SMALL_BLOCK_SIZE,
3414 cbTotalRead += cbRead;
3416 successWrite = BlockChainStream_WriteAt(bbTempChain,
3421 cbTotalWritten += cbWritten;
3423 offset.u.LowPart += This->smallBlockSize;
3425 } while (successRead && successWrite);
3426 HeapFree(GetProcessHeap(),0,buffer);
3428 assert(cbTotalRead == cbTotalWritten);
3431 * Destroy the small block chain.
3433 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3434 size.u.HighPart = 0;
3436 SmallBlockChainStream_SetSize(*ppsbChain, size);
3437 SmallBlockChainStream_Destroy(*ppsbChain);
3441 * Change the property information. This chain is now a big block chain
3442 * and it doesn't reside in the small blocks chain anymore.
3444 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3446 chainProperty.startingBlock = bbHeadOfChain;
3448 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3451 * Destroy the temporary propertyless big block chain.
3452 * Create a new big block chain associated with this property.
3454 BlockChainStream_Destroy(bbTempChain);
3455 bigBlockChain = BlockChainStream_Construct(This,
3459 return bigBlockChain;
3462 /******************************************************************************
3463 ** Storage32InternalImpl implementation
3466 StorageInternalImpl* StorageInternalImpl_Construct(
3467 StorageImpl* ancestorStorage,
3468 ULONG rootPropertyIndex)
3470 StorageInternalImpl* newStorage;
3473 * Allocate space for the new storage object
3475 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3479 memset(newStorage, 0, sizeof(StorageInternalImpl));
3482 * Initialize the virtual function table.
3484 newStorage->lpVtbl = &Storage32InternalImpl_Vtbl;
3485 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3488 * Keep the ancestor storage pointer and nail a reference to it.
3490 newStorage->ancestorStorage = ancestorStorage;
3491 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3494 * Keep the index of the root property set for this storage,
3496 newStorage->rootPropertySetIndex = rootPropertyIndex;
3504 void StorageInternalImpl_Destroy(
3505 StorageInternalImpl* This)
3507 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3508 HeapFree(GetProcessHeap(), 0, This);
3511 /******************************************************************************
3513 ** Storage32InternalImpl_Commit
3515 ** The non-root storages cannot be opened in transacted mode thus this function
3518 HRESULT WINAPI StorageInternalImpl_Commit(
3520 DWORD grfCommitFlags) /* [in] */
3525 /******************************************************************************
3527 ** Storage32InternalImpl_Revert
3529 ** The non-root storages cannot be opened in transacted mode thus this function
3532 HRESULT WINAPI StorageInternalImpl_Revert(
3538 /******************************************************************************
3539 ** IEnumSTATSTGImpl implementation
3542 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3543 StorageImpl* parentStorage,
3544 ULONG firstPropertyNode)
3546 IEnumSTATSTGImpl* newEnumeration;
3548 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3550 if (newEnumeration!=0)
3553 * Set-up the virtual function table and reference count.
3555 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3556 newEnumeration->ref = 0;
3559 * We want to nail-down the reference to the storage in case the
3560 * enumeration out-lives the storage in the client application.
3562 newEnumeration->parentStorage = parentStorage;
3563 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3565 newEnumeration->firstPropertyNode = firstPropertyNode;
3568 * Initialize the search stack
3570 newEnumeration->stackSize = 0;
3571 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3572 newEnumeration->stackToVisit =
3573 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3576 * Make sure the current node of the iterator is the first one.
3578 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3581 return newEnumeration;
3584 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3586 IStorage_Release((IStorage*)This->parentStorage);
3587 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3588 HeapFree(GetProcessHeap(), 0, This);
3591 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3592 IEnumSTATSTG* iface,
3596 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3599 * Perform a sanity check on the parameters.
3602 return E_INVALIDARG;
3605 * Initialize the return parameter.
3610 * Compare the riid with the interface IDs implemented by this object.
3612 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3614 *ppvObject = (IEnumSTATSTG*)This;
3616 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3618 *ppvObject = (IEnumSTATSTG*)This;
3622 * Check that we obtained an interface.
3624 if ((*ppvObject)==0)
3625 return E_NOINTERFACE;
3628 * Query Interface always increases the reference count by one when it is
3631 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3636 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3637 IEnumSTATSTG* iface)
3639 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3645 ULONG WINAPI IEnumSTATSTGImpl_Release(
3646 IEnumSTATSTG* iface)
3648 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3656 * If the reference count goes down to 0, perform suicide.
3660 IEnumSTATSTGImpl_Destroy(This);
3666 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3667 IEnumSTATSTG* iface,
3670 ULONG* pceltFetched)
3672 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3674 StgProperty currentProperty;
3675 STATSTG* currentReturnStruct = rgelt;
3676 ULONG objectFetched = 0;
3677 ULONG currentSearchNode;
3680 * Perform a sanity check on the parameters.
3682 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3683 return E_INVALIDARG;
3686 * To avoid the special case, get another pointer to a ULONG value if
3687 * the caller didn't supply one.
3689 if (pceltFetched==0)
3690 pceltFetched = &objectFetched;
3693 * Start the iteration, we will iterate until we hit the end of the
3694 * linked list or until we hit the number of items to iterate through
3699 * Start with the node at the top of the stack.
3701 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3703 while ( ( *pceltFetched < celt) &&
3704 ( currentSearchNode!=PROPERTY_NULL) )
3707 * Remove the top node from the stack
3709 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3712 * Read the property from the storage.
3714 StorageImpl_ReadProperty(This->parentStorage,
3719 * Copy the information to the return buffer.
3721 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3726 * Step to the next item in the iteration
3729 currentReturnStruct++;
3732 * Push the next search node in the search stack.
3734 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3737 * continue the iteration.
3739 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3742 if (*pceltFetched == celt)
3749 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3750 IEnumSTATSTG* iface,
3753 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3755 StgProperty currentProperty;
3756 ULONG objectFetched = 0;
3757 ULONG currentSearchNode;
3760 * Start with the node at the top of the stack.
3762 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3764 while ( (objectFetched < celt) &&
3765 (currentSearchNode!=PROPERTY_NULL) )
3768 * Remove the top node from the stack
3770 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3773 * Read the property from the storage.
3775 StorageImpl_ReadProperty(This->parentStorage,
3780 * Step to the next item in the iteration
3785 * Push the next search node in the search stack.
3787 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3790 * continue the iteration.
3792 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3795 if (objectFetched == celt)
3801 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3802 IEnumSTATSTG* iface)
3804 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3806 StgProperty rootProperty;
3807 BOOL readSuccessful;
3810 * Re-initialize the search stack to an empty stack
3812 This->stackSize = 0;
3815 * Read the root property from the storage.
3817 readSuccessful = StorageImpl_ReadProperty(
3818 This->parentStorage,
3819 This->firstPropertyNode,
3824 assert(rootProperty.sizeOfNameString!=0);
3827 * Push the search node in the search stack.
3829 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3835 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3836 IEnumSTATSTG* iface,
3837 IEnumSTATSTG** ppenum)
3839 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3841 IEnumSTATSTGImpl* newClone;
3844 * Perform a sanity check on the parameters.
3847 return E_INVALIDARG;
3849 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3850 This->firstPropertyNode);
3854 * The new clone enumeration must point to the same current node as
3857 newClone->stackSize = This->stackSize ;
3858 newClone->stackMaxSize = This->stackMaxSize ;
3859 newClone->stackToVisit =
3860 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3863 newClone->stackToVisit,
3865 sizeof(ULONG) * newClone->stackSize);
3867 *ppenum = (IEnumSTATSTG*)newClone;
3870 * Don't forget to nail down a reference to the clone before
3873 IEnumSTATSTGImpl_AddRef(*ppenum);
3878 INT IEnumSTATSTGImpl_FindParentProperty(
3879 IEnumSTATSTGImpl *This,
3880 ULONG childProperty,
3881 StgProperty *currentProperty,
3884 ULONG currentSearchNode;
3888 * To avoid the special case, get another pointer to a ULONG value if
3889 * the caller didn't supply one.
3892 thisNodeId = &foundNode;
3895 * Start with the node at the top of the stack.
3897 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3900 while (currentSearchNode!=PROPERTY_NULL)
3903 * Store the current node in the returned parameters
3905 *thisNodeId = currentSearchNode;
3908 * Remove the top node from the stack
3910 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3913 * Read the property from the storage.
3915 StorageImpl_ReadProperty(
3916 This->parentStorage,
3920 if (currentProperty->previousProperty == childProperty)
3921 return PROPERTY_RELATION_PREVIOUS;
3923 else if (currentProperty->nextProperty == childProperty)
3924 return PROPERTY_RELATION_NEXT;
3926 else if (currentProperty->dirProperty == childProperty)
3927 return PROPERTY_RELATION_DIR;
3930 * Push the next search node in the search stack.
3932 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3935 * continue the iteration.
3937 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3940 return PROPERTY_NULL;
3943 ULONG IEnumSTATSTGImpl_FindProperty(
3944 IEnumSTATSTGImpl* This,
3945 const OLECHAR* lpszPropName,
3946 StgProperty* currentProperty)
3948 ULONG currentSearchNode;
3951 * Start with the node at the top of the stack.
3953 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3955 while (currentSearchNode!=PROPERTY_NULL)
3958 * Remove the top node from the stack
3960 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3963 * Read the property from the storage.
3965 StorageImpl_ReadProperty(This->parentStorage,
3969 if ( propertyNameCmp(
3970 (OLECHAR*)currentProperty->name,
3971 (OLECHAR*)lpszPropName) == 0)
3972 return currentSearchNode;
3975 * Push the next search node in the search stack.
3977 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3980 * continue the iteration.
3982 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3985 return PROPERTY_NULL;
3988 void IEnumSTATSTGImpl_PushSearchNode(
3989 IEnumSTATSTGImpl* This,
3992 StgProperty rootProperty;
3993 BOOL readSuccessful;
3996 * First, make sure we're not trying to push an unexisting node.
3998 if (nodeToPush==PROPERTY_NULL)
4002 * First push the node to the stack
4004 if (This->stackSize == This->stackMaxSize)
4006 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4008 This->stackToVisit = HeapReAlloc(
4012 sizeof(ULONG) * This->stackMaxSize);
4015 This->stackToVisit[This->stackSize] = nodeToPush;
4019 * Read the root property from the storage.
4021 readSuccessful = StorageImpl_ReadProperty(
4022 This->parentStorage,
4028 assert(rootProperty.sizeOfNameString!=0);
4031 * Push the previous search node in the search stack.
4033 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4037 ULONG IEnumSTATSTGImpl_PopSearchNode(
4038 IEnumSTATSTGImpl* This,
4043 if (This->stackSize == 0)
4044 return PROPERTY_NULL;
4046 topNode = This->stackToVisit[This->stackSize-1];
4054 /******************************************************************************
4055 ** StorageUtl implementation
4058 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4060 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4063 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4065 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4068 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4070 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4073 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4075 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4078 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4080 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4081 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4082 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4084 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4087 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4089 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4090 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4091 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4093 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4096 void StorageUtl_CopyPropertyToSTATSTG(
4097 STATSTG* destination,
4098 StgProperty* source,
4102 * The copy of the string occurs only when the flag is not set
4104 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4105 (source->name == NULL) ||
4106 (source->name[0] == 0) )
4108 destination->pwcsName = 0;
4112 destination->pwcsName =
4113 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4115 strcpyW((LPWSTR)destination->pwcsName, source->name);
4118 switch (source->propertyType)
4120 case PROPTYPE_STORAGE:
4122 destination->type = STGTY_STORAGE;
4124 case PROPTYPE_STREAM:
4125 destination->type = STGTY_STREAM;
4128 destination->type = STGTY_STREAM;
4132 destination->cbSize = source->size;
4134 currentReturnStruct->mtime = {0}; TODO
4135 currentReturnStruct->ctime = {0};
4136 currentReturnStruct->atime = {0};
4138 destination->grfMode = 0;
4139 destination->grfLocksSupported = 0;
4140 destination->clsid = source->propertyUniqueID;
4141 destination->grfStateBits = 0;
4142 destination->reserved = 0;
4145 /******************************************************************************
4146 ** BlockChainStream implementation
4149 BlockChainStream* BlockChainStream_Construct(
4150 StorageImpl* parentStorage,
4151 ULONG* headOfStreamPlaceHolder,
4152 ULONG propertyIndex)
4154 BlockChainStream* newStream;
4157 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4159 newStream->parentStorage = parentStorage;
4160 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4161 newStream->ownerPropertyIndex = propertyIndex;
4162 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4163 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4164 newStream->numBlocks = 0;
4166 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4168 while (blockIndex != BLOCK_END_OF_CHAIN)
4170 newStream->numBlocks++;
4171 newStream->tailIndex = blockIndex;
4173 if(FAILED(StorageImpl_GetNextBlockInChain(
4178 HeapFree(GetProcessHeap(), 0, newStream);
4186 void BlockChainStream_Destroy(BlockChainStream* This)
4188 HeapFree(GetProcessHeap(), 0, This);
4191 /******************************************************************************
4192 * BlockChainStream_GetHeadOfChain
4194 * Returns the head of this stream chain.
4195 * Some special chains don't have properties, their heads are kept in
4196 * This->headOfStreamPlaceHolder.
4199 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4201 StgProperty chainProperty;
4202 BOOL readSuccessful;
4204 if (This->headOfStreamPlaceHolder != 0)
4205 return *(This->headOfStreamPlaceHolder);
4207 if (This->ownerPropertyIndex != PROPERTY_NULL)
4209 readSuccessful = StorageImpl_ReadProperty(
4210 This->parentStorage,
4211 This->ownerPropertyIndex,
4216 return chainProperty.startingBlock;
4220 return BLOCK_END_OF_CHAIN;
4223 /******************************************************************************
4224 * BlockChainStream_GetCount
4226 * Returns the number of blocks that comprises this chain.
4227 * This is not the size of the stream as the last block may not be full!
4230 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4235 blockIndex = BlockChainStream_GetHeadOfChain(This);
4237 while (blockIndex != BLOCK_END_OF_CHAIN)
4241 if(FAILED(StorageImpl_GetNextBlockInChain(
4242 This->parentStorage,
4251 /******************************************************************************
4252 * BlockChainStream_ReadAt
4254 * Reads a specified number of bytes from this chain at the specified offset.
4255 * bytesRead may be NULL.
4256 * Failure will be returned if the specified number of bytes has not been read.
4258 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4259 ULARGE_INTEGER offset,
4264 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4265 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4266 ULONG bytesToReadInBuffer;
4269 BYTE* bigBlockBuffer;
4272 * Find the first block in the stream that contains part of the buffer.
4274 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4275 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4276 (blockNoInSequence < This->lastBlockNoInSequence) )
4278 blockIndex = BlockChainStream_GetHeadOfChain(This);
4279 This->lastBlockNoInSequence = blockNoInSequence;
4283 ULONG temp = blockNoInSequence;
4285 blockIndex = This->lastBlockNoInSequenceIndex;
4286 blockNoInSequence -= This->lastBlockNoInSequence;
4287 This->lastBlockNoInSequence = temp;
4290 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4292 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4294 blockNoInSequence--;
4297 This->lastBlockNoInSequenceIndex = blockIndex;
4300 * Start reading the buffer.
4303 bufferWalker = buffer;
4305 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4308 * Calculate how many bytes we can copy from this big block.
4310 bytesToReadInBuffer =
4311 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4314 * Copy those bytes to the buffer
4317 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4319 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4321 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4324 * Step to the next big block.
4326 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4329 bufferWalker += bytesToReadInBuffer;
4330 size -= bytesToReadInBuffer;
4331 *bytesRead += bytesToReadInBuffer;
4332 offsetInBlock = 0; /* There is no offset on the next block */
4339 /******************************************************************************
4340 * BlockChainStream_WriteAt
4342 * Writes the specified number of bytes to this chain at the specified offset.
4343 * bytesWritten may be NULL.
4344 * Will fail if not all specified number of bytes have been written.
4346 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4347 ULARGE_INTEGER offset,
4350 ULONG* bytesWritten)
4352 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4353 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4357 BYTE* bigBlockBuffer;
4360 * Find the first block in the stream that contains part of the buffer.
4362 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4363 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4364 (blockNoInSequence < This->lastBlockNoInSequence) )
4366 blockIndex = BlockChainStream_GetHeadOfChain(This);
4367 This->lastBlockNoInSequence = blockNoInSequence;
4371 ULONG temp = blockNoInSequence;
4373 blockIndex = This->lastBlockNoInSequenceIndex;
4374 blockNoInSequence -= This->lastBlockNoInSequence;
4375 This->lastBlockNoInSequence = temp;
4378 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4380 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4383 blockNoInSequence--;
4386 This->lastBlockNoInSequenceIndex = blockIndex;
4389 * Here, I'm casting away the constness on the buffer variable
4390 * This is OK since we don't intend to modify that buffer.
4393 bufferWalker = (BYTE*)buffer;
4395 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4398 * Calculate how many bytes we can copy from this big block.
4401 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4404 * Copy those bytes to the buffer
4406 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4408 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4410 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4413 * Step to the next big block.
4415 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4418 bufferWalker += bytesToWrite;
4419 size -= bytesToWrite;
4420 *bytesWritten += bytesToWrite;
4421 offsetInBlock = 0; /* There is no offset on the next block */
4427 /******************************************************************************
4428 * BlockChainStream_Shrink
4430 * Shrinks this chain in the big block depot.
4432 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4433 ULARGE_INTEGER newSize)
4435 ULONG blockIndex, extraBlock;
4440 * Reset the last accessed block cache.
4442 This->lastBlockNoInSequence = 0xFFFFFFFF;
4443 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4446 * Figure out how many blocks are needed to contain the new size
4448 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4450 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4453 blockIndex = BlockChainStream_GetHeadOfChain(This);
4456 * Go to the new end of chain
4458 while (count < numBlocks)
4460 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4466 /* Get the next block before marking the new end */
4467 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4471 /* Mark the new end of chain */
4472 StorageImpl_SetNextBlockInChain(
4473 This->parentStorage,
4475 BLOCK_END_OF_CHAIN);
4477 This->tailIndex = blockIndex;
4478 This->numBlocks = numBlocks;
4481 * Mark the extra blocks as free
4483 while (extraBlock != BLOCK_END_OF_CHAIN)
4485 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4488 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4489 extraBlock = blockIndex;
4495 /******************************************************************************
4496 * BlockChainStream_Enlarge
4498 * Grows this chain in the big block depot.
4500 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4501 ULARGE_INTEGER newSize)
4503 ULONG blockIndex, currentBlock;
4505 ULONG oldNumBlocks = 0;
4507 blockIndex = BlockChainStream_GetHeadOfChain(This);
4510 * Empty chain. Create the head.
4512 if (blockIndex == BLOCK_END_OF_CHAIN)
4514 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4515 StorageImpl_SetNextBlockInChain(This->parentStorage,
4517 BLOCK_END_OF_CHAIN);
4519 if (This->headOfStreamPlaceHolder != 0)
4521 *(This->headOfStreamPlaceHolder) = blockIndex;
4525 StgProperty chainProp;
4526 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4528 StorageImpl_ReadProperty(
4529 This->parentStorage,
4530 This->ownerPropertyIndex,
4533 chainProp.startingBlock = blockIndex;
4535 StorageImpl_WriteProperty(
4536 This->parentStorage,
4537 This->ownerPropertyIndex,
4541 This->tailIndex = blockIndex;
4542 This->numBlocks = 1;
4546 * Figure out how many blocks are needed to contain this stream
4548 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4550 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4554 * Go to the current end of chain
4556 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4558 currentBlock = blockIndex;
4560 while (blockIndex != BLOCK_END_OF_CHAIN)
4563 currentBlock = blockIndex;
4565 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4570 This->tailIndex = currentBlock;
4573 currentBlock = This->tailIndex;
4574 oldNumBlocks = This->numBlocks;
4577 * Add new blocks to the chain
4579 if (oldNumBlocks < newNumBlocks)
4581 while (oldNumBlocks < newNumBlocks)
4583 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4585 StorageImpl_SetNextBlockInChain(
4586 This->parentStorage,
4590 StorageImpl_SetNextBlockInChain(
4591 This->parentStorage,
4593 BLOCK_END_OF_CHAIN);
4595 currentBlock = blockIndex;
4599 This->tailIndex = blockIndex;
4600 This->numBlocks = newNumBlocks;
4606 /******************************************************************************
4607 * BlockChainStream_SetSize
4609 * Sets the size of this stream. The big block depot will be updated.
4610 * The file will grow if we grow the chain.
4612 * TODO: Free the actual blocks in the file when we shrink the chain.
4613 * Currently, the blocks are still in the file. So the file size
4614 * doesn't shrink even if we shrink streams.
4616 BOOL BlockChainStream_SetSize(
4617 BlockChainStream* This,
4618 ULARGE_INTEGER newSize)
4620 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4622 if (newSize.u.LowPart == size.u.LowPart)
4625 if (newSize.u.LowPart < size.u.LowPart)
4627 BlockChainStream_Shrink(This, newSize);
4631 ULARGE_INTEGER fileSize =
4632 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4634 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4637 * Make sure the file stays a multiple of blocksize
4639 if ((diff % This->parentStorage->bigBlockSize) != 0)
4640 diff += (This->parentStorage->bigBlockSize -
4641 (diff % This->parentStorage->bigBlockSize) );
4643 fileSize.u.LowPart += diff;
4644 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4646 BlockChainStream_Enlarge(This, newSize);
4652 /******************************************************************************
4653 * BlockChainStream_GetSize
4655 * Returns the size of this chain.
4656 * Will return the block count if this chain doesn't have a property.
4658 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4660 StgProperty chainProperty;
4662 if(This->headOfStreamPlaceHolder == NULL)
4665 * This chain is a data stream read the property and return
4666 * the appropriate size
4668 StorageImpl_ReadProperty(
4669 This->parentStorage,
4670 This->ownerPropertyIndex,
4673 return chainProperty.size;
4678 * this chain is a chain that does not have a property, figure out the
4679 * size by making the product number of used blocks times the
4682 ULARGE_INTEGER result;
4683 result.u.HighPart = 0;
4686 BlockChainStream_GetCount(This) *
4687 This->parentStorage->bigBlockSize;
4693 /******************************************************************************
4694 ** SmallBlockChainStream implementation
4697 SmallBlockChainStream* SmallBlockChainStream_Construct(
4698 StorageImpl* parentStorage,
4699 ULONG propertyIndex)
4701 SmallBlockChainStream* newStream;
4703 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4705 newStream->parentStorage = parentStorage;
4706 newStream->ownerPropertyIndex = propertyIndex;
4711 void SmallBlockChainStream_Destroy(
4712 SmallBlockChainStream* This)
4714 HeapFree(GetProcessHeap(), 0, This);
4717 /******************************************************************************
4718 * SmallBlockChainStream_GetHeadOfChain
4720 * Returns the head of this chain of small blocks.
4722 ULONG SmallBlockChainStream_GetHeadOfChain(
4723 SmallBlockChainStream* This)
4725 StgProperty chainProperty;
4726 BOOL readSuccessful;
4728 if (This->ownerPropertyIndex)
4730 readSuccessful = StorageImpl_ReadProperty(
4731 This->parentStorage,
4732 This->ownerPropertyIndex,
4737 return chainProperty.startingBlock;
4742 return BLOCK_END_OF_CHAIN;
4745 /******************************************************************************
4746 * SmallBlockChainStream_GetNextBlockInChain
4748 * Returns the index of the next small block in this chain.
4751 * - BLOCK_END_OF_CHAIN: end of this chain
4752 * - BLOCK_UNUSED: small block 'blockIndex' is free
4754 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4755 SmallBlockChainStream* This,
4757 ULONG* nextBlockInChain)
4759 ULARGE_INTEGER offsetOfBlockInDepot;
4764 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4766 offsetOfBlockInDepot.u.HighPart = 0;
4767 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4770 * Read those bytes in the buffer from the small block file.
4772 success = BlockChainStream_ReadAt(
4773 This->parentStorage->smallBlockDepotChain,
4774 offsetOfBlockInDepot,
4781 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4785 return STG_E_READFAULT;
4788 /******************************************************************************
4789 * SmallBlockChainStream_SetNextBlockInChain
4791 * Writes the index of the next block of the specified block in the small
4793 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4794 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4796 void SmallBlockChainStream_SetNextBlockInChain(
4797 SmallBlockChainStream* This,
4801 ULARGE_INTEGER offsetOfBlockInDepot;
4805 offsetOfBlockInDepot.u.HighPart = 0;
4806 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4808 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4811 * Read those bytes in the buffer from the small block file.
4813 BlockChainStream_WriteAt(
4814 This->parentStorage->smallBlockDepotChain,
4815 offsetOfBlockInDepot,
4821 /******************************************************************************
4822 * SmallBlockChainStream_FreeBlock
4824 * Flag small block 'blockIndex' as free in the small block depot.
4826 void SmallBlockChainStream_FreeBlock(
4827 SmallBlockChainStream* This,
4830 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4833 /******************************************************************************
4834 * SmallBlockChainStream_GetNextFreeBlock
4836 * Returns the index of a free small block. The small block depot will be
4837 * enlarged if necessary. The small block chain will also be enlarged if
4840 ULONG SmallBlockChainStream_GetNextFreeBlock(
4841 SmallBlockChainStream* This)
4843 ULARGE_INTEGER offsetOfBlockInDepot;
4846 ULONG blockIndex = 0;
4847 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4848 BOOL success = TRUE;
4849 ULONG smallBlocksPerBigBlock;
4851 offsetOfBlockInDepot.u.HighPart = 0;
4854 * Scan the small block depot for a free block
4856 while (nextBlockIndex != BLOCK_UNUSED)
4858 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4860 success = BlockChainStream_ReadAt(
4861 This->parentStorage->smallBlockDepotChain,
4862 offsetOfBlockInDepot,
4868 * If we run out of space for the small block depot, enlarge it
4872 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4874 if (nextBlockIndex != BLOCK_UNUSED)
4880 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4882 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4883 ULONG nextBlock, newsbdIndex;
4884 BYTE* smallBlockDepot;
4886 nextBlock = sbdIndex;
4887 while (nextBlock != BLOCK_END_OF_CHAIN)
4889 sbdIndex = nextBlock;
4890 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4893 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4894 if (sbdIndex != BLOCK_END_OF_CHAIN)
4895 StorageImpl_SetNextBlockInChain(
4896 This->parentStorage,
4900 StorageImpl_SetNextBlockInChain(
4901 This->parentStorage,
4903 BLOCK_END_OF_CHAIN);
4906 * Initialize all the small blocks to free
4909 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4911 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4912 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4917 * We have just created the small block depot.
4919 StgProperty rootProp;
4923 * Save it in the header
4925 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4926 StorageImpl_SaveFileHeader(This->parentStorage);
4929 * And allocate the first big block that will contain small blocks
4932 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4934 StorageImpl_SetNextBlockInChain(
4935 This->parentStorage,
4937 BLOCK_END_OF_CHAIN);
4939 StorageImpl_ReadProperty(
4940 This->parentStorage,
4941 This->parentStorage->rootPropertySetIndex,
4944 rootProp.startingBlock = sbStartIndex;
4945 rootProp.size.u.HighPart = 0;
4946 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
4948 StorageImpl_WriteProperty(
4949 This->parentStorage,
4950 This->parentStorage->rootPropertySetIndex,
4956 smallBlocksPerBigBlock =
4957 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4960 * Verify if we have to allocate big blocks to contain small blocks
4962 if (blockIndex % smallBlocksPerBigBlock == 0)
4964 StgProperty rootProp;
4965 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4967 StorageImpl_ReadProperty(
4968 This->parentStorage,
4969 This->parentStorage->rootPropertySetIndex,
4972 if (rootProp.size.u.LowPart <
4973 (blocksRequired * This->parentStorage->bigBlockSize))
4975 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
4977 BlockChainStream_SetSize(
4978 This->parentStorage->smallBlockRootChain,
4981 StorageImpl_WriteProperty(
4982 This->parentStorage,
4983 This->parentStorage->rootPropertySetIndex,
4991 /******************************************************************************
4992 * SmallBlockChainStream_ReadAt
4994 * Reads a specified number of bytes from this chain at the specified offset.
4995 * bytesRead may be NULL.
4996 * Failure will be returned if the specified number of bytes has not been read.
4998 BOOL SmallBlockChainStream_ReadAt(
4999 SmallBlockChainStream* This,
5000 ULARGE_INTEGER offset,
5005 ULARGE_INTEGER offsetInBigBlockFile;
5006 ULONG blockNoInSequence =
5007 offset.u.LowPart / This->parentStorage->smallBlockSize;
5009 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5010 ULONG bytesToReadInBuffer;
5012 ULONG bytesReadFromBigBlockFile;
5016 * This should never happen on a small block file.
5018 assert(offset.u.HighPart==0);
5021 * Find the first block in the stream that contains part of the buffer.
5023 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5025 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5027 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5030 blockNoInSequence--;
5034 * Start reading the buffer.
5037 bufferWalker = buffer;
5039 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5042 * Calculate how many bytes we can copy from this small block.
5044 bytesToReadInBuffer =
5045 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5048 * Calculate the offset of the small block in the small block file.
5050 offsetInBigBlockFile.u.HighPart = 0;
5051 offsetInBigBlockFile.u.LowPart =
5052 blockIndex * This->parentStorage->smallBlockSize;
5054 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5057 * Read those bytes in the buffer from the small block file.
5059 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5060 offsetInBigBlockFile,
5061 bytesToReadInBuffer,
5063 &bytesReadFromBigBlockFile);
5065 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5068 * Step to the next big block.
5070 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5072 bufferWalker += bytesToReadInBuffer;
5073 size -= bytesToReadInBuffer;
5074 *bytesRead += bytesToReadInBuffer;
5075 offsetInBlock = 0; /* There is no offset on the next block */
5081 /******************************************************************************
5082 * SmallBlockChainStream_WriteAt
5084 * Writes the specified number of bytes to this chain at the specified offset.
5085 * bytesWritten may be NULL.
5086 * Will fail if not all specified number of bytes have been written.
5088 BOOL SmallBlockChainStream_WriteAt(
5089 SmallBlockChainStream* This,
5090 ULARGE_INTEGER offset,
5093 ULONG* bytesWritten)
5095 ULARGE_INTEGER offsetInBigBlockFile;
5096 ULONG blockNoInSequence =
5097 offset.u.LowPart / This->parentStorage->smallBlockSize;
5099 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5100 ULONG bytesToWriteInBuffer;
5102 ULONG bytesWrittenFromBigBlockFile;
5106 * This should never happen on a small block file.
5108 assert(offset.u.HighPart==0);
5111 * Find the first block in the stream that contains part of the buffer.
5113 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5115 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5117 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5119 blockNoInSequence--;
5123 * Start writing the buffer.
5125 * Here, I'm casting away the constness on the buffer variable
5126 * This is OK since we don't intend to modify that buffer.
5129 bufferWalker = (BYTE*)buffer;
5130 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5133 * Calculate how many bytes we can copy to this small block.
5135 bytesToWriteInBuffer =
5136 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5139 * Calculate the offset of the small block in the small block file.
5141 offsetInBigBlockFile.u.HighPart = 0;
5142 offsetInBigBlockFile.u.LowPart =
5143 blockIndex * This->parentStorage->smallBlockSize;
5145 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5148 * Write those bytes in the buffer to the small block file.
5150 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5151 offsetInBigBlockFile,
5152 bytesToWriteInBuffer,
5154 &bytesWrittenFromBigBlockFile);
5156 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5159 * Step to the next big block.
5161 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5164 bufferWalker += bytesToWriteInBuffer;
5165 size -= bytesToWriteInBuffer;
5166 *bytesWritten += bytesToWriteInBuffer;
5167 offsetInBlock = 0; /* There is no offset on the next block */
5173 /******************************************************************************
5174 * SmallBlockChainStream_Shrink
5176 * Shrinks this chain in the small block depot.
5178 BOOL SmallBlockChainStream_Shrink(
5179 SmallBlockChainStream* This,
5180 ULARGE_INTEGER newSize)
5182 ULONG blockIndex, extraBlock;
5186 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5188 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5191 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5194 * Go to the new end of chain
5196 while (count < numBlocks)
5198 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5205 * If the count is 0, we have a special case, the head of the chain was
5210 StgProperty chainProp;
5212 StorageImpl_ReadProperty(This->parentStorage,
5213 This->ownerPropertyIndex,
5216 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5218 StorageImpl_WriteProperty(This->parentStorage,
5219 This->ownerPropertyIndex,
5223 * We start freeing the chain at the head block.
5225 extraBlock = blockIndex;
5229 /* Get the next block before marking the new end */
5230 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5234 /* Mark the new end of chain */
5235 SmallBlockChainStream_SetNextBlockInChain(
5238 BLOCK_END_OF_CHAIN);
5242 * Mark the extra blocks as free
5244 while (extraBlock != BLOCK_END_OF_CHAIN)
5246 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5249 SmallBlockChainStream_FreeBlock(This, extraBlock);
5250 extraBlock = blockIndex;
5256 /******************************************************************************
5257 * SmallBlockChainStream_Enlarge
5259 * Grows this chain in the small block depot.
5261 BOOL SmallBlockChainStream_Enlarge(
5262 SmallBlockChainStream* This,
5263 ULARGE_INTEGER newSize)
5265 ULONG blockIndex, currentBlock;
5267 ULONG oldNumBlocks = 0;
5269 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5274 if (blockIndex == BLOCK_END_OF_CHAIN)
5277 StgProperty chainProp;
5279 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5282 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5284 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5287 blockIndex = chainProp.startingBlock;
5288 SmallBlockChainStream_SetNextBlockInChain(
5291 BLOCK_END_OF_CHAIN);
5294 currentBlock = blockIndex;
5297 * Figure out how many blocks are needed to contain this stream
5299 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5301 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5305 * Go to the current end of chain
5307 while (blockIndex != BLOCK_END_OF_CHAIN)
5310 currentBlock = blockIndex;
5311 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5316 * Add new blocks to the chain
5318 while (oldNumBlocks < newNumBlocks)
5320 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5321 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5323 SmallBlockChainStream_SetNextBlockInChain(
5326 BLOCK_END_OF_CHAIN);
5328 currentBlock = blockIndex;
5335 /******************************************************************************
5336 * SmallBlockChainStream_GetCount
5338 * Returns the number of blocks that comprises this chain.
5339 * This is not the size of this chain as the last block may not be full!
5341 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5346 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5348 while (blockIndex != BLOCK_END_OF_CHAIN)
5352 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5359 /******************************************************************************
5360 * SmallBlockChainStream_SetSize
5362 * Sets the size of this stream.
5363 * The file will grow if we grow the chain.
5365 * TODO: Free the actual blocks in the file when we shrink the chain.
5366 * Currently, the blocks are still in the file. So the file size
5367 * doesn't shrink even if we shrink streams.
5369 BOOL SmallBlockChainStream_SetSize(
5370 SmallBlockChainStream* This,
5371 ULARGE_INTEGER newSize)
5373 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5375 if (newSize.u.LowPart == size.u.LowPart)
5378 if (newSize.u.LowPart < size.u.LowPart)
5380 SmallBlockChainStream_Shrink(This, newSize);
5384 SmallBlockChainStream_Enlarge(This, newSize);
5390 /******************************************************************************
5391 * SmallBlockChainStream_GetSize
5393 * Returns the size of this chain.
5395 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5397 StgProperty chainProperty;
5399 StorageImpl_ReadProperty(
5400 This->parentStorage,
5401 This->ownerPropertyIndex,
5404 return chainProperty.size;
5407 /******************************************************************************
5408 * StgCreateDocfile [OLE32.@]
5410 HRESULT WINAPI StgCreateDocfile(
5414 IStorage **ppstgOpen)
5416 StorageImpl* newStorage = 0;
5417 HANDLE hFile = INVALID_HANDLE_VALUE;
5422 DWORD fileAttributes;
5423 WCHAR tempFileName[MAX_PATH];
5425 TRACE("(%s, %lx, %ld, %p)\n",
5426 debugstr_w(pwcsName), grfMode,
5427 reserved, ppstgOpen);
5430 * Validate the parameters
5433 return STG_E_INVALIDPOINTER;
5436 * Validate the STGM flags
5438 if ( FAILED( validateSTGM(grfMode) ))
5439 return STG_E_INVALIDFLAG;
5442 * Generate a unique name.
5446 WCHAR tempPath[MAX_PATH];
5447 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5449 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5450 return STG_E_INVALIDFLAG;
5451 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5452 return STG_E_INVALIDFLAG;
5454 memset(tempPath, 0, sizeof(tempPath));
5455 memset(tempFileName, 0, sizeof(tempFileName));
5457 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5460 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5461 pwcsName = tempFileName;
5463 return STG_E_INSUFFICIENTMEMORY;
5465 creationMode = TRUNCATE_EXISTING;
5469 creationMode = GetCreationModeFromSTGM(grfMode);
5473 * Interpret the STGM value grfMode
5475 shareMode = GetShareModeFromSTGM(grfMode);
5476 accessMode = GetAccessModeFromSTGM(grfMode);
5478 if (grfMode & STGM_DELETEONRELEASE)
5479 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5481 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5483 if (grfMode & STGM_TRANSACTED)
5484 FIXME("Transacted mode not implemented.\n");
5487 * Initialize the "out" parameter.
5491 hFile = CreateFileW(pwcsName,
5499 if (hFile == INVALID_HANDLE_VALUE)
5505 * Allocate and initialize the new IStorage32object.
5507 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5509 if (newStorage == 0)
5510 return STG_E_INSUFFICIENTMEMORY;
5512 hr = StorageImpl_Construct(
5523 HeapFree(GetProcessHeap(), 0, newStorage);
5528 * Get an "out" pointer for the caller.
5530 hr = StorageBaseImpl_QueryInterface(
5531 (IStorage*)newStorage,
5532 (REFIID)&IID_IStorage,
5538 /******************************************************************************
5539 * StgCreateStorageEx [OLE32.@]
5541 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5543 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5544 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5545 return STG_E_UNIMPLEMENTEDFUNCTION;
5548 /******************************************************************************
5549 * StgOpenStorage [OLE32.@]
5551 HRESULT WINAPI StgOpenStorage(
5552 const OLECHAR *pwcsName,
5553 IStorage *pstgPriority,
5557 IStorage **ppstgOpen)
5559 StorageImpl* newStorage = 0;
5564 WCHAR fullname[MAX_PATH];
5567 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5568 debugstr_w(pwcsName), pstgPriority, grfMode,
5569 snbExclude, reserved, ppstgOpen);
5572 * Perform a sanity check
5574 if (( pwcsName == 0) || (ppstgOpen == 0) )
5576 hr = STG_E_INVALIDPOINTER;
5581 * Validate the STGM flags
5583 if ( FAILED( validateSTGM(grfMode) ))
5585 hr = STG_E_INVALIDFLAG;
5590 * Interpret the STGM value grfMode
5592 shareMode = GetShareModeFromSTGM(grfMode);
5593 accessMode = GetAccessModeFromSTGM(grfMode);
5596 * Initialize the "out" parameter.
5600 hFile = CreateFileW( pwcsName,
5605 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5608 length = GetFileSize(hFile, NULL);
5610 if (hFile==INVALID_HANDLE_VALUE)
5612 DWORD last_error = GetLastError();
5618 case ERROR_FILE_NOT_FOUND:
5619 hr = STG_E_FILENOTFOUND;
5622 case ERROR_PATH_NOT_FOUND:
5623 hr = STG_E_PATHNOTFOUND;
5626 case ERROR_ACCESS_DENIED:
5627 case ERROR_WRITE_PROTECT:
5628 hr = STG_E_ACCESSDENIED;
5631 case ERROR_SHARING_VIOLATION:
5632 hr = STG_E_SHAREVIOLATION;
5643 * Allocate and initialize the new IStorage32object.
5645 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5647 if (newStorage == 0)
5649 hr = STG_E_INSUFFICIENTMEMORY;
5653 /* if the file's length was zero, initialize the storage */
5654 hr = StorageImpl_Construct(
5665 HeapFree(GetProcessHeap(), 0, newStorage);
5667 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5669 if(hr == STG_E_INVALIDHEADER)
5670 hr = STG_E_FILEALREADYEXISTS;
5674 /* prepare the file name string given in lieu of the root property name */
5675 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5676 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5677 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5680 * Get an "out" pointer for the caller.
5682 hr = StorageBaseImpl_QueryInterface(
5683 (IStorage*)newStorage,
5684 (REFIID)&IID_IStorage,
5688 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5692 /******************************************************************************
5693 * StgCreateDocfileOnILockBytes [OLE32.@]
5695 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5699 IStorage** ppstgOpen)
5701 StorageImpl* newStorage = 0;
5705 * Validate the parameters
5707 if ((ppstgOpen == 0) || (plkbyt == 0))
5708 return STG_E_INVALIDPOINTER;
5711 * Allocate and initialize the new IStorage object.
5713 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5715 if (newStorage == 0)
5716 return STG_E_INSUFFICIENTMEMORY;
5718 hr = StorageImpl_Construct(
5729 HeapFree(GetProcessHeap(), 0, newStorage);
5734 * Get an "out" pointer for the caller.
5736 hr = StorageBaseImpl_QueryInterface(
5737 (IStorage*)newStorage,
5738 (REFIID)&IID_IStorage,
5744 /******************************************************************************
5745 * StgOpenStorageOnILockBytes [OLE32.@]
5747 HRESULT WINAPI StgOpenStorageOnILockBytes(
5749 IStorage *pstgPriority,
5753 IStorage **ppstgOpen)
5755 StorageImpl* newStorage = 0;
5759 * Perform a sanity check
5761 if ((plkbyt == 0) || (ppstgOpen == 0))
5762 return STG_E_INVALIDPOINTER;
5765 * Validate the STGM flags
5767 if ( FAILED( validateSTGM(grfMode) ))
5768 return STG_E_INVALIDFLAG;
5771 * Initialize the "out" parameter.
5776 * Allocate and initialize the new IStorage object.
5778 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5780 if (newStorage == 0)
5781 return STG_E_INSUFFICIENTMEMORY;
5783 hr = StorageImpl_Construct(
5794 HeapFree(GetProcessHeap(), 0, newStorage);
5799 * Get an "out" pointer for the caller.
5801 hr = StorageBaseImpl_QueryInterface(
5802 (IStorage*)newStorage,
5803 (REFIID)&IID_IStorage,
5809 /******************************************************************************
5810 * StgSetTimes [ole32.@]
5811 * StgSetTimes [OLE32.@]
5815 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *a,
5816 FILETIME const *b, FILETIME const *c )
5818 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5822 /******************************************************************************
5823 * StgIsStorageILockBytes [OLE32.@]
5825 * Determines if the ILockBytes contains a storage object.
5827 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5830 ULARGE_INTEGER offset;
5832 offset.u.HighPart = 0;
5833 offset.u.LowPart = 0;
5835 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5837 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5843 /******************************************************************************
5844 * WriteClassStg [OLE32.@]
5846 * This method will store the specified CLSID in the specified storage object
5848 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5854 hRes = IStorage_SetClass(pStg, rclsid);
5859 /***********************************************************************
5860 * ReadClassStg (OLE32.@)
5862 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5864 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5874 * read a STATSTG structure (contains the clsid) from the storage
5876 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5879 *pclsid=pstatstg.clsid;
5884 /***********************************************************************
5885 * OleLoadFromStream (OLE32.@)
5887 * This function loads an object from stream
5889 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5893 LPPERSISTSTREAM xstm;
5895 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5897 res=ReadClassStm(pStm,&clsid);
5898 if (!SUCCEEDED(res))
5900 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5901 if (!SUCCEEDED(res))
5903 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5904 if (!SUCCEEDED(res)) {
5905 IUnknown_Release((IUnknown*)*ppvObj);
5908 res=IPersistStream_Load(xstm,pStm);
5909 IPersistStream_Release(xstm);
5910 /* FIXME: all refcounts ok at this point? I think they should be:
5913 * xstm : 0 (released)
5918 /***********************************************************************
5919 * OleSaveToStream (OLE32.@)
5921 * This function saves an object with the IPersistStream interface on it
5922 * to the specified stream.
5924 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5930 TRACE("(%p,%p)\n",pPStm,pStm);
5932 res=IPersistStream_GetClassID(pPStm,&clsid);
5934 if (SUCCEEDED(res)){
5936 res=WriteClassStm(pStm,&clsid);
5940 res=IPersistStream_Save(pPStm,pStm,TRUE);
5943 TRACE("Finished Save\n");
5947 /****************************************************************************
5948 * This method validate a STGM parameter that can contain the values below
5950 * STGM_DIRECT 0x00000000
5951 * STGM_TRANSACTED 0x00010000
5952 * STGM_SIMPLE 0x08000000
5954 * STGM_READ 0x00000000
5955 * STGM_WRITE 0x00000001
5956 * STGM_READWRITE 0x00000002
5958 * STGM_SHARE_DENY_NONE 0x00000040
5959 * STGM_SHARE_DENY_READ 0x00000030
5960 * STGM_SHARE_DENY_WRITE 0x00000020
5961 * STGM_SHARE_EXCLUSIVE 0x00000010
5963 * STGM_PRIORITY 0x00040000
5964 * STGM_DELETEONRELEASE 0x04000000
5966 * STGM_CREATE 0x00001000
5967 * STGM_CONVERT 0x00020000
5968 * STGM_FAILIFTHERE 0x00000000
5970 * STGM_NOSCRATCH 0x00100000
5971 * STGM_NOSNAPSHOT 0x00200000
5973 static HRESULT validateSTGM(DWORD stgm)
5975 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5976 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5977 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5979 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5980 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5981 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5983 BOOL bSTGM_SHARE_DENY_NONE =
5984 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5986 BOOL bSTGM_SHARE_DENY_READ =
5987 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5989 BOOL bSTGM_SHARE_DENY_WRITE =
5990 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5992 BOOL bSTGM_SHARE_EXCLUSIVE =
5993 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5995 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5996 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5998 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5999 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
6002 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6004 if ( ! bSTGM_DIRECT )
6005 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
6009 * STGM_WRITE | STGM_READWRITE | STGM_READ
6012 if( bSTGM_WRITE && bSTGM_READWRITE )
6016 * STGM_SHARE_DENY_NONE | others
6017 * (I assume here that DENY_READ implies DENY_WRITE)
6019 if ( bSTGM_SHARE_DENY_NONE )
6020 if ( bSTGM_SHARE_DENY_READ ||
6021 bSTGM_SHARE_DENY_WRITE ||
6022 bSTGM_SHARE_EXCLUSIVE)
6026 * STGM_CREATE | STGM_CONVERT
6027 * if both are false, STGM_FAILIFTHERE is set to TRUE
6029 if ( bSTGM_CREATE && bSTGM_CONVERT )
6033 * STGM_NOSCRATCH requires STGM_TRANSACTED
6035 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6039 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6040 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6042 if (bSTGM_NOSNAPSHOT)
6044 if ( ! ( bSTGM_TRANSACTED &&
6045 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6052 /****************************************************************************
6053 * GetShareModeFromSTGM
6055 * This method will return a share mode flag from a STGM value.
6056 * The STGM value is assumed valid.
6058 static DWORD GetShareModeFromSTGM(DWORD stgm)
6060 DWORD dwShareMode = 0;
6061 BOOL bSTGM_SHARE_DENY_NONE =
6062 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6064 BOOL bSTGM_SHARE_DENY_READ =
6065 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6067 BOOL bSTGM_SHARE_DENY_WRITE =
6068 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6070 BOOL bSTGM_SHARE_EXCLUSIVE =
6071 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6073 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6076 if (bSTGM_SHARE_DENY_NONE)
6077 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6079 if (bSTGM_SHARE_DENY_WRITE)
6080 dwShareMode = FILE_SHARE_READ;
6085 /****************************************************************************
6086 * GetAccessModeFromSTGM
6088 * This method will return an access mode flag from a STGM value.
6089 * The STGM value is assumed valid.
6091 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6093 DWORD dwDesiredAccess = GENERIC_READ;
6094 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6095 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6096 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6099 dwDesiredAccess = GENERIC_READ;
6102 dwDesiredAccess |= GENERIC_WRITE;
6104 if (bSTGM_READWRITE)
6105 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6107 return dwDesiredAccess;
6110 /****************************************************************************
6111 * GetCreationModeFromSTGM
6113 * This method will return a creation mode flag from a STGM value.
6114 * The STGM value is assumed valid.
6116 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6118 if ( stgm & STGM_CREATE)
6119 return CREATE_ALWAYS;
6120 if (stgm & STGM_CONVERT) {
6121 FIXME("STGM_CONVERT not implemented!\n");
6124 /* All other cases */
6125 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6126 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6131 /*************************************************************************
6132 * OLECONVERT_LoadOLE10 [Internal]
6134 * Loads the OLE10 STREAM to memory
6137 * pOleStream [I] The OLESTREAM
6138 * pData [I] Data Structure for the OLESTREAM Data
6142 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6143 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6146 * This function is used by OleConvertOLESTREAMToIStorage only.
6148 * Memory allocated for pData must be freed by the caller
6150 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6153 HRESULT hRes = S_OK;
6157 pData->pData = NULL;
6158 pData->pstrOleObjFileName = (CHAR *) NULL;
6160 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6163 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6164 if(dwSize != sizeof(pData->dwOleID))
6166 hRes = CONVERT10_E_OLESTREAM_GET;
6168 else if(pData->dwOleID != OLESTREAM_ID)
6170 hRes = CONVERT10_E_OLESTREAM_FMT;
6181 /* Get the TypeID...more info needed for this field */
6182 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6183 if(dwSize != sizeof(pData->dwTypeID))
6185 hRes = CONVERT10_E_OLESTREAM_GET;
6190 if(pData->dwTypeID != 0)
6192 /* Get the length of the OleTypeName */
6193 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6194 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6196 hRes = CONVERT10_E_OLESTREAM_GET;
6201 if(pData->dwOleTypeNameLength > 0)
6203 /* Get the OleTypeName */
6204 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6205 if(dwSize != pData->dwOleTypeNameLength)
6207 hRes = CONVERT10_E_OLESTREAM_GET;
6213 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6214 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6216 hRes = CONVERT10_E_OLESTREAM_GET;
6220 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6221 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6222 pData->pstrOleObjFileName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6223 if(pData->pstrOleObjFileName)
6225 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6226 if(dwSize != pData->dwOleObjFileNameLength)
6228 hRes = CONVERT10_E_OLESTREAM_GET;
6232 hRes = CONVERT10_E_OLESTREAM_GET;
6237 /* Get the Width of the Metafile */
6238 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6239 if(dwSize != sizeof(pData->dwMetaFileWidth))
6241 hRes = CONVERT10_E_OLESTREAM_GET;
6245 /* Get the Height of the Metafile */
6246 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6247 if(dwSize != sizeof(pData->dwMetaFileHeight))
6249 hRes = CONVERT10_E_OLESTREAM_GET;
6255 /* Get the Length of the Data */
6256 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6257 if(dwSize != sizeof(pData->dwDataLength))
6259 hRes = CONVERT10_E_OLESTREAM_GET;
6263 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6265 if(!bStrem1) /* if it is a second OLE stream data */
6267 pData->dwDataLength -= 8;
6268 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6269 if(dwSize != sizeof(pData->strUnknown))
6271 hRes = CONVERT10_E_OLESTREAM_GET;
6277 if(pData->dwDataLength > 0)
6279 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6281 /* Get Data (ex. IStorage, Metafile, or BMP) */
6284 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6285 if(dwSize != pData->dwDataLength)
6287 hRes = CONVERT10_E_OLESTREAM_GET;
6292 hRes = CONVERT10_E_OLESTREAM_GET;
6301 /*************************************************************************
6302 * OLECONVERT_SaveOLE10 [Internal]
6304 * Saves the OLE10 STREAM From memory
6307 * pData [I] Data Structure for the OLESTREAM Data
6308 * pOleStream [I] The OLESTREAM to save
6312 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6315 * This function is used by OleConvertIStorageToOLESTREAM only.
6318 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6321 HRESULT hRes = S_OK;
6325 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6326 if(dwSize != sizeof(pData->dwOleID))
6328 hRes = CONVERT10_E_OLESTREAM_PUT;
6333 /* Set the TypeID */
6334 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6335 if(dwSize != sizeof(pData->dwTypeID))
6337 hRes = CONVERT10_E_OLESTREAM_PUT;
6341 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6343 /* Set the Length of the OleTypeName */
6344 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6345 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6347 hRes = CONVERT10_E_OLESTREAM_PUT;
6352 if(pData->dwOleTypeNameLength > 0)
6354 /* Set the OleTypeName */
6355 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6356 if(dwSize != pData->dwOleTypeNameLength)
6358 hRes = CONVERT10_E_OLESTREAM_PUT;
6365 /* Set the width of the Metafile */
6366 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6367 if(dwSize != sizeof(pData->dwMetaFileWidth))
6369 hRes = CONVERT10_E_OLESTREAM_PUT;
6375 /* Set the height of the Metafile */
6376 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6377 if(dwSize != sizeof(pData->dwMetaFileHeight))
6379 hRes = CONVERT10_E_OLESTREAM_PUT;
6385 /* Set the length of the Data */
6386 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6387 if(dwSize != sizeof(pData->dwDataLength))
6389 hRes = CONVERT10_E_OLESTREAM_PUT;
6395 if(pData->dwDataLength > 0)
6397 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6398 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6399 if(dwSize != pData->dwDataLength)
6401 hRes = CONVERT10_E_OLESTREAM_PUT;
6409 /*************************************************************************
6410 * OLECONVERT_GetOLE20FromOLE10[Internal]
6412 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6413 * opens it, and copies the content to the dest IStorage for
6414 * OleConvertOLESTREAMToIStorage
6418 * pDestStorage [I] The IStorage to copy the data to
6419 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6420 * nBufferLength [I] The size of the buffer
6429 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6433 IStorage *pTempStorage;
6434 DWORD dwNumOfBytesWritten;
6435 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6436 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6438 /* Create a temp File */
6439 GetTempPathW(MAX_PATH, wstrTempDir);
6440 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6441 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6443 if(hFile != INVALID_HANDLE_VALUE)
6445 /* Write IStorage Data to File */
6446 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6449 /* Open and copy temp storage to the Dest Storage */
6450 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6453 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6454 StorageBaseImpl_Release(pTempStorage);
6456 DeleteFileW(wstrTempFile);
6461 /*************************************************************************
6462 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6464 * Saves the OLE10 STREAM From memory
6467 * pStorage [I] The Src IStorage to copy
6468 * pData [I] The Dest Memory to write to.
6471 * The size in bytes allocated for pData
6474 * Memory allocated for pData must be freed by the caller
6476 * Used by OleConvertIStorageToOLESTREAM only.
6479 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6483 DWORD nDataLength = 0;
6484 IStorage *pTempStorage;
6485 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6486 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6490 /* Create temp Storage */
6491 GetTempPathW(MAX_PATH, wstrTempDir);
6492 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6493 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6497 /* Copy Src Storage to the Temp Storage */
6498 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6499 StorageBaseImpl_Release(pTempStorage);
6501 /* Open Temp Storage as a file and copy to memory */
6502 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6503 if(hFile != INVALID_HANDLE_VALUE)
6505 nDataLength = GetFileSize(hFile, NULL);
6506 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6507 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6510 DeleteFileW(wstrTempFile);
6515 /*************************************************************************
6516 * OLECONVERT_CreateOleStream [Internal]
6518 * Creates the "\001OLE" stream in the IStorage if necessary.
6521 * pStorage [I] Dest storage to create the stream in
6527 * This function is used by OleConvertOLESTREAMToIStorage only.
6529 * This stream is still unknown, MS Word seems to have extra data
6530 * but since the data is stored in the OLESTREAM there should be
6531 * no need to recreate the stream. If the stream is manually
6532 * deleted it will create it with this default data.
6535 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6539 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6540 BYTE pOleStreamHeader [] =
6542 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6543 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6544 0x00, 0x00, 0x00, 0x00
6547 /* Create stream if not present */
6548 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6549 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6553 /* Write default Data */
6554 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6555 IStream_Release(pStream);
6559 /* write a string to a stream, preceded by its length */
6560 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6567 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6568 r = IStream_Write( stm, &len, sizeof(len), NULL);
6573 str = CoTaskMemAlloc( len );
6574 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6575 r = IStream_Write( stm, str, len, NULL);
6576 CoTaskMemFree( str );
6580 /* read a string preceded by its length from a stream */
6581 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6584 DWORD len, count = 0;
6588 r = IStream_Read( stm, &len, sizeof(len), &count );
6591 if( count != sizeof(len) )
6592 return E_OUTOFMEMORY;
6594 TRACE("%ld bytes\n",len);
6596 str = CoTaskMemAlloc( len );
6598 return E_OUTOFMEMORY;
6600 r = IStream_Read( stm, str, len, &count );
6605 CoTaskMemFree( str );
6606 return E_OUTOFMEMORY;
6609 TRACE("Read string %s\n",debugstr_an(str,len));
6611 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6612 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6614 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6615 CoTaskMemFree( str );
6623 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6624 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6628 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6630 static const BYTE unknown1[12] =
6631 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6632 0xFF, 0xFF, 0xFF, 0xFF};
6633 static const BYTE unknown2[16] =
6634 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6635 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6637 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6638 debugstr_w(lpszUserType), debugstr_w(szClipName),
6639 debugstr_w(szProgIDName));
6641 /* Create a CompObj stream if it doesn't exist */
6642 r = IStorage_CreateStream(pstg, szwStreamName,
6643 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6647 /* Write CompObj Structure to stream */
6648 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6650 if( SUCCEEDED( r ) )
6651 r = WriteClassStm( pstm, clsid );
6653 if( SUCCEEDED( r ) )
6654 r = STREAM_WriteString( pstm, lpszUserType );
6655 if( SUCCEEDED( r ) )
6656 r = STREAM_WriteString( pstm, szClipName );
6657 if( SUCCEEDED( r ) )
6658 r = STREAM_WriteString( pstm, szProgIDName );
6659 if( SUCCEEDED( r ) )
6660 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6662 IStream_Release( pstm );
6667 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6668 static HRESULT CLSIDFromUserType(LPCWSTR lpszUserType, CLSID *clsid)
6670 LONG r, count, i, len;
6672 HKEY hkey, hkeyclsid;
6673 LPWSTR buffer = NULL;
6675 static const WCHAR szclsid[] = { 'C','L','S','I','D',0 };
6677 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType));
6679 r = RegOpenKeyW( HKEY_CLASSES_ROOT, szclsid, &hkeyclsid );
6681 return E_INVALIDARG;
6683 len = lstrlenW( lpszUserType ) + 1;
6684 buffer = CoTaskMemAlloc( len * sizeof (WCHAR) );
6688 for(i=0; !found; i++ )
6690 r = RegEnumKeyW( hkeyclsid, i, szKey, sizeof(szKey)/sizeof(WCHAR));
6691 if( r != ERROR_SUCCESS )
6694 r = RegOpenKeyW( hkeyclsid, szKey, &hkey );
6695 if( r != ERROR_SUCCESS )
6697 count = len * sizeof (WCHAR);
6698 r = RegQueryValueW( hkey, NULL, buffer, &count );
6699 found = ( r == ERROR_SUCCESS ) &&
6700 ( count == len*sizeof(WCHAR) ) &&
6701 !lstrcmpW( buffer, lpszUserType ) ;
6702 RegCloseKey( hkey );
6707 CoTaskMemFree( buffer );
6708 RegCloseKey( hkeyclsid );
6711 return E_INVALIDARG;
6713 TRACE("clsid is %s\n", debugstr_w( szKey ) );
6715 r = CLSIDFromString( szKey, clsid );
6721 /***********************************************************************
6722 * WriteFmtUserTypeStg (OLE32.@)
6724 HRESULT WINAPI WriteFmtUserTypeStg(
6725 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6728 WCHAR szwClipName[0x40];
6729 WCHAR szCLSIDName[OLESTREAM_MAX_STR_LEN];
6733 LPMALLOC allocator = NULL;
6735 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6737 r = CoGetMalloc(0, &allocator);
6739 return E_OUTOFMEMORY;
6741 /* get the clipboard format name */
6742 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6745 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6749 r = CLSIDFromUserType(lpszUserType, &clsid);
6753 TRACE("CLSID is %s\n",debugstr_guid(&clsid));
6755 /* get the real program ID */
6756 r = ProgIDFromCLSID( &clsid, &wstrProgID);
6760 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6762 /* if we have a good string, write the stream */
6764 r = STORAGE_WriteCompObj( pstg, &clsid,
6765 lpszUserType, szwClipName, wstrProgID );
6769 IMalloc_Free( allocator, wstrProgID);
6775 /******************************************************************************
6776 * ReadFmtUserTypeStg [OLE32.@]
6778 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6782 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6783 unsigned char unknown1[12];
6784 unsigned char unknown2[16];
6786 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6789 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6791 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6792 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6795 ERR("Failed to open stream\n");
6799 /* read the various parts of the structure */
6800 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6801 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6803 r = ReadClassStm( stm, &clsid );
6807 r = STREAM_ReadString( stm, &szCLSIDName );
6811 r = STREAM_ReadString( stm, &szOleTypeName );
6815 r = STREAM_ReadString( stm, &szProgIDName );
6819 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6820 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6823 /* ok, success... now we just need to store what we found */
6825 *pcf = RegisterClipboardFormatW( szOleTypeName );
6826 CoTaskMemFree( szOleTypeName );
6828 if( lplpszUserType )
6829 *lplpszUserType = szCLSIDName;
6830 CoTaskMemFree( szProgIDName );
6833 IStream_Release( stm );
6839 /*************************************************************************
6840 * OLECONVERT_CreateCompObjStream [Internal]
6842 * Creates a "\001CompObj" is the destination IStorage if necessary.
6845 * pStorage [I] The dest IStorage to create the CompObj Stream
6847 * strOleTypeName [I] The ProgID
6851 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6854 * This function is used by OleConvertOLESTREAMToIStorage only.
6856 * The stream data is stored in the OLESTREAM and there should be
6857 * no need to recreate the stream. If the stream is manually
6858 * deleted it will attempt to create it by querying the registry.
6862 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6865 HRESULT hStorageRes, hRes = S_OK;
6866 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6867 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6868 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
6870 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6871 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6873 /* Initialize the CompObj structure */
6874 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6875 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6876 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6879 /* Create a CompObj stream if it doesn't exist */
6880 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6881 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6882 if(hStorageRes == S_OK)
6884 /* copy the OleTypeName to the compobj struct */
6885 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6886 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6888 /* copy the OleTypeName to the compobj struct */
6889 /* Note: in the test made, these were Identical */
6890 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6891 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6894 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
6895 bufferW, OLESTREAM_MAX_STR_LEN );
6896 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
6902 /* Get the CLSID Default Name from the Registry */
6903 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6904 if(hErr == ERROR_SUCCESS)
6906 char strTemp[OLESTREAM_MAX_STR_LEN];
6907 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6908 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6909 if(hErr == ERROR_SUCCESS)
6911 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6917 /* Write CompObj Structure to stream */
6918 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6920 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6922 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6923 if(IStorageCompObj.dwCLSIDNameLength > 0)
6925 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6927 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6928 if(IStorageCompObj.dwOleTypeNameLength > 0)
6930 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6932 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6933 if(IStorageCompObj.dwProgIDNameLength > 0)
6935 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6937 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6938 IStream_Release(pStream);
6944 /*************************************************************************
6945 * OLECONVERT_CreateOlePresStream[Internal]
6947 * Creates the "\002OlePres000" Stream with the Metafile data
6950 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6951 * dwExtentX [I] Width of the Metafile
6952 * dwExtentY [I] Height of the Metafile
6953 * pData [I] Metafile data
6954 * dwDataLength [I] Size of the Metafile data
6958 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6961 * This function is used by OleConvertOLESTREAMToIStorage only.
6964 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6968 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6969 BYTE pOlePresStreamHeader [] =
6971 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6972 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6973 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6974 0x00, 0x00, 0x00, 0x00
6977 BYTE pOlePresStreamHeaderEmpty [] =
6979 0x00, 0x00, 0x00, 0x00,
6980 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6981 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6982 0x00, 0x00, 0x00, 0x00
6985 /* Create the OlePres000 Stream */
6986 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6987 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6992 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6994 memset(&OlePres, 0, sizeof(OlePres));
6995 /* Do we have any metafile data to save */
6996 if(dwDataLength > 0)
6998 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6999 nHeaderSize = sizeof(pOlePresStreamHeader);
7003 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7004 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7006 /* Set width and height of the metafile */
7007 OlePres.dwExtentX = dwExtentX;
7008 OlePres.dwExtentY = -dwExtentY;
7010 /* Set Data and Length */
7011 if(dwDataLength > sizeof(METAFILEPICT16))
7013 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7014 OlePres.pData = &(pData[8]);
7016 /* Save OlePres000 Data to Stream */
7017 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7018 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7019 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7020 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7021 if(OlePres.dwSize > 0)
7023 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7025 IStream_Release(pStream);
7029 /*************************************************************************
7030 * OLECONVERT_CreateOle10NativeStream [Internal]
7032 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7035 * pStorage [I] Dest storage to create the stream in
7036 * pData [I] Ole10 Native Data (ex. bmp)
7037 * dwDataLength [I] Size of the Ole10 Native Data
7043 * This function is used by OleConvertOLESTREAMToIStorage only.
7045 * Might need to verify the data and return appropriate error message
7048 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7052 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7054 /* Create the Ole10Native Stream */
7055 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7056 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7060 /* Write info to stream */
7061 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7062 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7063 IStream_Release(pStream);
7068 /*************************************************************************
7069 * OLECONVERT_GetOLE10ProgID [Internal]
7071 * Finds the ProgID (or OleTypeID) from the IStorage
7074 * pStorage [I] The Src IStorage to get the ProgID
7075 * strProgID [I] the ProgID string to get
7076 * dwSize [I] the size of the string
7080 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7083 * This function is used by OleConvertIStorageToOLESTREAM only.
7087 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7091 LARGE_INTEGER iSeekPos;
7092 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7093 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7095 /* Open the CompObj Stream */
7096 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7097 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7101 /*Get the OleType from the CompObj Stream */
7102 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7103 iSeekPos.u.HighPart = 0;
7105 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7106 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7107 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7108 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7109 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7110 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7111 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7113 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7116 IStream_Read(pStream, strProgID, *dwSize, NULL);
7118 IStream_Release(pStream);
7123 LPOLESTR wstrProgID;
7125 /* Get the OleType from the registry */
7126 REFCLSID clsid = &(stat.clsid);
7127 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7128 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7131 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7138 /*************************************************************************
7139 * OLECONVERT_GetOle10PresData [Internal]
7141 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7144 * pStorage [I] Src IStroage
7145 * pOleStream [I] Dest OleStream Mem Struct
7151 * This function is used by OleConvertIStorageToOLESTREAM only.
7153 * Memory allocated for pData must be freed by the caller
7157 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7162 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7164 /* Initialize Default data for OLESTREAM */
7165 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7166 pOleStreamData[0].dwTypeID = 2;
7167 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7168 pOleStreamData[1].dwTypeID = 0;
7169 pOleStreamData[0].dwMetaFileWidth = 0;
7170 pOleStreamData[0].dwMetaFileHeight = 0;
7171 pOleStreamData[0].pData = NULL;
7172 pOleStreamData[1].pData = NULL;
7174 /* Open Ole10Native Stream */
7175 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7176 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7180 /* Read Size and Data */
7181 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7182 if(pOleStreamData->dwDataLength > 0)
7184 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7185 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7187 IStream_Release(pStream);
7193 /*************************************************************************
7194 * OLECONVERT_GetOle20PresData[Internal]
7196 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7199 * pStorage [I] Src IStroage
7200 * pOleStreamData [I] Dest OleStream Mem Struct
7206 * This function is used by OleConvertIStorageToOLESTREAM only.
7208 * Memory allocated for pData must be freed by the caller
7210 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7214 OLECONVERT_ISTORAGE_OLEPRES olePress;
7215 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7217 /* Initialize Default data for OLESTREAM */
7218 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7219 pOleStreamData[0].dwTypeID = 2;
7220 pOleStreamData[0].dwMetaFileWidth = 0;
7221 pOleStreamData[0].dwMetaFileHeight = 0;
7222 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7223 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7224 pOleStreamData[1].dwTypeID = 0;
7225 pOleStreamData[1].dwOleTypeNameLength = 0;
7226 pOleStreamData[1].strOleTypeName[0] = 0;
7227 pOleStreamData[1].dwMetaFileWidth = 0;
7228 pOleStreamData[1].dwMetaFileHeight = 0;
7229 pOleStreamData[1].pData = NULL;
7230 pOleStreamData[1].dwDataLength = 0;
7233 /* Open OlePress000 stream */
7234 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7235 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7238 LARGE_INTEGER iSeekPos;
7239 METAFILEPICT16 MetaFilePict;
7240 static const char strMetafilePictName[] = "METAFILEPICT";
7242 /* Set the TypeID for a Metafile */
7243 pOleStreamData[1].dwTypeID = 5;
7245 /* Set the OleTypeName to Metafile */
7246 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7247 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7249 iSeekPos.u.HighPart = 0;
7250 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7252 /* Get Presentation Data */
7253 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7254 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7255 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7256 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7258 /*Set width and Height */
7259 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7260 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7261 if(olePress.dwSize > 0)
7264 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7266 /* Set MetaFilePict struct */
7267 MetaFilePict.mm = 8;
7268 MetaFilePict.xExt = olePress.dwExtentX;
7269 MetaFilePict.yExt = olePress.dwExtentY;
7270 MetaFilePict.hMF = 0;
7272 /* Get Metafile Data */
7273 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7274 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7275 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7277 IStream_Release(pStream);
7281 /*************************************************************************
7282 * OleConvertOLESTREAMToIStorage [OLE32.@]
7287 * DVTARGETDEVICE paramenter is not handled
7288 * Still unsure of some mem fields for OLE 10 Stream
7289 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7290 * and "\001OLE" streams
7293 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7294 LPOLESTREAM pOleStream,
7296 const DVTARGETDEVICE* ptd)
7300 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7302 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7306 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7309 if(pstg == NULL || pOleStream == NULL)
7311 hRes = E_INVALIDARG;
7316 /* Load the OLESTREAM to Memory */
7317 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7322 /* Load the OLESTREAM to Memory (part 2)*/
7323 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7329 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7331 /* Do we have the IStorage Data in the OLESTREAM */
7332 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7334 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7335 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7339 /* It must be an original OLE 1.0 source */
7340 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7345 /* It must be an original OLE 1.0 source */
7346 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7349 /* Create CompObj Stream if necessary */
7350 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7353 /*Create the Ole Stream if necessary */
7354 OLECONVERT_CreateOleStream(pstg);
7359 /* Free allocated memory */
7360 for(i=0; i < 2; i++)
7362 if(pOleStreamData[i].pData != NULL)
7364 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7366 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7368 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7369 pOleStreamData[i].pstrOleObjFileName = NULL;
7375 /*************************************************************************
7376 * OleConvertIStorageToOLESTREAM [OLE32.@]
7383 * Still unsure of some mem fields for OLE 10 Stream
7384 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7385 * and "\001OLE" streams.
7388 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7390 LPOLESTREAM pOleStream)
7393 HRESULT hRes = S_OK;
7395 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7396 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7399 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7401 if(pstg == NULL || pOleStream == NULL)
7403 hRes = E_INVALIDARG;
7407 /* Get the ProgID */
7408 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7409 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7413 /* Was it originally Ole10 */
7414 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7417 IStream_Release(pStream);
7418 /* Get Presentation Data for Ole10Native */
7419 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7423 /* Get Presentation Data (OLE20) */
7424 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7427 /* Save OLESTREAM */
7428 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7431 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7436 /* Free allocated memory */
7437 for(i=0; i < 2; i++)
7439 if(pOleStreamData[i].pData != NULL)
7441 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7448 /***********************************************************************
7449 * GetConvertStg (OLE32.@)
7451 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7452 FIXME("unimplemented stub!\n");
7456 /******************************************************************************
7457 * StgIsStorageFile [OLE32.@]
7460 StgIsStorageFile(LPCOLESTR fn)
7466 TRACE("(\'%s\')\n", debugstr_w(fn));
7467 hf = CreateFileW(fn, GENERIC_READ,
7468 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7469 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7471 if (hf == INVALID_HANDLE_VALUE)
7472 return STG_E_FILENOTFOUND;
7474 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7476 WARN(" unable to read file\n");
7483 if (bytes_read != 8) {
7484 WARN(" too short\n");
7488 if (!memcmp(magic,STORAGE_magic,8)) {
7493 WARN(" -> Invalid header.\n");