2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
42 #include "wine/unicode.h"
43 #include "wine/debug.h"
45 #include "storage32.h"
46 #include "ole2.h" /* For Write/ReadClassStm */
49 #include "wine/wingdi16.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(storage);
55 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
56 #define OLESTREAM_ID 0x501
57 #define OLESTREAM_MAX_STR_LEN 255
59 static const char rootPropertyName[] = "Root Entry";
62 /* OLESTREAM memory structure to use for Get and Put Routines */
63 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
68 DWORD dwOleTypeNameLength;
69 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
70 CHAR *pstrOleObjFileName;
71 DWORD dwOleObjFileNameLength;
72 DWORD dwMetaFileWidth;
73 DWORD dwMetaFileHeight;
74 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
77 }OLECONVERT_OLESTREAM_DATA;
79 /* CompObj Stream structure */
80 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
85 DWORD dwCLSIDNameLength;
86 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
87 DWORD dwOleTypeNameLength;
88 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
89 DWORD dwProgIDNameLength;
90 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
92 }OLECONVERT_ISTORAGE_COMPOBJ;
95 /* Ole Presention Stream structure */
96 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
104 }OLECONVERT_ISTORAGE_OLEPRES;
108 /***********************************************************************
109 * Forward declaration of internal functions used by the method DestroyElement
111 static HRESULT deleteStorageProperty(
112 StorageImpl *parentStorage,
113 ULONG foundPropertyIndexToDelete,
114 StgProperty propertyToDelete);
116 static HRESULT deleteStreamProperty(
117 StorageImpl *parentStorage,
118 ULONG foundPropertyIndexToDelete,
119 StgProperty propertyToDelete);
121 static HRESULT findPlaceholder(
122 StorageImpl *storage,
123 ULONG propertyIndexToStore,
124 ULONG storagePropertyIndex,
127 static HRESULT adjustPropertyChain(
129 StgProperty propertyToDelete,
130 StgProperty parentProperty,
131 ULONG parentPropertyId,
134 /***********************************************************************
135 * Declaration of the functions used to manipulate StgProperty
138 static ULONG getFreeProperty(
139 StorageImpl *storage);
141 static void updatePropertyChain(
142 StorageImpl *storage,
143 ULONG newPropertyIndex,
144 StgProperty newProperty);
146 static LONG propertyNameCmp(
147 const OLECHAR *newProperty,
148 const OLECHAR *currentProperty);
151 /***********************************************************************
152 * Declaration of miscellaneous functions...
154 static HRESULT validateSTGM(DWORD stgmValue);
156 static DWORD GetShareModeFromSTGM(DWORD stgm);
157 static DWORD GetAccessModeFromSTGM(DWORD stgm);
158 static DWORD GetCreationModeFromSTGM(DWORD stgm);
161 * Virtual function table for the IStorage32Impl class.
163 static IStorageVtbl Storage32Impl_Vtbl =
165 StorageBaseImpl_QueryInterface,
166 StorageBaseImpl_AddRef,
167 StorageBaseImpl_Release,
168 StorageBaseImpl_CreateStream,
169 StorageBaseImpl_OpenStream,
170 StorageImpl_CreateStorage,
171 StorageBaseImpl_OpenStorage,
173 StorageImpl_MoveElementTo,
176 StorageBaseImpl_EnumElements,
177 StorageImpl_DestroyElement,
178 StorageBaseImpl_RenameElement,
179 StorageImpl_SetElementTimes,
180 StorageBaseImpl_SetClass,
181 StorageImpl_SetStateBits,
186 * Virtual function table for the Storage32InternalImpl class.
188 static IStorageVtbl Storage32InternalImpl_Vtbl =
190 StorageBaseImpl_QueryInterface,
191 StorageBaseImpl_AddRef,
192 StorageBaseImpl_Release,
193 StorageBaseImpl_CreateStream,
194 StorageBaseImpl_OpenStream,
195 StorageImpl_CreateStorage,
196 StorageBaseImpl_OpenStorage,
198 StorageImpl_MoveElementTo,
199 StorageInternalImpl_Commit,
200 StorageInternalImpl_Revert,
201 StorageBaseImpl_EnumElements,
202 StorageImpl_DestroyElement,
203 StorageBaseImpl_RenameElement,
204 StorageImpl_SetElementTimes,
205 StorageBaseImpl_SetClass,
206 StorageImpl_SetStateBits,
211 * Virtual function table for the IEnumSTATSTGImpl class.
213 static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
215 IEnumSTATSTGImpl_QueryInterface,
216 IEnumSTATSTGImpl_AddRef,
217 IEnumSTATSTGImpl_Release,
218 IEnumSTATSTGImpl_Next,
219 IEnumSTATSTGImpl_Skip,
220 IEnumSTATSTGImpl_Reset,
221 IEnumSTATSTGImpl_Clone
224 extern IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
228 /************************************************************************
229 ** Storage32BaseImpl implementatiion
232 /************************************************************************
233 * Storage32BaseImpl_QueryInterface (IUnknown)
235 * This method implements the common QueryInterface for all IStorage32
236 * implementations contained in this file.
238 * See Windows documentation for more details on IUnknown methods.
240 HRESULT WINAPI StorageBaseImpl_QueryInterface(
245 StorageBaseImpl *This = (StorageBaseImpl *)iface;
247 * Perform a sanity check on the parameters.
249 if ( (This==0) || (ppvObject==0) )
253 * Initialize the return parameter.
258 * Compare the riid with the interface IDs implemented by this object.
260 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
262 *ppvObject = (IStorage*)This;
264 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
266 *ppvObject = (IStorage*)This;
268 else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)
270 *ppvObject = (IStorage*)&This->pssVtbl;
274 * Check that we obtained an interface.
277 return E_NOINTERFACE;
280 * Query Interface always increases the reference count by one when it is
283 StorageBaseImpl_AddRef(iface);
288 /************************************************************************
289 * Storage32BaseImpl_AddRef (IUnknown)
291 * This method implements the common AddRef for all IStorage32
292 * implementations contained in this file.
294 * See Windows documentation for more details on IUnknown methods.
296 ULONG WINAPI StorageBaseImpl_AddRef(
299 StorageBaseImpl *This = (StorageBaseImpl *)iface;
300 ULONG ref = InterlockedIncrement(&This->ref);
302 TRACE("(%p) AddRef to %ld\n", This, ref);
307 /************************************************************************
308 * Storage32BaseImpl_Release (IUnknown)
310 * This method implements the common Release for all IStorage32
311 * implementations contained in this file.
313 * See Windows documentation for more details on IUnknown methods.
315 ULONG WINAPI StorageBaseImpl_Release(
318 StorageBaseImpl *This = (StorageBaseImpl *)iface;
320 * Decrease the reference count on this object.
322 ULONG ref = InterlockedDecrement(&This->ref);
324 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
327 * If the reference count goes down to 0, perform suicide.
332 * Since we are using a system of base-classes, we want to call the
333 * destructor of the appropriate derived class. To do this, we are
334 * using virtual functions to implement the destructor.
336 This->v_destructor(This);
342 /************************************************************************
343 * Storage32BaseImpl_OpenStream (IStorage)
345 * This method will open the specified stream object from the current storage.
347 * See Windows documentation for more details on IStorage methods.
349 HRESULT WINAPI StorageBaseImpl_OpenStream(
351 const OLECHAR* pwcsName, /* [string][in] */
352 void* reserved1, /* [unique][in] */
353 DWORD grfMode, /* [in] */
354 DWORD reserved2, /* [in] */
355 IStream** ppstm) /* [out] */
357 StorageBaseImpl *This = (StorageBaseImpl *)iface;
358 IEnumSTATSTGImpl* propertyEnumeration;
359 StgStreamImpl* newStream;
360 StgProperty currentProperty;
361 ULONG foundPropertyIndex;
362 HRESULT res = STG_E_UNKNOWN;
364 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
365 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
368 * Perform a sanity check on the parameters.
370 if ( (pwcsName==NULL) || (ppstm==0) )
377 * Initialize the out parameter
382 * Validate the STGM flags
384 if ( FAILED( validateSTGM(grfMode) ))
386 res = STG_E_INVALIDFLAG;
393 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
394 (grfMode & STGM_DELETEONRELEASE) ||
395 (grfMode & STGM_TRANSACTED) )
397 res = STG_E_INVALIDFUNCTION;
402 * Create a property enumeration to search the properties
404 propertyEnumeration = IEnumSTATSTGImpl_Construct(
405 This->ancestorStorage,
406 This->rootPropertySetIndex);
409 * Search the enumeration for the property with the given name
411 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
417 * Delete the property enumeration since we don't need it anymore
419 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
422 * If it was found, construct the stream object and return a pointer to it.
424 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
425 (currentProperty.propertyType==PROPTYPE_STREAM) )
427 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
431 newStream->grfMode = grfMode;
432 *ppstm = (IStream*)newStream;
435 * Since we are returning a pointer to the interface, we have to
436 * nail down the reference.
438 StgStreamImpl_AddRef(*ppstm);
448 res = STG_E_FILENOTFOUND;
452 TRACE("<-- IStream %p\n", *ppstm);
453 TRACE("<-- %08lx\n", res);
457 /************************************************************************
458 * Storage32BaseImpl_OpenStorage (IStorage)
460 * This method will open a new storage object from the current storage.
462 * See Windows documentation for more details on IStorage methods.
464 HRESULT WINAPI StorageBaseImpl_OpenStorage(
466 const OLECHAR* pwcsName, /* [string][unique][in] */
467 IStorage* pstgPriority, /* [unique][in] */
468 DWORD grfMode, /* [in] */
469 SNB snbExclude, /* [unique][in] */
470 DWORD reserved, /* [in] */
471 IStorage** ppstg) /* [out] */
473 StorageBaseImpl *This = (StorageBaseImpl *)iface;
474 StorageInternalImpl* newStorage;
475 IEnumSTATSTGImpl* propertyEnumeration;
476 StgProperty currentProperty;
477 ULONG foundPropertyIndex;
478 HRESULT res = STG_E_UNKNOWN;
480 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
481 iface, debugstr_w(pwcsName), pstgPriority,
482 grfMode, snbExclude, reserved, ppstg);
485 * Perform a sanity check on the parameters.
487 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
494 if (snbExclude != NULL)
496 res = STG_E_INVALIDPARAMETER;
501 * Validate the STGM flags
503 if ( FAILED( validateSTGM(grfMode) ))
505 res = STG_E_INVALIDFLAG;
512 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
513 (grfMode & STGM_DELETEONRELEASE) ||
514 (grfMode & STGM_PRIORITY) )
516 res = STG_E_INVALIDFUNCTION;
521 * Initialize the out parameter
526 * Create a property enumeration to search the properties
528 propertyEnumeration = IEnumSTATSTGImpl_Construct(
529 This->ancestorStorage,
530 This->rootPropertySetIndex);
533 * Search the enumeration for the property with the given name
535 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
541 * Delete the property enumeration since we don't need it anymore
543 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
546 * If it was found, construct the stream object and return a pointer to it.
548 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
549 (currentProperty.propertyType==PROPTYPE_STORAGE) )
552 * Construct a new Storage object
554 newStorage = StorageInternalImpl_Construct(
555 This->ancestorStorage,
560 *ppstg = (IStorage*)newStorage;
563 * Since we are returning a pointer to the interface,
564 * we have to nail down the reference.
566 StorageBaseImpl_AddRef(*ppstg);
572 res = STG_E_INSUFFICIENTMEMORY;
576 res = STG_E_FILENOTFOUND;
579 TRACE("<-- %08lx\n", res);
583 /************************************************************************
584 * Storage32BaseImpl_EnumElements (IStorage)
586 * This method will create an enumerator object that can be used to
587 * retrieve informatino about all the properties in the storage object.
589 * See Windows documentation for more details on IStorage methods.
591 HRESULT WINAPI StorageBaseImpl_EnumElements(
593 DWORD reserved1, /* [in] */
594 void* reserved2, /* [size_is][unique][in] */
595 DWORD reserved3, /* [in] */
596 IEnumSTATSTG** ppenum) /* [out] */
598 StorageBaseImpl *This = (StorageBaseImpl *)iface;
599 IEnumSTATSTGImpl* newEnum;
601 TRACE("(%p, %ld, %p, %ld, %p)\n",
602 iface, reserved1, reserved2, reserved3, ppenum);
605 * Perform a sanity check on the parameters.
607 if ( (This==0) || (ppenum==0))
611 * Construct the enumerator.
613 newEnum = IEnumSTATSTGImpl_Construct(
614 This->ancestorStorage,
615 This->rootPropertySetIndex);
619 *ppenum = (IEnumSTATSTG*)newEnum;
622 * Don't forget to nail down a reference to the new object before
625 IEnumSTATSTGImpl_AddRef(*ppenum);
630 return E_OUTOFMEMORY;
633 /************************************************************************
634 * Storage32BaseImpl_Stat (IStorage)
636 * This method will retrieve information about this storage object.
638 * See Windows documentation for more details on IStorage methods.
640 HRESULT WINAPI StorageBaseImpl_Stat(
642 STATSTG* pstatstg, /* [out] */
643 DWORD grfStatFlag) /* [in] */
645 StorageBaseImpl *This = (StorageBaseImpl *)iface;
646 StgProperty curProperty;
648 HRESULT res = STG_E_UNKNOWN;
650 TRACE("(%p, %p, %lx)\n",
651 iface, pstatstg, grfStatFlag);
654 * Perform a sanity check on the parameters.
656 if ( (This==0) || (pstatstg==0))
663 * Read the information from the property.
665 readSuccessful = StorageImpl_ReadProperty(
666 This->ancestorStorage,
667 This->rootPropertySetIndex,
672 StorageUtl_CopyPropertyToSTATSTG(
686 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);
688 TRACE("<-- %08lx\n", res);
692 /************************************************************************
693 * Storage32BaseImpl_RenameElement (IStorage)
695 * This method will rename the specified element.
697 * See Windows documentation for more details on IStorage methods.
699 * Implementation notes: The method used to rename consists of creating a clone
700 * of the deleted StgProperty object setting it with the new name and to
701 * perform a DestroyElement of the old StgProperty.
703 HRESULT WINAPI StorageBaseImpl_RenameElement(
705 const OLECHAR* pwcsOldName, /* [in] */
706 const OLECHAR* pwcsNewName) /* [in] */
708 StorageBaseImpl *This = (StorageBaseImpl *)iface;
709 IEnumSTATSTGImpl* propertyEnumeration;
710 StgProperty currentProperty;
711 ULONG foundPropertyIndex;
713 TRACE("(%p, %s, %s)\n",
714 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
717 * Create a property enumeration to search the properties
719 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
720 This->rootPropertySetIndex);
723 * Search the enumeration for the new property name
725 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
729 if (foundPropertyIndex != PROPERTY_NULL)
732 * There is already a property with the new name
734 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
735 return STG_E_FILEALREADYEXISTS;
738 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
741 * Search the enumeration for the old property name
743 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
748 * Delete the property enumeration since we don't need it anymore
750 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
752 if (foundPropertyIndex != PROPERTY_NULL)
754 StgProperty renamedProperty;
755 ULONG renamedPropertyIndex;
758 * Setup a new property for the renamed property
760 renamedProperty.sizeOfNameString =
761 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
763 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
764 return STG_E_INVALIDNAME;
766 strcpyW(renamedProperty.name, pwcsNewName);
768 renamedProperty.propertyType = currentProperty.propertyType;
769 renamedProperty.startingBlock = currentProperty.startingBlock;
770 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
771 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
773 renamedProperty.previousProperty = PROPERTY_NULL;
774 renamedProperty.nextProperty = PROPERTY_NULL;
777 * Bring the dirProperty link in case it is a storage and in which
778 * case the renamed storage elements don't require to be reorganized.
780 renamedProperty.dirProperty = currentProperty.dirProperty;
782 /* call CoFileTime to get the current time
783 renamedProperty.timeStampS1
784 renamedProperty.timeStampD1
785 renamedProperty.timeStampS2
786 renamedProperty.timeStampD2
787 renamedProperty.propertyUniqueID
791 * Obtain a free property in the property chain
793 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
796 * Save the new property into the new property spot
798 StorageImpl_WriteProperty(
799 This->ancestorStorage,
800 renamedPropertyIndex,
804 * Find a spot in the property chain for our newly created property.
808 renamedPropertyIndex,
812 * At this point the renamed property has been inserted in the tree,
813 * now, before to Destroy the old property we must zeroed it's dirProperty
814 * otherwise the DestroyProperty below will zap it all and we do not want
816 * Also, we fake that the old property is a storage so the DestroyProperty
817 * will not do a SetSize(0) on the stream data.
819 * This means that we need to tweek the StgProperty if it is a stream or a
822 StorageImpl_ReadProperty(This->ancestorStorage,
826 currentProperty.dirProperty = PROPERTY_NULL;
827 currentProperty.propertyType = PROPTYPE_STORAGE;
828 StorageImpl_WriteProperty(
829 This->ancestorStorage,
834 * Invoke Destroy to get rid of the ole property and automatically redo
835 * the linking of it's previous and next members...
837 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
843 * There is no property with the old name
845 return STG_E_FILENOTFOUND;
851 /************************************************************************
852 * Storage32BaseImpl_CreateStream (IStorage)
854 * This method will create a stream object within this storage
856 * See Windows documentation for more details on IStorage methods.
858 HRESULT WINAPI StorageBaseImpl_CreateStream(
860 const OLECHAR* pwcsName, /* [string][in] */
861 DWORD grfMode, /* [in] */
862 DWORD reserved1, /* [in] */
863 DWORD reserved2, /* [in] */
864 IStream** ppstm) /* [out] */
866 StorageBaseImpl *This = (StorageBaseImpl *)iface;
867 IEnumSTATSTGImpl* propertyEnumeration;
868 StgStreamImpl* newStream;
869 StgProperty currentProperty, newStreamProperty;
870 ULONG foundPropertyIndex, newPropertyIndex;
872 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
873 iface, debugstr_w(pwcsName), grfMode,
874 reserved1, reserved2, ppstm);
877 * Validate parameters
880 return STG_E_INVALIDPOINTER;
883 return STG_E_INVALIDNAME;
885 if (reserved1 || reserved2)
886 return STG_E_INVALIDPARAMETER;
889 * Validate the STGM flags
891 if ( FAILED( validateSTGM(grfMode) ))
892 return STG_E_INVALIDFLAG;
894 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
895 return STG_E_INVALIDFLAG;
900 if ((grfMode & STGM_DELETEONRELEASE) ||
901 (grfMode & STGM_TRANSACTED))
902 return STG_E_INVALIDFUNCTION;
905 * Initialize the out parameter
910 * Create a property enumeration to search the properties
912 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
913 This->rootPropertySetIndex);
915 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
919 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
921 if (foundPropertyIndex != PROPERTY_NULL)
924 * An element with this name already exists
926 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
928 IStorage_DestroyElement(iface, pwcsName);
931 return STG_E_FILEALREADYEXISTS;
935 * memset the empty property
937 memset(&newStreamProperty, 0, sizeof(StgProperty));
939 newStreamProperty.sizeOfNameString =
940 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
942 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
943 return STG_E_INVALIDNAME;
945 strcpyW(newStreamProperty.name, pwcsName);
947 newStreamProperty.propertyType = PROPTYPE_STREAM;
948 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
949 newStreamProperty.size.u.LowPart = 0;
950 newStreamProperty.size.u.HighPart = 0;
952 newStreamProperty.previousProperty = PROPERTY_NULL;
953 newStreamProperty.nextProperty = PROPERTY_NULL;
954 newStreamProperty.dirProperty = PROPERTY_NULL;
956 /* call CoFileTime to get the current time
957 newStreamProperty.timeStampS1
958 newStreamProperty.timeStampD1
959 newStreamProperty.timeStampS2
960 newStreamProperty.timeStampD2
963 /* newStreamProperty.propertyUniqueID */
966 * Get a free property or create a new one
968 newPropertyIndex = getFreeProperty(This->ancestorStorage);
971 * Save the new property into the new property spot
973 StorageImpl_WriteProperty(
974 This->ancestorStorage,
979 * Find a spot in the property chain for our newly created property.
987 * Open the stream to return it.
989 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
993 *ppstm = (IStream*)newStream;
996 * Since we are returning a pointer to the interface, we have to nail down
999 StgStreamImpl_AddRef(*ppstm);
1003 return STG_E_INSUFFICIENTMEMORY;
1009 /************************************************************************
1010 * Storage32BaseImpl_SetClass (IStorage)
1012 * This method will write the specified CLSID in the property of this
1015 * See Windows documentation for more details on IStorage methods.
1017 HRESULT WINAPI StorageBaseImpl_SetClass(
1019 REFCLSID clsid) /* [in] */
1021 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1022 HRESULT hRes = E_FAIL;
1023 StgProperty curProperty;
1026 TRACE("(%p, %p)\n", iface, clsid);
1028 success = StorageImpl_ReadProperty(This->ancestorStorage,
1029 This->rootPropertySetIndex,
1033 curProperty.propertyUniqueID = *clsid;
1035 success = StorageImpl_WriteProperty(This->ancestorStorage,
1036 This->rootPropertySetIndex,
1045 /************************************************************************
1046 ** Storage32Impl implementation
1049 /************************************************************************
1050 * Storage32Impl_CreateStorage (IStorage)
1052 * This method will create the storage object within the provided storage.
1054 * See Windows documentation for more details on IStorage methods.
1056 HRESULT WINAPI StorageImpl_CreateStorage(
1058 const OLECHAR *pwcsName, /* [string][in] */
1059 DWORD grfMode, /* [in] */
1060 DWORD reserved1, /* [in] */
1061 DWORD reserved2, /* [in] */
1062 IStorage **ppstg) /* [out] */
1064 StorageImpl* const This=(StorageImpl*)iface;
1066 IEnumSTATSTGImpl *propertyEnumeration;
1067 StgProperty currentProperty;
1068 StgProperty newProperty;
1069 ULONG foundPropertyIndex;
1070 ULONG newPropertyIndex;
1073 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1074 iface, debugstr_w(pwcsName), grfMode,
1075 reserved1, reserved2, ppstg);
1078 * Validate parameters
1081 return STG_E_INVALIDPOINTER;
1084 return STG_E_INVALIDNAME;
1087 * Validate the STGM flags
1089 if ( FAILED( validateSTGM(grfMode) ) ||
1090 (grfMode & STGM_DELETEONRELEASE) )
1091 return STG_E_INVALIDFLAG;
1094 * Initialize the out parameter
1099 * Create a property enumeration and search the properties
1101 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1102 This->base.rootPropertySetIndex);
1104 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1107 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1109 if (foundPropertyIndex != PROPERTY_NULL)
1112 * An element with this name already exists
1114 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1115 IStorage_DestroyElement(iface, pwcsName);
1117 return STG_E_FILEALREADYEXISTS;
1121 * memset the empty property
1123 memset(&newProperty, 0, sizeof(StgProperty));
1125 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1127 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1128 return STG_E_INVALIDNAME;
1130 strcpyW(newProperty.name, pwcsName);
1132 newProperty.propertyType = PROPTYPE_STORAGE;
1133 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1134 newProperty.size.u.LowPart = 0;
1135 newProperty.size.u.HighPart = 0;
1137 newProperty.previousProperty = PROPERTY_NULL;
1138 newProperty.nextProperty = PROPERTY_NULL;
1139 newProperty.dirProperty = PROPERTY_NULL;
1141 /* call CoFileTime to get the current time
1142 newProperty.timeStampS1
1143 newProperty.timeStampD1
1144 newProperty.timeStampS2
1145 newProperty.timeStampD2
1148 /* newStorageProperty.propertyUniqueID */
1151 * Obtain a free property in the property chain
1153 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1156 * Save the new property into the new property spot
1158 StorageImpl_WriteProperty(
1159 This->base.ancestorStorage,
1164 * Find a spot in the property chain for our newly created property.
1166 updatePropertyChain(
1172 * Open it to get a pointer to return.
1174 hr = IStorage_OpenStorage(
1176 (const OLECHAR*)pwcsName,
1183 if( (hr != S_OK) || (*ppstg == NULL))
1193 /***************************************************************************
1197 * Get a free property or create a new one.
1199 static ULONG getFreeProperty(
1200 StorageImpl *storage)
1202 ULONG currentPropertyIndex = 0;
1203 ULONG newPropertyIndex = PROPERTY_NULL;
1204 BOOL readSuccessful = TRUE;
1205 StgProperty currentProperty;
1210 * Start by reading the root property
1212 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1213 currentPropertyIndex,
1217 if (currentProperty.sizeOfNameString == 0)
1220 * The property existis and is available, we found it.
1222 newPropertyIndex = currentPropertyIndex;
1228 * We exhausted the property list, we will create more space below
1230 newPropertyIndex = currentPropertyIndex;
1232 currentPropertyIndex++;
1234 } while (newPropertyIndex == PROPERTY_NULL);
1237 * grow the property chain
1239 if (! readSuccessful)
1241 StgProperty emptyProperty;
1242 ULARGE_INTEGER newSize;
1243 ULONG propertyIndex;
1244 ULONG lastProperty = 0;
1245 ULONG blockCount = 0;
1248 * obtain the new count of property blocks
1250 blockCount = BlockChainStream_GetCount(
1251 storage->base.ancestorStorage->rootBlockChain)+1;
1254 * initialize the size used by the property stream
1256 newSize.u.HighPart = 0;
1257 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1260 * add a property block to the property chain
1262 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1265 * memset the empty property in order to initialize the unused newly
1268 memset(&emptyProperty, 0, sizeof(StgProperty));
1273 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1276 propertyIndex = newPropertyIndex;
1277 propertyIndex < lastProperty;
1280 StorageImpl_WriteProperty(
1281 storage->base.ancestorStorage,
1287 return newPropertyIndex;
1290 /****************************************************************************
1294 * Case insensitive comparaison of StgProperty.name by first considering
1297 * Returns <0 when newPrpoerty < currentProperty
1298 * >0 when newPrpoerty > currentProperty
1299 * 0 when newPrpoerty == currentProperty
1301 static LONG propertyNameCmp(
1302 const OLECHAR *newProperty,
1303 const OLECHAR *currentProperty)
1305 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1310 * We compare the string themselves only when they are of the same length
1312 diff = lstrcmpiW( newProperty, currentProperty);
1318 /****************************************************************************
1322 * Properly link this new element in the property chain.
1324 static void updatePropertyChain(
1325 StorageImpl *storage,
1326 ULONG newPropertyIndex,
1327 StgProperty newProperty)
1329 StgProperty currentProperty;
1332 * Read the root property
1334 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1335 storage->base.rootPropertySetIndex,
1338 if (currentProperty.dirProperty != PROPERTY_NULL)
1341 * The root storage contains some element, therefore, start the research
1342 * for the appropriate location.
1345 ULONG current, next, previous, currentPropertyId;
1348 * Keep the StgProperty sequence number of the storage first property
1350 currentPropertyId = currentProperty.dirProperty;
1355 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1356 currentProperty.dirProperty,
1359 previous = currentProperty.previousProperty;
1360 next = currentProperty.nextProperty;
1361 current = currentPropertyId;
1365 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1369 if (previous != PROPERTY_NULL)
1371 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1378 currentProperty.previousProperty = newPropertyIndex;
1379 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1387 if (next != PROPERTY_NULL)
1389 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1396 currentProperty.nextProperty = newPropertyIndex;
1397 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1406 * Trying to insert an item with the same name in the
1407 * subtree structure.
1412 previous = currentProperty.previousProperty;
1413 next = currentProperty.nextProperty;
1419 * The root storage is empty, link the new property to it's dir property
1421 currentProperty.dirProperty = newPropertyIndex;
1422 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1423 storage->base.rootPropertySetIndex,
1429 /*************************************************************************
1432 HRESULT WINAPI StorageImpl_CopyTo(
1434 DWORD ciidExclude, /* [in] */
1435 const IID* rgiidExclude, /* [size_is][unique][in] */
1436 SNB snbExclude, /* [unique][in] */
1437 IStorage* pstgDest) /* [unique][in] */
1439 IEnumSTATSTG *elements = 0;
1440 STATSTG curElement, strStat;
1442 IStorage *pstgTmp, *pstgChild;
1443 IStream *pstrTmp, *pstrChild;
1445 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1446 FIXME("Exclude option not implemented\n");
1448 TRACE("(%p, %ld, %p, %p, %p)\n",
1449 iface, ciidExclude, rgiidExclude,
1450 snbExclude, pstgDest);
1453 * Perform a sanity check
1455 if ( pstgDest == 0 )
1456 return STG_E_INVALIDPOINTER;
1459 * Enumerate the elements
1461 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1469 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1470 IStorage_SetClass( pstgDest, &curElement.clsid );
1475 * Obtain the next element
1477 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1479 if ( hr == S_FALSE )
1481 hr = S_OK; /* done, every element has been copied */
1485 if (curElement.type == STGTY_STORAGE)
1488 * open child source storage
1490 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1491 STGM_READ|STGM_SHARE_EXCLUSIVE,
1492 NULL, 0, &pstgChild );
1498 * Check if destination storage is not a child of the source
1499 * storage, which will cause an infinite loop
1501 if (pstgChild == pstgDest)
1503 IEnumSTATSTG_Release(elements);
1505 return STG_E_ACCESSDENIED;
1509 * create a new storage in destination storage
1511 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1512 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1516 * if it already exist, don't create a new one use this one
1518 if (hr == STG_E_FILEALREADYEXISTS)
1520 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1521 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1522 NULL, 0, &pstgTmp );
1530 * do the copy recursively
1532 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1533 snbExclude, pstgTmp );
1535 IStorage_Release( pstgTmp );
1536 IStorage_Release( pstgChild );
1538 else if (curElement.type == STGTY_STREAM)
1541 * create a new stream in destination storage. If the stream already
1542 * exist, it will be deleted and a new one will be created.
1544 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1545 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1552 * open child stream storage
1554 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1555 STGM_READ|STGM_SHARE_EXCLUSIVE,
1562 * Get the size of the source stream
1564 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1567 * Set the size of the destination stream.
1569 IStream_SetSize(pstrTmp, strStat.cbSize);
1574 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1577 IStream_Release( pstrTmp );
1578 IStream_Release( pstrChild );
1582 WARN("unknown element type: %ld\n", curElement.type);
1585 } while (hr == S_OK);
1590 IEnumSTATSTG_Release(elements);
1595 /*************************************************************************
1596 * MoveElementTo (IStorage)
1598 HRESULT WINAPI StorageImpl_MoveElementTo(
1600 const OLECHAR *pwcsName, /* [string][in] */
1601 IStorage *pstgDest, /* [unique][in] */
1602 const OLECHAR *pwcsNewName,/* [string][in] */
1603 DWORD grfFlags) /* [in] */
1605 FIXME("not implemented!\n");
1609 /*************************************************************************
1612 HRESULT WINAPI StorageImpl_Commit(
1614 DWORD grfCommitFlags)/* [in] */
1616 FIXME("(%ld): stub!\n", grfCommitFlags);
1620 /*************************************************************************
1623 HRESULT WINAPI StorageImpl_Revert(
1626 FIXME("not implemented!\n");
1630 /*************************************************************************
1631 * DestroyElement (IStorage)
1633 * Stategy: This implementation is build this way for simplicity not for speed.
1634 * I always delete the top most element of the enumeration and adjust
1635 * the deleted element pointer all the time. This takes longer to
1636 * do but allow to reinvoke DestroyElement whenever we encounter a
1637 * storage object. The optimisation reside in the usage of another
1638 * enumeration stategy that would give all the leaves of a storage
1639 * first. (postfix order)
1641 HRESULT WINAPI StorageImpl_DestroyElement(
1643 const OLECHAR *pwcsName)/* [string][in] */
1645 StorageImpl* const This=(StorageImpl*)iface;
1647 IEnumSTATSTGImpl* propertyEnumeration;
1650 StgProperty propertyToDelete;
1651 StgProperty parentProperty;
1652 ULONG foundPropertyIndexToDelete;
1653 ULONG typeOfRelation;
1654 ULONG parentPropertyId;
1657 iface, debugstr_w(pwcsName));
1660 * Perform a sanity check on the parameters.
1663 return STG_E_INVALIDPOINTER;
1666 * Create a property enumeration to search the property with the given name
1668 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1669 This->base.ancestorStorage,
1670 This->base.rootPropertySetIndex);
1672 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1673 propertyEnumeration,
1677 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1679 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1681 return STG_E_FILENOTFOUND;
1685 * Find the parent property of the property to delete (the one that
1686 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1687 * the parent is This. Otherwise, the parent is one of it's sibling...
1691 * First, read This's StgProperty..
1693 res = StorageImpl_ReadProperty(
1694 This->base.ancestorStorage,
1695 This->base.rootPropertySetIndex,
1701 * Second, check to see if by any chance the actual storage (This) is not
1702 * the parent of the property to delete... We never know...
1704 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1707 * Set data as it would have been done in the else part...
1709 typeOfRelation = PROPERTY_RELATION_DIR;
1710 parentPropertyId = This->base.rootPropertySetIndex;
1715 * Create a property enumeration to search the parent properties, and
1716 * delete it once done.
1718 IEnumSTATSTGImpl* propertyEnumeration2;
1720 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1721 This->base.ancestorStorage,
1722 This->base.rootPropertySetIndex);
1724 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1725 propertyEnumeration2,
1726 foundPropertyIndexToDelete,
1730 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1733 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1735 hr = deleteStorageProperty(
1737 foundPropertyIndexToDelete,
1740 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1742 hr = deleteStreamProperty(
1744 foundPropertyIndexToDelete,
1752 * Adjust the property chain
1754 hr = adjustPropertyChain(
1765 /************************************************************************
1766 * StorageImpl_Stat (IStorage)
1768 * This method will retrieve information about this storage object.
1770 * See Windows documentation for more details on IStorage methods.
1772 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1773 STATSTG* pstatstg, /* [out] */
1774 DWORD grfStatFlag) /* [in] */
1776 StorageImpl* const This = (StorageImpl*)iface;
1777 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1779 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1781 CoTaskMemFree(pstatstg->pwcsName);
1782 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1783 strcpyW(pstatstg->pwcsName, This->pwcsName);
1791 /*********************************************************************
1795 * Perform the deletion of a complete storage node
1798 static HRESULT deleteStorageProperty(
1799 StorageImpl *parentStorage,
1800 ULONG indexOfPropertyToDelete,
1801 StgProperty propertyToDelete)
1803 IEnumSTATSTG *elements = 0;
1804 IStorage *childStorage = 0;
1805 STATSTG currentElement;
1807 HRESULT destroyHr = S_OK;
1810 * Open the storage and enumerate it
1812 hr = StorageBaseImpl_OpenStorage(
1813 (IStorage*)parentStorage,
1814 propertyToDelete.name,
1816 STGM_SHARE_EXCLUSIVE,
1827 * Enumerate the elements
1829 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1834 * Obtain the next element
1836 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1839 destroyHr = StorageImpl_DestroyElement(
1840 (IStorage*)childStorage,
1841 (OLECHAR*)currentElement.pwcsName);
1843 CoTaskMemFree(currentElement.pwcsName);
1847 * We need to Reset the enumeration every time because we delete elements
1848 * and the enumeration could be invalid
1850 IEnumSTATSTG_Reset(elements);
1852 } while ((hr == S_OK) && (destroyHr == S_OK));
1855 * Invalidate the property by zeroing it's name member.
1857 propertyToDelete.sizeOfNameString = 0;
1859 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1860 indexOfPropertyToDelete,
1863 IStorage_Release(childStorage);
1864 IEnumSTATSTG_Release(elements);
1869 /*********************************************************************
1873 * Perform the deletion of a stream node
1876 static HRESULT deleteStreamProperty(
1877 StorageImpl *parentStorage,
1878 ULONG indexOfPropertyToDelete,
1879 StgProperty propertyToDelete)
1883 ULARGE_INTEGER size;
1885 size.u.HighPart = 0;
1888 hr = StorageBaseImpl_OpenStream(
1889 (IStorage*)parentStorage,
1890 (OLECHAR*)propertyToDelete.name,
1892 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1904 hr = IStream_SetSize(pis, size);
1912 * Release the stream object.
1914 IStream_Release(pis);
1917 * Invalidate the property by zeroing it's name member.
1919 propertyToDelete.sizeOfNameString = 0;
1922 * Here we should re-read the property so we get the updated pointer
1923 * but since we are here to zap it, I don't do it...
1925 StorageImpl_WriteProperty(
1926 parentStorage->base.ancestorStorage,
1927 indexOfPropertyToDelete,
1933 /*********************************************************************
1937 * Finds a placeholder for the StgProperty within the Storage
1940 static HRESULT findPlaceholder(
1941 StorageImpl *storage,
1942 ULONG propertyIndexToStore,
1943 ULONG storePropertyIndex,
1946 StgProperty storeProperty;
1951 * Read the storage property
1953 res = StorageImpl_ReadProperty(
1954 storage->base.ancestorStorage,
1963 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1965 if (storeProperty.previousProperty != PROPERTY_NULL)
1967 return findPlaceholder(
1969 propertyIndexToStore,
1970 storeProperty.previousProperty,
1975 storeProperty.previousProperty = propertyIndexToStore;
1978 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1980 if (storeProperty.nextProperty != PROPERTY_NULL)
1982 return findPlaceholder(
1984 propertyIndexToStore,
1985 storeProperty.nextProperty,
1990 storeProperty.nextProperty = propertyIndexToStore;
1993 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1995 if (storeProperty.dirProperty != PROPERTY_NULL)
1997 return findPlaceholder(
1999 propertyIndexToStore,
2000 storeProperty.dirProperty,
2005 storeProperty.dirProperty = propertyIndexToStore;
2009 hr = StorageImpl_WriteProperty(
2010 storage->base.ancestorStorage,
2022 /*************************************************************************
2026 * This method takes the previous and the next property link of a property
2027 * to be deleted and find them a place in the Storage.
2029 static HRESULT adjustPropertyChain(
2031 StgProperty propertyToDelete,
2032 StgProperty parentProperty,
2033 ULONG parentPropertyId,
2036 ULONG newLinkProperty = PROPERTY_NULL;
2037 BOOL needToFindAPlaceholder = FALSE;
2038 ULONG storeNode = PROPERTY_NULL;
2039 ULONG toStoreNode = PROPERTY_NULL;
2040 INT relationType = 0;
2044 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2046 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2049 * Set the parent previous to the property to delete previous
2051 newLinkProperty = propertyToDelete.previousProperty;
2053 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2056 * We also need to find a storage for the other link, setup variables
2057 * to do this at the end...
2059 needToFindAPlaceholder = TRUE;
2060 storeNode = propertyToDelete.previousProperty;
2061 toStoreNode = propertyToDelete.nextProperty;
2062 relationType = PROPERTY_RELATION_NEXT;
2065 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2068 * Set the parent previous to the property to delete next
2070 newLinkProperty = propertyToDelete.nextProperty;
2074 * Link it for real...
2076 parentProperty.previousProperty = newLinkProperty;
2079 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2081 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2084 * Set the parent next to the property to delete next previous
2086 newLinkProperty = propertyToDelete.previousProperty;
2088 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2091 * We also need to find a storage for the other link, setup variables
2092 * to do this at the end...
2094 needToFindAPlaceholder = TRUE;
2095 storeNode = propertyToDelete.previousProperty;
2096 toStoreNode = propertyToDelete.nextProperty;
2097 relationType = PROPERTY_RELATION_NEXT;
2100 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2103 * Set the parent next to the property to delete next
2105 newLinkProperty = propertyToDelete.nextProperty;
2109 * Link it for real...
2111 parentProperty.nextProperty = newLinkProperty;
2113 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2115 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2118 * Set the parent dir to the property to delete previous
2120 newLinkProperty = propertyToDelete.previousProperty;
2122 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2125 * We also need to find a storage for the other link, setup variables
2126 * to do this at the end...
2128 needToFindAPlaceholder = TRUE;
2129 storeNode = propertyToDelete.previousProperty;
2130 toStoreNode = propertyToDelete.nextProperty;
2131 relationType = PROPERTY_RELATION_NEXT;
2134 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2137 * Set the parent dir to the property to delete next
2139 newLinkProperty = propertyToDelete.nextProperty;
2143 * Link it for real...
2145 parentProperty.dirProperty = newLinkProperty;
2149 * Write back the parent property
2151 res = StorageImpl_WriteProperty(
2152 This->base.ancestorStorage,
2161 * If a placeholder is required for the other link, then, find one and
2162 * get out of here...
2164 if (needToFindAPlaceholder)
2166 hr = findPlaceholder(
2177 /******************************************************************************
2178 * SetElementTimes (IStorage)
2180 HRESULT WINAPI StorageImpl_SetElementTimes(
2182 const OLECHAR *pwcsName,/* [string][in] */
2183 const FILETIME *pctime, /* [in] */
2184 const FILETIME *patime, /* [in] */
2185 const FILETIME *pmtime) /* [in] */
2187 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2191 /******************************************************************************
2192 * SetStateBits (IStorage)
2194 HRESULT WINAPI StorageImpl_SetStateBits(
2196 DWORD grfStateBits,/* [in] */
2197 DWORD grfMask) /* [in] */
2199 FIXME("not implemented!\n");
2203 HRESULT StorageImpl_Construct(
2213 StgProperty currentProperty;
2214 BOOL readSuccessful;
2215 ULONG currentPropertyIndex;
2217 if ( FAILED( validateSTGM(openFlags) ))
2218 return STG_E_INVALIDFLAG;
2220 memset(This, 0, sizeof(StorageImpl));
2223 * Initialize the virtual function table.
2225 This->base.lpVtbl = &Storage32Impl_Vtbl;
2226 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2227 This->base.v_destructor = &StorageImpl_Destroy;
2230 * This is the top-level storage so initialize the ancestor pointer
2233 This->base.ancestorStorage = This;
2236 * Initialize the physical support of the storage.
2238 This->hFile = hFile;
2241 * Store copy of file path.
2244 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2245 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2246 if (!This->pwcsName)
2247 return STG_E_INSUFFICIENTMEMORY;
2248 strcpyW(This->pwcsName, pwcsName);
2252 * Initialize the big block cache.
2254 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2255 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2256 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2262 if (This->bigBlockFile == 0)
2267 ULARGE_INTEGER size;
2268 BYTE* bigBlockBuffer;
2271 * Initialize all header variables:
2272 * - The big block depot consists of one block and it is at block 0
2273 * - The properties start at block 1
2274 * - There is no small block depot
2276 memset( This->bigBlockDepotStart,
2278 sizeof(This->bigBlockDepotStart));
2280 This->bigBlockDepotCount = 1;
2281 This->bigBlockDepotStart[0] = 0;
2282 This->rootStartBlock = 1;
2283 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2284 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2285 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2286 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2287 This->extBigBlockDepotCount = 0;
2289 StorageImpl_SaveFileHeader(This);
2292 * Add one block for the big block depot and one block for the properties
2294 size.u.HighPart = 0;
2295 size.u.LowPart = This->bigBlockSize * 3;
2296 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2299 * Initialize the big block depot
2301 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2302 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2303 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2304 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2305 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2310 * Load the header for the file.
2312 hr = StorageImpl_LoadFileHeader(This);
2316 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2323 * There is no block depot cached yet.
2325 This->indexBlockDepotCached = 0xFFFFFFFF;
2328 * Start searching for free blocks with block 0.
2330 This->prevFreeBlock = 0;
2333 * Create the block chain abstractions.
2335 if(!(This->rootBlockChain =
2336 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2337 return STG_E_READFAULT;
2339 if(!(This->smallBlockDepotChain =
2340 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2342 return STG_E_READFAULT;
2345 * Write the root property
2349 StgProperty rootProp;
2351 * Initialize the property chain
2353 memset(&rootProp, 0, sizeof(rootProp));
2354 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2355 sizeof(rootProp.name)/sizeof(WCHAR) );
2356 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2357 rootProp.propertyType = PROPTYPE_ROOT;
2358 rootProp.previousProperty = PROPERTY_NULL;
2359 rootProp.nextProperty = PROPERTY_NULL;
2360 rootProp.dirProperty = PROPERTY_NULL;
2361 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2362 rootProp.size.u.HighPart = 0;
2363 rootProp.size.u.LowPart = 0;
2365 StorageImpl_WriteProperty(This, 0, &rootProp);
2369 * Find the ID of the root in the property sets.
2371 currentPropertyIndex = 0;
2375 readSuccessful = StorageImpl_ReadProperty(
2377 currentPropertyIndex,
2382 if ( (currentProperty.sizeOfNameString != 0 ) &&
2383 (currentProperty.propertyType == PROPTYPE_ROOT) )
2385 This->base.rootPropertySetIndex = currentPropertyIndex;
2389 currentPropertyIndex++;
2391 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2393 if (!readSuccessful)
2396 return STG_E_READFAULT;
2400 * Create the block chain abstraction for the small block root chain.
2402 if(!(This->smallBlockRootChain =
2403 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2404 return STG_E_READFAULT;
2409 void StorageImpl_Destroy(StorageBaseImpl* iface)
2411 StorageImpl *This = (StorageImpl*) iface;
2412 TRACE("(%p)\n", This);
2414 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2416 BlockChainStream_Destroy(This->smallBlockRootChain);
2417 BlockChainStream_Destroy(This->rootBlockChain);
2418 BlockChainStream_Destroy(This->smallBlockDepotChain);
2420 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2424 /******************************************************************************
2425 * Storage32Impl_GetNextFreeBigBlock
2427 * Returns the index of the next free big block.
2428 * If the big block depot is filled, this method will enlarge it.
2431 ULONG StorageImpl_GetNextFreeBigBlock(
2434 ULONG depotBlockIndexPos;
2436 ULONG depotBlockOffset;
2437 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2438 ULONG nextBlockIndex = BLOCK_SPECIAL;
2440 ULONG freeBlock = BLOCK_UNUSED;
2442 depotIndex = This->prevFreeBlock / blocksPerDepot;
2443 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2446 * Scan the entire big block depot until we find a block marked free
2448 while (nextBlockIndex != BLOCK_UNUSED)
2450 if (depotIndex < COUNT_BBDEPOTINHEADER)
2452 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2455 * Grow the primary depot.
2457 if (depotBlockIndexPos == BLOCK_UNUSED)
2459 depotBlockIndexPos = depotIndex*blocksPerDepot;
2462 * Add a block depot.
2464 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2465 This->bigBlockDepotCount++;
2466 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2469 * Flag it as a block depot.
2471 StorageImpl_SetNextBlockInChain(This,
2475 /* Save new header information.
2477 StorageImpl_SaveFileHeader(This);
2482 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2484 if (depotBlockIndexPos == BLOCK_UNUSED)
2487 * Grow the extended depot.
2489 ULONG extIndex = BLOCK_UNUSED;
2490 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2491 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2493 if (extBlockOffset == 0)
2495 /* We need an extended block.
2497 extIndex = Storage32Impl_AddExtBlockDepot(This);
2498 This->extBigBlockDepotCount++;
2499 depotBlockIndexPos = extIndex + 1;
2502 depotBlockIndexPos = depotIndex * blocksPerDepot;
2505 * Add a block depot and mark it in the extended block.
2507 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2508 This->bigBlockDepotCount++;
2509 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2511 /* Flag the block depot.
2513 StorageImpl_SetNextBlockInChain(This,
2517 /* If necessary, flag the extended depot block.
2519 if (extIndex != BLOCK_UNUSED)
2520 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2522 /* Save header information.
2524 StorageImpl_SaveFileHeader(This);
2528 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2530 if (depotBuffer != 0)
2532 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2533 ( nextBlockIndex != BLOCK_UNUSED))
2535 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2537 if (nextBlockIndex == BLOCK_UNUSED)
2539 freeBlock = (depotIndex * blocksPerDepot) +
2540 (depotBlockOffset/sizeof(ULONG));
2543 depotBlockOffset += sizeof(ULONG);
2546 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2550 depotBlockOffset = 0;
2553 This->prevFreeBlock = freeBlock;
2558 /******************************************************************************
2559 * Storage32Impl_AddBlockDepot
2561 * This will create a depot block, essentially it is a block initialized
2564 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2568 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2571 * Initialize blocks as free
2573 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2575 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2578 /******************************************************************************
2579 * Storage32Impl_GetExtDepotBlock
2581 * Returns the index of the block that corresponds to the specified depot
2582 * index. This method is only for depot indexes equal or greater than
2583 * COUNT_BBDEPOTINHEADER.
2585 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2587 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2588 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2589 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2590 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2591 ULONG blockIndex = BLOCK_UNUSED;
2592 ULONG extBlockIndex = This->extBigBlockDepotStart;
2594 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2596 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2597 return BLOCK_UNUSED;
2599 while (extBlockCount > 0)
2601 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2605 if (extBlockIndex != BLOCK_UNUSED)
2609 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2611 if (depotBuffer != 0)
2613 StorageUtl_ReadDWord(depotBuffer,
2614 extBlockOffset * sizeof(ULONG),
2617 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2624 /******************************************************************************
2625 * Storage32Impl_SetExtDepotBlock
2627 * Associates the specified block index to the specified depot index.
2628 * This method is only for depot indexes equal or greater than
2629 * COUNT_BBDEPOTINHEADER.
2631 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2635 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2636 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2637 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2638 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2639 ULONG extBlockIndex = This->extBigBlockDepotStart;
2641 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2643 while (extBlockCount > 0)
2645 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2649 if (extBlockIndex != BLOCK_UNUSED)
2653 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2655 if (depotBuffer != 0)
2657 StorageUtl_WriteDWord(depotBuffer,
2658 extBlockOffset * sizeof(ULONG),
2661 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2666 /******************************************************************************
2667 * Storage32Impl_AddExtBlockDepot
2669 * Creates an extended depot block.
2671 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2673 ULONG numExtBlocks = This->extBigBlockDepotCount;
2674 ULONG nextExtBlock = This->extBigBlockDepotStart;
2675 BYTE* depotBuffer = NULL;
2676 ULONG index = BLOCK_UNUSED;
2677 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2678 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2679 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2681 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2682 blocksPerDepotBlock;
2684 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2687 * The first extended block.
2689 This->extBigBlockDepotStart = index;
2695 * Follow the chain to the last one.
2697 for (i = 0; i < (numExtBlocks - 1); i++)
2699 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2703 * Add the new extended block to the chain.
2705 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2706 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2707 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2711 * Initialize this block.
2713 depotBuffer = StorageImpl_GetBigBlock(This, index);
2714 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2715 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2720 /******************************************************************************
2721 * Storage32Impl_FreeBigBlock
2723 * This method will flag the specified block as free in the big block depot.
2725 void StorageImpl_FreeBigBlock(
2729 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2731 if (blockIndex < This->prevFreeBlock)
2732 This->prevFreeBlock = blockIndex;
2735 /************************************************************************
2736 * Storage32Impl_GetNextBlockInChain
2738 * This method will retrieve the block index of the next big block in
2741 * Params: This - Pointer to the Storage object.
2742 * blockIndex - Index of the block to retrieve the chain
2744 * nextBlockIndex - receives the return value.
2746 * Returns: This method returns the index of the next block in the chain.
2747 * It will return the constants:
2748 * BLOCK_SPECIAL - If the block given was not part of a
2750 * BLOCK_END_OF_CHAIN - If the block given was the last in
2752 * BLOCK_UNUSED - If the block given was not past of a chain
2754 * BLOCK_EXTBBDEPOT - This block is part of the extended
2757 * See Windows documentation for more details on IStorage methods.
2759 HRESULT StorageImpl_GetNextBlockInChain(
2762 ULONG* nextBlockIndex)
2764 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2765 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2766 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2768 ULONG depotBlockIndexPos;
2771 *nextBlockIndex = BLOCK_SPECIAL;
2773 if(depotBlockCount >= This->bigBlockDepotCount)
2775 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2776 This->bigBlockDepotCount);
2777 return STG_E_READFAULT;
2781 * Cache the currently accessed depot block.
2783 if (depotBlockCount != This->indexBlockDepotCached)
2785 This->indexBlockDepotCached = depotBlockCount;
2787 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2789 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2794 * We have to look in the extended depot.
2796 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2799 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2802 return STG_E_READFAULT;
2804 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2806 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2807 This->blockDepotCached[index] = *nextBlockIndex;
2809 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2812 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2817 /******************************************************************************
2818 * Storage32Impl_GetNextExtendedBlock
2820 * Given an extended block this method will return the next extended block.
2823 * The last ULONG of an extended block is the block index of the next
2824 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2828 * - The index of the next extended block
2829 * - BLOCK_UNUSED: there is no next extended block.
2830 * - Any other return values denotes failure.
2832 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2834 ULONG nextBlockIndex = BLOCK_SPECIAL;
2835 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2838 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2842 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2844 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2847 return nextBlockIndex;
2850 /******************************************************************************
2851 * Storage32Impl_SetNextBlockInChain
2853 * This method will write the index of the specified block's next block
2854 * in the big block depot.
2856 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2859 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2860 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2861 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2864 void StorageImpl_SetNextBlockInChain(
2869 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2870 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2871 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2872 ULONG depotBlockIndexPos;
2875 assert(depotBlockCount < This->bigBlockDepotCount);
2876 assert(blockIndex != nextBlock);
2878 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2880 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2885 * We have to look in the extended depot.
2887 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2890 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2894 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2895 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2899 * Update the cached block depot, if necessary.
2901 if (depotBlockCount == This->indexBlockDepotCached)
2903 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2907 /******************************************************************************
2908 * Storage32Impl_LoadFileHeader
2910 * This method will read in the file header, i.e. big block index -1.
2912 HRESULT StorageImpl_LoadFileHeader(
2915 HRESULT hr = STG_E_FILENOTFOUND;
2916 void* headerBigBlock = NULL;
2920 * Get a pointer to the big block of data containing the header.
2922 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2925 * Extract the information from the header.
2927 if (headerBigBlock!=0)
2930 * Check for the "magic number" signature and return an error if it is not
2933 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2935 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2936 return STG_E_OLDFORMAT;
2939 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2941 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2942 return STG_E_INVALIDHEADER;
2945 StorageUtl_ReadWord(
2947 OFFSET_BIGBLOCKSIZEBITS,
2948 &This->bigBlockSizeBits);
2950 StorageUtl_ReadWord(
2952 OFFSET_SMALLBLOCKSIZEBITS,
2953 &This->smallBlockSizeBits);
2955 StorageUtl_ReadDWord(
2957 OFFSET_BBDEPOTCOUNT,
2958 &This->bigBlockDepotCount);
2960 StorageUtl_ReadDWord(
2962 OFFSET_ROOTSTARTBLOCK,
2963 &This->rootStartBlock);
2965 StorageUtl_ReadDWord(
2967 OFFSET_SBDEPOTSTART,
2968 &This->smallBlockDepotStart);
2970 StorageUtl_ReadDWord(
2972 OFFSET_EXTBBDEPOTSTART,
2973 &This->extBigBlockDepotStart);
2975 StorageUtl_ReadDWord(
2977 OFFSET_EXTBBDEPOTCOUNT,
2978 &This->extBigBlockDepotCount);
2980 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2982 StorageUtl_ReadDWord(
2984 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2985 &(This->bigBlockDepotStart[index]));
2989 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2993 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2994 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2998 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2999 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3003 * Right now, the code is making some assumptions about the size of the
3004 * blocks, just make sure they are what we're expecting.
3006 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3007 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3009 WARN("Broken OLE storage file\n");
3010 hr = STG_E_INVALIDHEADER;
3016 * Release the block.
3018 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3024 /******************************************************************************
3025 * Storage32Impl_SaveFileHeader
3027 * This method will save to the file the header, i.e. big block -1.
3029 void StorageImpl_SaveFileHeader(
3032 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3037 * Get a pointer to the big block of data containing the header.
3039 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3042 * If the block read failed, the file is probably new.
3047 * Initialize for all unknown fields.
3049 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3052 * Initialize the magic number.
3054 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3057 * And a bunch of things we don't know what they mean
3059 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3060 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3061 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3062 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3066 * Write the information to the header.
3068 StorageUtl_WriteWord(
3070 OFFSET_BIGBLOCKSIZEBITS,
3071 This->bigBlockSizeBits);
3073 StorageUtl_WriteWord(
3075 OFFSET_SMALLBLOCKSIZEBITS,
3076 This->smallBlockSizeBits);
3078 StorageUtl_WriteDWord(
3080 OFFSET_BBDEPOTCOUNT,
3081 This->bigBlockDepotCount);
3083 StorageUtl_WriteDWord(
3085 OFFSET_ROOTSTARTBLOCK,
3086 This->rootStartBlock);
3088 StorageUtl_WriteDWord(
3090 OFFSET_SBDEPOTSTART,
3091 This->smallBlockDepotStart);
3093 StorageUtl_WriteDWord(
3095 OFFSET_SBDEPOTCOUNT,
3096 This->smallBlockDepotChain ?
3097 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3099 StorageUtl_WriteDWord(
3101 OFFSET_EXTBBDEPOTSTART,
3102 This->extBigBlockDepotStart);
3104 StorageUtl_WriteDWord(
3106 OFFSET_EXTBBDEPOTCOUNT,
3107 This->extBigBlockDepotCount);
3109 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3111 StorageUtl_WriteDWord(
3113 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3114 (This->bigBlockDepotStart[index]));
3118 * Write the big block back to the file.
3120 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3123 /******************************************************************************
3124 * Storage32Impl_ReadProperty
3126 * This method will read the specified property from the property chain.
3128 BOOL StorageImpl_ReadProperty(
3131 StgProperty* buffer)
3133 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3134 ULARGE_INTEGER offsetInPropSet;
3135 BOOL readSuccessful;
3138 offsetInPropSet.u.HighPart = 0;
3139 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3141 readSuccessful = BlockChainStream_ReadAt(
3142 This->rootBlockChain,
3150 /* replace the name of root entry (often "Root Entry") by the file name */
3151 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3152 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3154 memset(buffer->name, 0, sizeof(buffer->name));
3158 PROPERTY_NAME_BUFFER_LEN );
3159 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3161 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3163 StorageUtl_ReadWord(
3165 OFFSET_PS_NAMELENGTH,
3166 &buffer->sizeOfNameString);
3168 StorageUtl_ReadDWord(
3170 OFFSET_PS_PREVIOUSPROP,
3171 &buffer->previousProperty);
3173 StorageUtl_ReadDWord(
3176 &buffer->nextProperty);
3178 StorageUtl_ReadDWord(
3181 &buffer->dirProperty);
3183 StorageUtl_ReadGUID(
3186 &buffer->propertyUniqueID);
3188 StorageUtl_ReadDWord(
3191 &buffer->timeStampS1);
3193 StorageUtl_ReadDWord(
3196 &buffer->timeStampD1);
3198 StorageUtl_ReadDWord(
3201 &buffer->timeStampS2);
3203 StorageUtl_ReadDWord(
3206 &buffer->timeStampD2);
3208 StorageUtl_ReadDWord(
3210 OFFSET_PS_STARTBLOCK,
3211 &buffer->startingBlock);
3213 StorageUtl_ReadDWord(
3216 &buffer->size.u.LowPart);
3218 buffer->size.u.HighPart = 0;
3221 return readSuccessful;
3224 /*********************************************************************
3225 * Write the specified property into the property chain
3227 BOOL StorageImpl_WriteProperty(
3230 StgProperty* buffer)
3232 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3233 ULARGE_INTEGER offsetInPropSet;
3234 BOOL writeSuccessful;
3237 offsetInPropSet.u.HighPart = 0;
3238 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3240 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3243 currentProperty + OFFSET_PS_NAME,
3245 PROPERTY_NAME_BUFFER_LEN );
3247 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3249 StorageUtl_WriteWord(
3251 OFFSET_PS_NAMELENGTH,
3252 buffer->sizeOfNameString);
3254 StorageUtl_WriteDWord(
3256 OFFSET_PS_PREVIOUSPROP,
3257 buffer->previousProperty);
3259 StorageUtl_WriteDWord(
3262 buffer->nextProperty);
3264 StorageUtl_WriteDWord(
3267 buffer->dirProperty);
3269 StorageUtl_WriteGUID(
3272 &buffer->propertyUniqueID);
3274 StorageUtl_WriteDWord(
3277 buffer->timeStampS1);
3279 StorageUtl_WriteDWord(
3282 buffer->timeStampD1);
3284 StorageUtl_WriteDWord(
3287 buffer->timeStampS2);
3289 StorageUtl_WriteDWord(
3292 buffer->timeStampD2);
3294 StorageUtl_WriteDWord(
3296 OFFSET_PS_STARTBLOCK,
3297 buffer->startingBlock);
3299 StorageUtl_WriteDWord(
3302 buffer->size.u.LowPart);
3304 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3309 return writeSuccessful;
3312 BOOL StorageImpl_ReadBigBlock(
3317 void* bigBlockBuffer;
3319 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3321 if (bigBlockBuffer!=0)
3323 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3325 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3333 BOOL StorageImpl_WriteBigBlock(
3338 void* bigBlockBuffer;
3340 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3342 if (bigBlockBuffer!=0)
3344 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3346 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3354 void* StorageImpl_GetROBigBlock(
3358 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3361 void* StorageImpl_GetBigBlock(
3365 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3368 void StorageImpl_ReleaseBigBlock(
3372 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3375 /******************************************************************************
3376 * Storage32Impl_SmallBlocksToBigBlocks
3378 * This method will convert a small block chain to a big block chain.
3379 * The small block chain will be destroyed.
3381 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3383 SmallBlockChainStream** ppsbChain)
3385 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3386 ULARGE_INTEGER size, offset;
3387 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3388 ULONG propertyIndex;
3389 BOOL successRead, successWrite;
3390 StgProperty chainProperty;
3392 BlockChainStream *bbTempChain = NULL;
3393 BlockChainStream *bigBlockChain = NULL;
3396 * Create a temporary big block chain that doesn't have
3397 * an associated property. This temporary chain will be
3398 * used to copy data from small blocks to big blocks.
3400 bbTempChain = BlockChainStream_Construct(This,
3403 if(!bbTempChain) return NULL;
3405 * Grow the big block chain.
3407 size = SmallBlockChainStream_GetSize(*ppsbChain);
3408 BlockChainStream_SetSize(bbTempChain, size);
3411 * Copy the contents of the small block chain to the big block chain
3412 * by small block size increments.
3414 offset.u.LowPart = 0;
3415 offset.u.HighPart = 0;
3419 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3422 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3424 DEF_SMALL_BLOCK_SIZE,
3427 cbTotalRead += cbRead;
3429 successWrite = BlockChainStream_WriteAt(bbTempChain,
3434 cbTotalWritten += cbWritten;
3436 offset.u.LowPart += This->smallBlockSize;
3438 } while (successRead && successWrite);
3439 HeapFree(GetProcessHeap(),0,buffer);
3441 assert(cbTotalRead == cbTotalWritten);
3444 * Destroy the small block chain.
3446 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3447 size.u.HighPart = 0;
3449 SmallBlockChainStream_SetSize(*ppsbChain, size);
3450 SmallBlockChainStream_Destroy(*ppsbChain);
3454 * Change the property information. This chain is now a big block chain
3455 * and it doesn't reside in the small blocks chain anymore.
3457 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3459 chainProperty.startingBlock = bbHeadOfChain;
3461 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3464 * Destroy the temporary propertyless big block chain.
3465 * Create a new big block chain associated with this property.
3467 BlockChainStream_Destroy(bbTempChain);
3468 bigBlockChain = BlockChainStream_Construct(This,
3472 return bigBlockChain;
3475 /******************************************************************************
3476 ** Storage32InternalImpl implementation
3479 StorageInternalImpl* StorageInternalImpl_Construct(
3480 StorageImpl* ancestorStorage,
3481 ULONG rootPropertyIndex)
3483 StorageInternalImpl* newStorage;
3486 * Allocate space for the new storage object
3488 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3492 memset(newStorage, 0, sizeof(StorageInternalImpl));
3495 * Initialize the virtual function table.
3497 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
3498 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
3501 * Keep the ancestor storage pointer and nail a reference to it.
3503 newStorage->base.ancestorStorage = ancestorStorage;
3504 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
3507 * Keep the index of the root property set for this storage,
3509 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
3517 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3519 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3521 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3522 HeapFree(GetProcessHeap(), 0, This);
3525 /******************************************************************************
3527 ** Storage32InternalImpl_Commit
3529 ** The non-root storages cannot be opened in transacted mode thus this function
3532 HRESULT WINAPI StorageInternalImpl_Commit(
3534 DWORD grfCommitFlags) /* [in] */
3539 /******************************************************************************
3541 ** Storage32InternalImpl_Revert
3543 ** The non-root storages cannot be opened in transacted mode thus this function
3546 HRESULT WINAPI StorageInternalImpl_Revert(
3552 /******************************************************************************
3553 ** IEnumSTATSTGImpl implementation
3556 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3557 StorageImpl* parentStorage,
3558 ULONG firstPropertyNode)
3560 IEnumSTATSTGImpl* newEnumeration;
3562 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3564 if (newEnumeration!=0)
3567 * Set-up the virtual function table and reference count.
3569 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3570 newEnumeration->ref = 0;
3573 * We want to nail-down the reference to the storage in case the
3574 * enumeration out-lives the storage in the client application.
3576 newEnumeration->parentStorage = parentStorage;
3577 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3579 newEnumeration->firstPropertyNode = firstPropertyNode;
3582 * Initialize the search stack
3584 newEnumeration->stackSize = 0;
3585 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3586 newEnumeration->stackToVisit =
3587 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3590 * Make sure the current node of the iterator is the first one.
3592 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3595 return newEnumeration;
3598 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3600 IStorage_Release((IStorage*)This->parentStorage);
3601 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3602 HeapFree(GetProcessHeap(), 0, This);
3605 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3606 IEnumSTATSTG* iface,
3610 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3613 * Perform a sanity check on the parameters.
3616 return E_INVALIDARG;
3619 * Initialize the return parameter.
3624 * Compare the riid with the interface IDs implemented by this object.
3626 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3628 *ppvObject = (IEnumSTATSTG*)This;
3630 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3632 *ppvObject = (IEnumSTATSTG*)This;
3636 * Check that we obtained an interface.
3638 if ((*ppvObject)==0)
3639 return E_NOINTERFACE;
3642 * Query Interface always increases the reference count by one when it is
3645 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3650 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3651 IEnumSTATSTG* iface)
3653 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3654 return InterlockedIncrement(&This->ref);
3657 ULONG WINAPI IEnumSTATSTGImpl_Release(
3658 IEnumSTATSTG* iface)
3660 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3664 newRef = InterlockedDecrement(&This->ref);
3667 * If the reference count goes down to 0, perform suicide.
3671 IEnumSTATSTGImpl_Destroy(This);
3677 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3678 IEnumSTATSTG* iface,
3681 ULONG* pceltFetched)
3683 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3685 StgProperty currentProperty;
3686 STATSTG* currentReturnStruct = rgelt;
3687 ULONG objectFetched = 0;
3688 ULONG currentSearchNode;
3691 * Perform a sanity check on the parameters.
3693 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3694 return E_INVALIDARG;
3697 * To avoid the special case, get another pointer to a ULONG value if
3698 * the caller didn't supply one.
3700 if (pceltFetched==0)
3701 pceltFetched = &objectFetched;
3704 * Start the iteration, we will iterate until we hit the end of the
3705 * linked list or until we hit the number of items to iterate through
3710 * Start with the node at the top of the stack.
3712 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3714 while ( ( *pceltFetched < celt) &&
3715 ( currentSearchNode!=PROPERTY_NULL) )
3718 * Remove the top node from the stack
3720 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3723 * Read the property from the storage.
3725 StorageImpl_ReadProperty(This->parentStorage,
3730 * Copy the information to the return buffer.
3732 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3737 * Step to the next item in the iteration
3740 currentReturnStruct++;
3743 * Push the next search node in the search stack.
3745 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3748 * continue the iteration.
3750 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3753 if (*pceltFetched == celt)
3760 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3761 IEnumSTATSTG* iface,
3764 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3766 StgProperty currentProperty;
3767 ULONG objectFetched = 0;
3768 ULONG currentSearchNode;
3771 * Start with the node at the top of the stack.
3773 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3775 while ( (objectFetched < celt) &&
3776 (currentSearchNode!=PROPERTY_NULL) )
3779 * Remove the top node from the stack
3781 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3784 * Read the property from the storage.
3786 StorageImpl_ReadProperty(This->parentStorage,
3791 * Step to the next item in the iteration
3796 * Push the next search node in the search stack.
3798 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3801 * continue the iteration.
3803 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3806 if (objectFetched == celt)
3812 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3813 IEnumSTATSTG* iface)
3815 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3817 StgProperty rootProperty;
3818 BOOL readSuccessful;
3821 * Re-initialize the search stack to an empty stack
3823 This->stackSize = 0;
3826 * Read the root property from the storage.
3828 readSuccessful = StorageImpl_ReadProperty(
3829 This->parentStorage,
3830 This->firstPropertyNode,
3835 assert(rootProperty.sizeOfNameString!=0);
3838 * Push the search node in the search stack.
3840 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3846 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3847 IEnumSTATSTG* iface,
3848 IEnumSTATSTG** ppenum)
3850 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3852 IEnumSTATSTGImpl* newClone;
3855 * Perform a sanity check on the parameters.
3858 return E_INVALIDARG;
3860 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3861 This->firstPropertyNode);
3865 * The new clone enumeration must point to the same current node as
3868 newClone->stackSize = This->stackSize ;
3869 newClone->stackMaxSize = This->stackMaxSize ;
3870 newClone->stackToVisit =
3871 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3874 newClone->stackToVisit,
3876 sizeof(ULONG) * newClone->stackSize);
3878 *ppenum = (IEnumSTATSTG*)newClone;
3881 * Don't forget to nail down a reference to the clone before
3884 IEnumSTATSTGImpl_AddRef(*ppenum);
3889 INT IEnumSTATSTGImpl_FindParentProperty(
3890 IEnumSTATSTGImpl *This,
3891 ULONG childProperty,
3892 StgProperty *currentProperty,
3895 ULONG currentSearchNode;
3899 * To avoid the special case, get another pointer to a ULONG value if
3900 * the caller didn't supply one.
3903 thisNodeId = &foundNode;
3906 * Start with the node at the top of the stack.
3908 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3911 while (currentSearchNode!=PROPERTY_NULL)
3914 * Store the current node in the returned parameters
3916 *thisNodeId = currentSearchNode;
3919 * Remove the top node from the stack
3921 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3924 * Read the property from the storage.
3926 StorageImpl_ReadProperty(
3927 This->parentStorage,
3931 if (currentProperty->previousProperty == childProperty)
3932 return PROPERTY_RELATION_PREVIOUS;
3934 else if (currentProperty->nextProperty == childProperty)
3935 return PROPERTY_RELATION_NEXT;
3937 else if (currentProperty->dirProperty == childProperty)
3938 return PROPERTY_RELATION_DIR;
3941 * Push the next search node in the search stack.
3943 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3946 * continue the iteration.
3948 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3951 return PROPERTY_NULL;
3954 ULONG IEnumSTATSTGImpl_FindProperty(
3955 IEnumSTATSTGImpl* This,
3956 const OLECHAR* lpszPropName,
3957 StgProperty* currentProperty)
3959 ULONG currentSearchNode;
3962 * Start with the node at the top of the stack.
3964 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3966 while (currentSearchNode!=PROPERTY_NULL)
3969 * Remove the top node from the stack
3971 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3974 * Read the property from the storage.
3976 StorageImpl_ReadProperty(This->parentStorage,
3980 if ( propertyNameCmp(
3981 (const OLECHAR*)currentProperty->name,
3982 (const OLECHAR*)lpszPropName) == 0)
3983 return currentSearchNode;
3986 * Push the next search node in the search stack.
3988 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3991 * continue the iteration.
3993 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3996 return PROPERTY_NULL;
3999 void IEnumSTATSTGImpl_PushSearchNode(
4000 IEnumSTATSTGImpl* This,
4003 StgProperty rootProperty;
4004 BOOL readSuccessful;
4007 * First, make sure we're not trying to push an unexisting node.
4009 if (nodeToPush==PROPERTY_NULL)
4013 * First push the node to the stack
4015 if (This->stackSize == This->stackMaxSize)
4017 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4019 This->stackToVisit = HeapReAlloc(
4023 sizeof(ULONG) * This->stackMaxSize);
4026 This->stackToVisit[This->stackSize] = nodeToPush;
4030 * Read the root property from the storage.
4032 readSuccessful = StorageImpl_ReadProperty(
4033 This->parentStorage,
4039 assert(rootProperty.sizeOfNameString!=0);
4042 * Push the previous search node in the search stack.
4044 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4048 ULONG IEnumSTATSTGImpl_PopSearchNode(
4049 IEnumSTATSTGImpl* This,
4054 if (This->stackSize == 0)
4055 return PROPERTY_NULL;
4057 topNode = This->stackToVisit[This->stackSize-1];
4065 /******************************************************************************
4066 ** StorageUtl implementation
4067 * FIXME: these should read and write in little-endian order on all
4068 * architectures, but right now just assume the host is little-endian.
4071 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4073 memcpy(value, buffer+offset, sizeof(WORD));
4076 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4078 memcpy(buffer+offset, &value, sizeof(WORD));
4081 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4083 memcpy(value, buffer+offset, sizeof(DWORD));
4086 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4088 memcpy(buffer+offset, &value, sizeof(DWORD));
4091 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4093 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4094 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4095 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4097 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4100 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4102 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4103 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4104 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4106 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4109 void StorageUtl_CopyPropertyToSTATSTG(
4110 STATSTG* destination,
4111 StgProperty* source,
4115 * The copy of the string occurs only when the flag is not set
4117 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4118 (source->name == NULL) ||
4119 (source->name[0] == 0) )
4121 destination->pwcsName = 0;
4125 destination->pwcsName =
4126 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4128 strcpyW((LPWSTR)destination->pwcsName, source->name);
4131 switch (source->propertyType)
4133 case PROPTYPE_STORAGE:
4135 destination->type = STGTY_STORAGE;
4137 case PROPTYPE_STREAM:
4138 destination->type = STGTY_STREAM;
4141 destination->type = STGTY_STREAM;
4145 destination->cbSize = source->size;
4147 currentReturnStruct->mtime = {0}; TODO
4148 currentReturnStruct->ctime = {0};
4149 currentReturnStruct->atime = {0};
4151 destination->grfMode = 0;
4152 destination->grfLocksSupported = 0;
4153 destination->clsid = source->propertyUniqueID;
4154 destination->grfStateBits = 0;
4155 destination->reserved = 0;
4158 /******************************************************************************
4159 ** BlockChainStream implementation
4162 BlockChainStream* BlockChainStream_Construct(
4163 StorageImpl* parentStorage,
4164 ULONG* headOfStreamPlaceHolder,
4165 ULONG propertyIndex)
4167 BlockChainStream* newStream;
4170 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4172 newStream->parentStorage = parentStorage;
4173 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4174 newStream->ownerPropertyIndex = propertyIndex;
4175 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4176 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4177 newStream->numBlocks = 0;
4179 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4181 while (blockIndex != BLOCK_END_OF_CHAIN)
4183 newStream->numBlocks++;
4184 newStream->tailIndex = blockIndex;
4186 if(FAILED(StorageImpl_GetNextBlockInChain(
4191 HeapFree(GetProcessHeap(), 0, newStream);
4199 void BlockChainStream_Destroy(BlockChainStream* This)
4201 HeapFree(GetProcessHeap(), 0, This);
4204 /******************************************************************************
4205 * BlockChainStream_GetHeadOfChain
4207 * Returns the head of this stream chain.
4208 * Some special chains don't have properties, their heads are kept in
4209 * This->headOfStreamPlaceHolder.
4212 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4214 StgProperty chainProperty;
4215 BOOL readSuccessful;
4217 if (This->headOfStreamPlaceHolder != 0)
4218 return *(This->headOfStreamPlaceHolder);
4220 if (This->ownerPropertyIndex != PROPERTY_NULL)
4222 readSuccessful = StorageImpl_ReadProperty(
4223 This->parentStorage,
4224 This->ownerPropertyIndex,
4229 return chainProperty.startingBlock;
4233 return BLOCK_END_OF_CHAIN;
4236 /******************************************************************************
4237 * BlockChainStream_GetCount
4239 * Returns the number of blocks that comprises this chain.
4240 * This is not the size of the stream as the last block may not be full!
4243 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4248 blockIndex = BlockChainStream_GetHeadOfChain(This);
4250 while (blockIndex != BLOCK_END_OF_CHAIN)
4254 if(FAILED(StorageImpl_GetNextBlockInChain(
4255 This->parentStorage,
4264 /******************************************************************************
4265 * BlockChainStream_ReadAt
4267 * Reads a specified number of bytes from this chain at the specified offset.
4268 * bytesRead may be NULL.
4269 * Failure will be returned if the specified number of bytes has not been read.
4271 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4272 ULARGE_INTEGER offset,
4277 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4278 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4279 ULONG bytesToReadInBuffer;
4282 BYTE* bigBlockBuffer;
4285 * Find the first block in the stream that contains part of the buffer.
4287 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4288 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4289 (blockNoInSequence < This->lastBlockNoInSequence) )
4291 blockIndex = BlockChainStream_GetHeadOfChain(This);
4292 This->lastBlockNoInSequence = blockNoInSequence;
4296 ULONG temp = blockNoInSequence;
4298 blockIndex = This->lastBlockNoInSequenceIndex;
4299 blockNoInSequence -= This->lastBlockNoInSequence;
4300 This->lastBlockNoInSequence = temp;
4303 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4305 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4307 blockNoInSequence--;
4310 This->lastBlockNoInSequenceIndex = blockIndex;
4313 * Start reading the buffer.
4316 bufferWalker = buffer;
4318 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4321 * Calculate how many bytes we can copy from this big block.
4323 bytesToReadInBuffer =
4324 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4327 * Copy those bytes to the buffer
4330 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4332 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4334 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4337 * Step to the next big block.
4339 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4342 bufferWalker += bytesToReadInBuffer;
4343 size -= bytesToReadInBuffer;
4344 *bytesRead += bytesToReadInBuffer;
4345 offsetInBlock = 0; /* There is no offset on the next block */
4352 /******************************************************************************
4353 * BlockChainStream_WriteAt
4355 * Writes the specified number of bytes to this chain at the specified offset.
4356 * bytesWritten may be NULL.
4357 * Will fail if not all specified number of bytes have been written.
4359 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4360 ULARGE_INTEGER offset,
4363 ULONG* bytesWritten)
4365 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4366 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4369 const BYTE* bufferWalker;
4370 BYTE* bigBlockBuffer;
4373 * Find the first block in the stream that contains part of the buffer.
4375 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4376 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4377 (blockNoInSequence < This->lastBlockNoInSequence) )
4379 blockIndex = BlockChainStream_GetHeadOfChain(This);
4380 This->lastBlockNoInSequence = blockNoInSequence;
4384 ULONG temp = blockNoInSequence;
4386 blockIndex = This->lastBlockNoInSequenceIndex;
4387 blockNoInSequence -= This->lastBlockNoInSequence;
4388 This->lastBlockNoInSequence = temp;
4391 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4393 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4396 blockNoInSequence--;
4399 This->lastBlockNoInSequenceIndex = blockIndex;
4402 * Here, I'm casting away the constness on the buffer variable
4403 * This is OK since we don't intend to modify that buffer.
4406 bufferWalker = (const BYTE*)buffer;
4408 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4411 * Calculate how many bytes we can copy from this big block.
4414 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4417 * Copy those bytes to the buffer
4419 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4421 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4423 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4426 * Step to the next big block.
4428 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4431 bufferWalker += bytesToWrite;
4432 size -= bytesToWrite;
4433 *bytesWritten += bytesToWrite;
4434 offsetInBlock = 0; /* There is no offset on the next block */
4440 /******************************************************************************
4441 * BlockChainStream_Shrink
4443 * Shrinks this chain in the big block depot.
4445 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4446 ULARGE_INTEGER newSize)
4448 ULONG blockIndex, extraBlock;
4453 * Reset the last accessed block cache.
4455 This->lastBlockNoInSequence = 0xFFFFFFFF;
4456 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4459 * Figure out how many blocks are needed to contain the new size
4461 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4463 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4466 blockIndex = BlockChainStream_GetHeadOfChain(This);
4469 * Go to the new end of chain
4471 while (count < numBlocks)
4473 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4479 /* Get the next block before marking the new end */
4480 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4484 /* Mark the new end of chain */
4485 StorageImpl_SetNextBlockInChain(
4486 This->parentStorage,
4488 BLOCK_END_OF_CHAIN);
4490 This->tailIndex = blockIndex;
4491 This->numBlocks = numBlocks;
4494 * Mark the extra blocks as free
4496 while (extraBlock != BLOCK_END_OF_CHAIN)
4498 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4501 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4502 extraBlock = blockIndex;
4508 /******************************************************************************
4509 * BlockChainStream_Enlarge
4511 * Grows this chain in the big block depot.
4513 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4514 ULARGE_INTEGER newSize)
4516 ULONG blockIndex, currentBlock;
4518 ULONG oldNumBlocks = 0;
4520 blockIndex = BlockChainStream_GetHeadOfChain(This);
4523 * Empty chain. Create the head.
4525 if (blockIndex == BLOCK_END_OF_CHAIN)
4527 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4528 StorageImpl_SetNextBlockInChain(This->parentStorage,
4530 BLOCK_END_OF_CHAIN);
4532 if (This->headOfStreamPlaceHolder != 0)
4534 *(This->headOfStreamPlaceHolder) = blockIndex;
4538 StgProperty chainProp;
4539 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4541 StorageImpl_ReadProperty(
4542 This->parentStorage,
4543 This->ownerPropertyIndex,
4546 chainProp.startingBlock = blockIndex;
4548 StorageImpl_WriteProperty(
4549 This->parentStorage,
4550 This->ownerPropertyIndex,
4554 This->tailIndex = blockIndex;
4555 This->numBlocks = 1;
4559 * Figure out how many blocks are needed to contain this stream
4561 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4563 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4567 * Go to the current end of chain
4569 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4571 currentBlock = blockIndex;
4573 while (blockIndex != BLOCK_END_OF_CHAIN)
4576 currentBlock = blockIndex;
4578 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4583 This->tailIndex = currentBlock;
4586 currentBlock = This->tailIndex;
4587 oldNumBlocks = This->numBlocks;
4590 * Add new blocks to the chain
4592 if (oldNumBlocks < newNumBlocks)
4594 while (oldNumBlocks < newNumBlocks)
4596 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4598 StorageImpl_SetNextBlockInChain(
4599 This->parentStorage,
4603 StorageImpl_SetNextBlockInChain(
4604 This->parentStorage,
4606 BLOCK_END_OF_CHAIN);
4608 currentBlock = blockIndex;
4612 This->tailIndex = blockIndex;
4613 This->numBlocks = newNumBlocks;
4619 /******************************************************************************
4620 * BlockChainStream_SetSize
4622 * Sets the size of this stream. The big block depot will be updated.
4623 * The file will grow if we grow the chain.
4625 * TODO: Free the actual blocks in the file when we shrink the chain.
4626 * Currently, the blocks are still in the file. So the file size
4627 * doesn't shrink even if we shrink streams.
4629 BOOL BlockChainStream_SetSize(
4630 BlockChainStream* This,
4631 ULARGE_INTEGER newSize)
4633 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4635 if (newSize.u.LowPart == size.u.LowPart)
4638 if (newSize.u.LowPart < size.u.LowPart)
4640 BlockChainStream_Shrink(This, newSize);
4644 ULARGE_INTEGER fileSize =
4645 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4647 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4650 * Make sure the file stays a multiple of blocksize
4652 if ((diff % This->parentStorage->bigBlockSize) != 0)
4653 diff += (This->parentStorage->bigBlockSize -
4654 (diff % This->parentStorage->bigBlockSize) );
4656 fileSize.u.LowPart += diff;
4657 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4659 BlockChainStream_Enlarge(This, newSize);
4665 /******************************************************************************
4666 * BlockChainStream_GetSize
4668 * Returns the size of this chain.
4669 * Will return the block count if this chain doesn't have a property.
4671 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4673 StgProperty chainProperty;
4675 if(This->headOfStreamPlaceHolder == NULL)
4678 * This chain is a data stream read the property and return
4679 * the appropriate size
4681 StorageImpl_ReadProperty(
4682 This->parentStorage,
4683 This->ownerPropertyIndex,
4686 return chainProperty.size;
4691 * this chain is a chain that does not have a property, figure out the
4692 * size by making the product number of used blocks times the
4695 ULARGE_INTEGER result;
4696 result.u.HighPart = 0;
4699 BlockChainStream_GetCount(This) *
4700 This->parentStorage->bigBlockSize;
4706 /******************************************************************************
4707 ** SmallBlockChainStream implementation
4710 SmallBlockChainStream* SmallBlockChainStream_Construct(
4711 StorageImpl* parentStorage,
4712 ULONG propertyIndex)
4714 SmallBlockChainStream* newStream;
4716 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4718 newStream->parentStorage = parentStorage;
4719 newStream->ownerPropertyIndex = propertyIndex;
4724 void SmallBlockChainStream_Destroy(
4725 SmallBlockChainStream* This)
4727 HeapFree(GetProcessHeap(), 0, This);
4730 /******************************************************************************
4731 * SmallBlockChainStream_GetHeadOfChain
4733 * Returns the head of this chain of small blocks.
4735 ULONG SmallBlockChainStream_GetHeadOfChain(
4736 SmallBlockChainStream* This)
4738 StgProperty chainProperty;
4739 BOOL readSuccessful;
4741 if (This->ownerPropertyIndex)
4743 readSuccessful = StorageImpl_ReadProperty(
4744 This->parentStorage,
4745 This->ownerPropertyIndex,
4750 return chainProperty.startingBlock;
4755 return BLOCK_END_OF_CHAIN;
4758 /******************************************************************************
4759 * SmallBlockChainStream_GetNextBlockInChain
4761 * Returns the index of the next small block in this chain.
4764 * - BLOCK_END_OF_CHAIN: end of this chain
4765 * - BLOCK_UNUSED: small block 'blockIndex' is free
4767 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4768 SmallBlockChainStream* This,
4770 ULONG* nextBlockInChain)
4772 ULARGE_INTEGER offsetOfBlockInDepot;
4777 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4779 offsetOfBlockInDepot.u.HighPart = 0;
4780 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4783 * Read those bytes in the buffer from the small block file.
4785 success = BlockChainStream_ReadAt(
4786 This->parentStorage->smallBlockDepotChain,
4787 offsetOfBlockInDepot,
4794 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4798 return STG_E_READFAULT;
4801 /******************************************************************************
4802 * SmallBlockChainStream_SetNextBlockInChain
4804 * Writes the index of the next block of the specified block in the small
4806 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4807 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4809 void SmallBlockChainStream_SetNextBlockInChain(
4810 SmallBlockChainStream* This,
4814 ULARGE_INTEGER offsetOfBlockInDepot;
4818 offsetOfBlockInDepot.u.HighPart = 0;
4819 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4821 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4824 * Read those bytes in the buffer from the small block file.
4826 BlockChainStream_WriteAt(
4827 This->parentStorage->smallBlockDepotChain,
4828 offsetOfBlockInDepot,
4834 /******************************************************************************
4835 * SmallBlockChainStream_FreeBlock
4837 * Flag small block 'blockIndex' as free in the small block depot.
4839 void SmallBlockChainStream_FreeBlock(
4840 SmallBlockChainStream* This,
4843 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4846 /******************************************************************************
4847 * SmallBlockChainStream_GetNextFreeBlock
4849 * Returns the index of a free small block. The small block depot will be
4850 * enlarged if necessary. The small block chain will also be enlarged if
4853 ULONG SmallBlockChainStream_GetNextFreeBlock(
4854 SmallBlockChainStream* This)
4856 ULARGE_INTEGER offsetOfBlockInDepot;
4859 ULONG blockIndex = 0;
4860 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4861 BOOL success = TRUE;
4862 ULONG smallBlocksPerBigBlock;
4864 offsetOfBlockInDepot.u.HighPart = 0;
4867 * Scan the small block depot for a free block
4869 while (nextBlockIndex != BLOCK_UNUSED)
4871 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4873 success = BlockChainStream_ReadAt(
4874 This->parentStorage->smallBlockDepotChain,
4875 offsetOfBlockInDepot,
4881 * If we run out of space for the small block depot, enlarge it
4885 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4887 if (nextBlockIndex != BLOCK_UNUSED)
4893 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4895 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4896 ULONG nextBlock, newsbdIndex;
4897 BYTE* smallBlockDepot;
4899 nextBlock = sbdIndex;
4900 while (nextBlock != BLOCK_END_OF_CHAIN)
4902 sbdIndex = nextBlock;
4903 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4906 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4907 if (sbdIndex != BLOCK_END_OF_CHAIN)
4908 StorageImpl_SetNextBlockInChain(
4909 This->parentStorage,
4913 StorageImpl_SetNextBlockInChain(
4914 This->parentStorage,
4916 BLOCK_END_OF_CHAIN);
4919 * Initialize all the small blocks to free
4922 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4924 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4925 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4930 * We have just created the small block depot.
4932 StgProperty rootProp;
4936 * Save it in the header
4938 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4939 StorageImpl_SaveFileHeader(This->parentStorage);
4942 * And allocate the first big block that will contain small blocks
4945 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4947 StorageImpl_SetNextBlockInChain(
4948 This->parentStorage,
4950 BLOCK_END_OF_CHAIN);
4952 StorageImpl_ReadProperty(
4953 This->parentStorage,
4954 This->parentStorage->base.rootPropertySetIndex,
4957 rootProp.startingBlock = sbStartIndex;
4958 rootProp.size.u.HighPart = 0;
4959 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
4961 StorageImpl_WriteProperty(
4962 This->parentStorage,
4963 This->parentStorage->base.rootPropertySetIndex,
4969 smallBlocksPerBigBlock =
4970 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4973 * Verify if we have to allocate big blocks to contain small blocks
4975 if (blockIndex % smallBlocksPerBigBlock == 0)
4977 StgProperty rootProp;
4978 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4980 StorageImpl_ReadProperty(
4981 This->parentStorage,
4982 This->parentStorage->base.rootPropertySetIndex,
4985 if (rootProp.size.u.LowPart <
4986 (blocksRequired * This->parentStorage->bigBlockSize))
4988 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
4990 BlockChainStream_SetSize(
4991 This->parentStorage->smallBlockRootChain,
4994 StorageImpl_WriteProperty(
4995 This->parentStorage,
4996 This->parentStorage->base.rootPropertySetIndex,
5004 /******************************************************************************
5005 * SmallBlockChainStream_ReadAt
5007 * Reads a specified number of bytes from this chain at the specified offset.
5008 * bytesRead may be NULL.
5009 * Failure will be returned if the specified number of bytes has not been read.
5011 BOOL SmallBlockChainStream_ReadAt(
5012 SmallBlockChainStream* This,
5013 ULARGE_INTEGER offset,
5018 ULARGE_INTEGER offsetInBigBlockFile;
5019 ULONG blockNoInSequence =
5020 offset.u.LowPart / This->parentStorage->smallBlockSize;
5022 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5023 ULONG bytesToReadInBuffer;
5025 ULONG bytesReadFromBigBlockFile;
5029 * This should never happen on a small block file.
5031 assert(offset.u.HighPart==0);
5034 * Find the first block in the stream that contains part of the buffer.
5036 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5038 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5040 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5043 blockNoInSequence--;
5047 * Start reading the buffer.
5050 bufferWalker = buffer;
5052 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5055 * Calculate how many bytes we can copy from this small block.
5057 bytesToReadInBuffer =
5058 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5061 * Calculate the offset of the small block in the small block file.
5063 offsetInBigBlockFile.u.HighPart = 0;
5064 offsetInBigBlockFile.u.LowPart =
5065 blockIndex * This->parentStorage->smallBlockSize;
5067 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5070 * Read those bytes in the buffer from the small block file.
5072 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5073 offsetInBigBlockFile,
5074 bytesToReadInBuffer,
5076 &bytesReadFromBigBlockFile);
5078 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5081 * Step to the next big block.
5083 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5085 bufferWalker += bytesToReadInBuffer;
5086 size -= bytesToReadInBuffer;
5087 *bytesRead += bytesToReadInBuffer;
5088 offsetInBlock = 0; /* There is no offset on the next block */
5094 /******************************************************************************
5095 * SmallBlockChainStream_WriteAt
5097 * Writes the specified number of bytes to this chain at the specified offset.
5098 * bytesWritten may be NULL.
5099 * Will fail if not all specified number of bytes have been written.
5101 BOOL SmallBlockChainStream_WriteAt(
5102 SmallBlockChainStream* This,
5103 ULARGE_INTEGER offset,
5106 ULONG* bytesWritten)
5108 ULARGE_INTEGER offsetInBigBlockFile;
5109 ULONG blockNoInSequence =
5110 offset.u.LowPart / This->parentStorage->smallBlockSize;
5112 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5113 ULONG bytesToWriteInBuffer;
5115 ULONG bytesWrittenFromBigBlockFile;
5116 const BYTE* bufferWalker;
5119 * This should never happen on a small block file.
5121 assert(offset.u.HighPart==0);
5124 * Find the first block in the stream that contains part of the buffer.
5126 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5128 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5130 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5132 blockNoInSequence--;
5136 * Start writing the buffer.
5138 * Here, I'm casting away the constness on the buffer variable
5139 * This is OK since we don't intend to modify that buffer.
5142 bufferWalker = (const BYTE*)buffer;
5143 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5146 * Calculate how many bytes we can copy to this small block.
5148 bytesToWriteInBuffer =
5149 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5152 * Calculate the offset of the small block in the small block file.
5154 offsetInBigBlockFile.u.HighPart = 0;
5155 offsetInBigBlockFile.u.LowPart =
5156 blockIndex * This->parentStorage->smallBlockSize;
5158 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5161 * Write those bytes in the buffer to the small block file.
5163 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5164 offsetInBigBlockFile,
5165 bytesToWriteInBuffer,
5167 &bytesWrittenFromBigBlockFile);
5169 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5172 * Step to the next big block.
5174 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5177 bufferWalker += bytesToWriteInBuffer;
5178 size -= bytesToWriteInBuffer;
5179 *bytesWritten += bytesToWriteInBuffer;
5180 offsetInBlock = 0; /* There is no offset on the next block */
5186 /******************************************************************************
5187 * SmallBlockChainStream_Shrink
5189 * Shrinks this chain in the small block depot.
5191 BOOL SmallBlockChainStream_Shrink(
5192 SmallBlockChainStream* This,
5193 ULARGE_INTEGER newSize)
5195 ULONG blockIndex, extraBlock;
5199 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5201 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5204 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5207 * Go to the new end of chain
5209 while (count < numBlocks)
5211 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5218 * If the count is 0, we have a special case, the head of the chain was
5223 StgProperty chainProp;
5225 StorageImpl_ReadProperty(This->parentStorage,
5226 This->ownerPropertyIndex,
5229 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5231 StorageImpl_WriteProperty(This->parentStorage,
5232 This->ownerPropertyIndex,
5236 * We start freeing the chain at the head block.
5238 extraBlock = blockIndex;
5242 /* Get the next block before marking the new end */
5243 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5247 /* Mark the new end of chain */
5248 SmallBlockChainStream_SetNextBlockInChain(
5251 BLOCK_END_OF_CHAIN);
5255 * Mark the extra blocks as free
5257 while (extraBlock != BLOCK_END_OF_CHAIN)
5259 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5262 SmallBlockChainStream_FreeBlock(This, extraBlock);
5263 extraBlock = blockIndex;
5269 /******************************************************************************
5270 * SmallBlockChainStream_Enlarge
5272 * Grows this chain in the small block depot.
5274 BOOL SmallBlockChainStream_Enlarge(
5275 SmallBlockChainStream* This,
5276 ULARGE_INTEGER newSize)
5278 ULONG blockIndex, currentBlock;
5280 ULONG oldNumBlocks = 0;
5282 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5287 if (blockIndex == BLOCK_END_OF_CHAIN)
5290 StgProperty chainProp;
5292 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5295 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5297 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5300 blockIndex = chainProp.startingBlock;
5301 SmallBlockChainStream_SetNextBlockInChain(
5304 BLOCK_END_OF_CHAIN);
5307 currentBlock = blockIndex;
5310 * Figure out how many blocks are needed to contain this stream
5312 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5314 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5318 * Go to the current end of chain
5320 while (blockIndex != BLOCK_END_OF_CHAIN)
5323 currentBlock = blockIndex;
5324 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5329 * Add new blocks to the chain
5331 while (oldNumBlocks < newNumBlocks)
5333 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5334 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5336 SmallBlockChainStream_SetNextBlockInChain(
5339 BLOCK_END_OF_CHAIN);
5341 currentBlock = blockIndex;
5348 /******************************************************************************
5349 * SmallBlockChainStream_GetCount
5351 * Returns the number of blocks that comprises this chain.
5352 * This is not the size of this chain as the last block may not be full!
5354 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5359 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5361 while (blockIndex != BLOCK_END_OF_CHAIN)
5365 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5372 /******************************************************************************
5373 * SmallBlockChainStream_SetSize
5375 * Sets the size of this stream.
5376 * The file will grow if we grow the chain.
5378 * TODO: Free the actual blocks in the file when we shrink the chain.
5379 * Currently, the blocks are still in the file. So the file size
5380 * doesn't shrink even if we shrink streams.
5382 BOOL SmallBlockChainStream_SetSize(
5383 SmallBlockChainStream* This,
5384 ULARGE_INTEGER newSize)
5386 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5388 if (newSize.u.LowPart == size.u.LowPart)
5391 if (newSize.u.LowPart < size.u.LowPart)
5393 SmallBlockChainStream_Shrink(This, newSize);
5397 SmallBlockChainStream_Enlarge(This, newSize);
5403 /******************************************************************************
5404 * SmallBlockChainStream_GetSize
5406 * Returns the size of this chain.
5408 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5410 StgProperty chainProperty;
5412 StorageImpl_ReadProperty(
5413 This->parentStorage,
5414 This->ownerPropertyIndex,
5417 return chainProperty.size;
5420 /******************************************************************************
5421 * StgCreateDocfile [OLE32.@]
5423 HRESULT WINAPI StgCreateDocfile(
5427 IStorage **ppstgOpen)
5429 StorageImpl* newStorage = 0;
5430 HANDLE hFile = INVALID_HANDLE_VALUE;
5431 HRESULT hr = STG_E_INVALIDFLAG;
5435 DWORD fileAttributes;
5436 WCHAR tempFileName[MAX_PATH];
5438 TRACE("(%s, %lx, %ld, %p)\n",
5439 debugstr_w(pwcsName), grfMode,
5440 reserved, ppstgOpen);
5443 * Validate the parameters
5446 return STG_E_INVALIDPOINTER;
5448 return STG_E_INVALIDPARAMETER;
5451 * Validate the STGM flags
5453 if ( FAILED( validateSTGM(grfMode) ))
5456 /* StgCreateDocFile always opens for write */
5457 switch(STGM_ACCESS_MODE(grfMode))
5460 case STGM_READWRITE:
5466 /* can't share write */
5467 switch(STGM_SHARE_MODE(grfMode))
5469 case STGM_SHARE_EXCLUSIVE:
5470 case STGM_SHARE_DENY_WRITE:
5476 /* shared reading requires transacted mode */
5477 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5478 !(grfMode&STGM_TRANSACTED) )
5482 * Generate a unique name.
5486 WCHAR tempPath[MAX_PATH];
5487 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5489 if (STGM_SHARE_MODE(grfMode) == STGM_SHARE_EXCLUSIVE)
5492 memset(tempPath, 0, sizeof(tempPath));
5493 memset(tempFileName, 0, sizeof(tempFileName));
5495 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5498 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5499 pwcsName = tempFileName;
5502 hr = STG_E_INSUFFICIENTMEMORY;
5506 creationMode = TRUNCATE_EXISTING;
5510 creationMode = GetCreationModeFromSTGM(grfMode);
5514 * Interpret the STGM value grfMode
5516 shareMode = GetShareModeFromSTGM(grfMode);
5517 accessMode = GetAccessModeFromSTGM(grfMode);
5519 if (grfMode & STGM_DELETEONRELEASE)
5520 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5522 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5524 if (grfMode & STGM_TRANSACTED)
5525 FIXME("Transacted mode not implemented.\n");
5528 * Initialize the "out" parameter.
5532 hFile = CreateFileW(pwcsName,
5540 if (hFile == INVALID_HANDLE_VALUE)
5542 if(GetLastError() == ERROR_FILE_EXISTS)
5543 hr = STG_E_FILEALREADYEXISTS;
5550 * Allocate and initialize the new IStorage32object.
5552 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5554 if (newStorage == 0)
5556 hr = STG_E_INSUFFICIENTMEMORY;
5560 hr = StorageImpl_Construct(
5571 HeapFree(GetProcessHeap(), 0, newStorage);
5576 * Get an "out" pointer for the caller.
5578 hr = StorageBaseImpl_QueryInterface(
5579 (IStorage*)newStorage,
5580 (REFIID)&IID_IStorage,
5583 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5588 /******************************************************************************
5589 * StgCreateStorageEx [OLE32.@]
5591 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5593 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5594 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5595 return STG_E_UNIMPLEMENTEDFUNCTION;
5598 /******************************************************************************
5599 * StgCreatePropSetStg [OLE32.@]
5601 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5602 IPropertySetStorage **ppPropSetStg)
5606 TRACE("(%p, 0x%lx, %p): stub\n", pstg, reserved, ppPropSetStg);
5608 hr = STG_E_INVALIDPARAMETER;
5610 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5611 (void**)ppPropSetStg);
5615 /******************************************************************************
5616 * StgOpenStorage [OLE32.@]
5618 HRESULT WINAPI StgOpenStorage(
5619 const OLECHAR *pwcsName,
5620 IStorage *pstgPriority,
5624 IStorage **ppstgOpen)
5626 StorageImpl* newStorage = 0;
5631 WCHAR fullname[MAX_PATH];
5634 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5635 debugstr_w(pwcsName), pstgPriority, grfMode,
5636 snbExclude, reserved, ppstgOpen);
5639 * Perform sanity checks
5643 hr = STG_E_INVALIDNAME;
5649 hr = STG_E_INVALIDPOINTER;
5655 hr = STG_E_INVALIDPARAMETER;
5660 * Validate the sharing mode
5662 switch(STGM_SHARE_MODE(grfMode))
5664 case STGM_SHARE_EXCLUSIVE:
5665 case STGM_SHARE_DENY_WRITE:
5668 hr = STG_E_INVALIDFLAG;
5673 * Validate the STGM flags
5675 if ( FAILED( validateSTGM(grfMode) ) ||
5676 (grfMode&STGM_CREATE))
5678 hr = STG_E_INVALIDFLAG;
5682 /* shared reading requires transacted mode */
5683 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5684 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5685 !(grfMode&STGM_TRANSACTED) )
5687 hr = STG_E_INVALIDFLAG;
5692 * Interpret the STGM value grfMode
5694 shareMode = GetShareModeFromSTGM(grfMode);
5695 accessMode = GetAccessModeFromSTGM(grfMode);
5698 * Initialize the "out" parameter.
5702 hFile = CreateFileW( pwcsName,
5707 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5710 if (hFile==INVALID_HANDLE_VALUE)
5712 DWORD last_error = GetLastError();
5718 case ERROR_FILE_NOT_FOUND:
5719 hr = STG_E_FILENOTFOUND;
5722 case ERROR_PATH_NOT_FOUND:
5723 hr = STG_E_PATHNOTFOUND;
5726 case ERROR_ACCESS_DENIED:
5727 case ERROR_WRITE_PROTECT:
5728 hr = STG_E_ACCESSDENIED;
5731 case ERROR_SHARING_VIOLATION:
5732 hr = STG_E_SHAREVIOLATION;
5743 * Refuse to open the file if it's too small to be a structured storage file
5744 * FIXME: verify the file when reading instead of here
5746 length = GetFileSize(hFile, NULL);
5750 hr = STG_E_FILEALREADYEXISTS;
5755 * Allocate and initialize the new IStorage32object.
5757 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5759 if (newStorage == 0)
5761 hr = STG_E_INSUFFICIENTMEMORY;
5765 /* if the file's length was zero, initialize the storage */
5766 hr = StorageImpl_Construct(
5777 HeapFree(GetProcessHeap(), 0, newStorage);
5779 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5781 if(hr == STG_E_INVALIDHEADER)
5782 hr = STG_E_FILEALREADYEXISTS;
5786 /* prepare the file name string given in lieu of the root property name */
5787 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5788 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5789 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5792 * Get an "out" pointer for the caller.
5794 hr = StorageBaseImpl_QueryInterface(
5795 (IStorage*)newStorage,
5796 (REFIID)&IID_IStorage,
5800 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5804 /******************************************************************************
5805 * StgCreateDocfileOnILockBytes [OLE32.@]
5807 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5811 IStorage** ppstgOpen)
5813 StorageImpl* newStorage = 0;
5817 * Validate the parameters
5819 if ((ppstgOpen == 0) || (plkbyt == 0))
5820 return STG_E_INVALIDPOINTER;
5823 * Allocate and initialize the new IStorage object.
5825 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5827 if (newStorage == 0)
5828 return STG_E_INSUFFICIENTMEMORY;
5830 hr = StorageImpl_Construct(
5841 HeapFree(GetProcessHeap(), 0, newStorage);
5846 * Get an "out" pointer for the caller.
5848 hr = StorageBaseImpl_QueryInterface(
5849 (IStorage*)newStorage,
5850 (REFIID)&IID_IStorage,
5856 /******************************************************************************
5857 * StgOpenStorageOnILockBytes [OLE32.@]
5859 HRESULT WINAPI StgOpenStorageOnILockBytes(
5861 IStorage *pstgPriority,
5865 IStorage **ppstgOpen)
5867 StorageImpl* newStorage = 0;
5871 * Perform a sanity check
5873 if ((plkbyt == 0) || (ppstgOpen == 0))
5874 return STG_E_INVALIDPOINTER;
5877 * Validate the STGM flags
5879 if ( FAILED( validateSTGM(grfMode) ))
5880 return STG_E_INVALIDFLAG;
5883 * Initialize the "out" parameter.
5888 * Allocate and initialize the new IStorage object.
5890 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5892 if (newStorage == 0)
5893 return STG_E_INSUFFICIENTMEMORY;
5895 hr = StorageImpl_Construct(
5906 HeapFree(GetProcessHeap(), 0, newStorage);
5911 * Get an "out" pointer for the caller.
5913 hr = StorageBaseImpl_QueryInterface(
5914 (IStorage*)newStorage,
5915 (REFIID)&IID_IStorage,
5921 /******************************************************************************
5922 * StgSetTimes [ole32.@]
5923 * StgSetTimes [OLE32.@]
5927 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
5928 FILETIME const *patime, FILETIME const *pmtime)
5930 IStorage *stg = NULL;
5933 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
5935 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
5939 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
5940 IStorage_Release(stg);
5946 /******************************************************************************
5947 * StgIsStorageILockBytes [OLE32.@]
5949 * Determines if the ILockBytes contains a storage object.
5951 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5954 ULARGE_INTEGER offset;
5956 offset.u.HighPart = 0;
5957 offset.u.LowPart = 0;
5959 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5961 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5967 /******************************************************************************
5968 * WriteClassStg [OLE32.@]
5970 * This method will store the specified CLSID in the specified storage object
5972 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5978 hRes = IStorage_SetClass(pStg, rclsid);
5983 /***********************************************************************
5984 * ReadClassStg (OLE32.@)
5986 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5988 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5998 * read a STATSTG structure (contains the clsid) from the storage
6000 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6003 *pclsid=pstatstg.clsid;
6008 /***********************************************************************
6009 * OleLoadFromStream (OLE32.@)
6011 * This function loads an object from stream
6013 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6017 LPPERSISTSTREAM xstm;
6019 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6021 res=ReadClassStm(pStm,&clsid);
6022 if (!SUCCEEDED(res))
6024 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6025 if (!SUCCEEDED(res))
6027 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6028 if (!SUCCEEDED(res)) {
6029 IUnknown_Release((IUnknown*)*ppvObj);
6032 res=IPersistStream_Load(xstm,pStm);
6033 IPersistStream_Release(xstm);
6034 /* FIXME: all refcounts ok at this point? I think they should be:
6037 * xstm : 0 (released)
6042 /***********************************************************************
6043 * OleSaveToStream (OLE32.@)
6045 * This function saves an object with the IPersistStream interface on it
6046 * to the specified stream.
6048 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6054 TRACE("(%p,%p)\n",pPStm,pStm);
6056 res=IPersistStream_GetClassID(pPStm,&clsid);
6058 if (SUCCEEDED(res)){
6060 res=WriteClassStm(pStm,&clsid);
6064 res=IPersistStream_Save(pPStm,pStm,TRUE);
6067 TRACE("Finished Save\n");
6071 /****************************************************************************
6072 * This method validate a STGM parameter that can contain the values below
6074 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6075 * The stgm values contained in 0xffff0000 are bitmasks.
6077 * STGM_DIRECT 0x00000000
6078 * STGM_TRANSACTED 0x00010000
6079 * STGM_SIMPLE 0x08000000
6081 * STGM_READ 0x00000000
6082 * STGM_WRITE 0x00000001
6083 * STGM_READWRITE 0x00000002
6085 * STGM_SHARE_DENY_NONE 0x00000040
6086 * STGM_SHARE_DENY_READ 0x00000030
6087 * STGM_SHARE_DENY_WRITE 0x00000020
6088 * STGM_SHARE_EXCLUSIVE 0x00000010
6090 * STGM_PRIORITY 0x00040000
6091 * STGM_DELETEONRELEASE 0x04000000
6093 * STGM_CREATE 0x00001000
6094 * STGM_CONVERT 0x00020000
6095 * STGM_FAILIFTHERE 0x00000000
6097 * STGM_NOSCRATCH 0x00100000
6098 * STGM_NOSNAPSHOT 0x00200000
6100 static HRESULT validateSTGM(DWORD stgm)
6102 DWORD access = STGM_ACCESS_MODE(stgm);
6103 DWORD share = STGM_SHARE_MODE(stgm);
6104 DWORD create = STGM_CREATE_MODE(stgm);
6106 if (stgm&~STGM_KNOWN_FLAGS)
6108 ERR("unknown flags %08lx\n", stgm);
6116 case STGM_READWRITE:
6124 case STGM_SHARE_DENY_NONE:
6125 case STGM_SHARE_DENY_READ:
6126 case STGM_SHARE_DENY_WRITE:
6127 case STGM_SHARE_EXCLUSIVE:
6136 case STGM_FAILIFTHERE:
6143 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6145 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6149 * STGM_CREATE | STGM_CONVERT
6150 * if both are false, STGM_FAILIFTHERE is set to TRUE
6152 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6156 * STGM_NOSCRATCH requires STGM_TRANSACTED
6158 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6162 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6163 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6165 if ( (stgm & STGM_NOSNAPSHOT) &&
6166 (!(stgm & STGM_TRANSACTED) ||
6167 share == STGM_SHARE_EXCLUSIVE ||
6168 share == STGM_SHARE_DENY_WRITE) )
6174 /****************************************************************************
6175 * GetShareModeFromSTGM
6177 * This method will return a share mode flag from a STGM value.
6178 * The STGM value is assumed valid.
6180 static DWORD GetShareModeFromSTGM(DWORD stgm)
6182 switch (STGM_SHARE_MODE(stgm))
6184 case STGM_SHARE_DENY_NONE:
6185 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6186 case STGM_SHARE_DENY_READ:
6187 return FILE_SHARE_WRITE;
6188 case STGM_SHARE_DENY_WRITE:
6189 return FILE_SHARE_READ;
6190 case STGM_SHARE_EXCLUSIVE:
6193 ERR("Invalid share mode!\n");
6198 /****************************************************************************
6199 * GetAccessModeFromSTGM
6201 * This method will return an access mode flag from a STGM value.
6202 * The STGM value is assumed valid.
6204 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6206 switch (STGM_ACCESS_MODE(stgm))
6209 return GENERIC_READ;
6211 case STGM_READWRITE:
6212 return GENERIC_READ | GENERIC_WRITE;
6214 ERR("Invalid access mode!\n");
6219 /****************************************************************************
6220 * GetCreationModeFromSTGM
6222 * This method will return a creation mode flag from a STGM value.
6223 * The STGM value is assumed valid.
6225 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6227 switch(STGM_CREATE_MODE(stgm))
6230 return CREATE_ALWAYS;
6232 FIXME("STGM_CONVERT not implemented!\n");
6234 case STGM_FAILIFTHERE:
6237 ERR("Invalid create mode!\n");
6243 /*************************************************************************
6244 * OLECONVERT_LoadOLE10 [Internal]
6246 * Loads the OLE10 STREAM to memory
6249 * pOleStream [I] The OLESTREAM
6250 * pData [I] Data Structure for the OLESTREAM Data
6254 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6255 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6258 * This function is used by OleConvertOLESTREAMToIStorage only.
6260 * Memory allocated for pData must be freed by the caller
6262 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6265 HRESULT hRes = S_OK;
6269 pData->pData = NULL;
6270 pData->pstrOleObjFileName = (CHAR *) NULL;
6272 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6275 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6276 if(dwSize != sizeof(pData->dwOleID))
6278 hRes = CONVERT10_E_OLESTREAM_GET;
6280 else if(pData->dwOleID != OLESTREAM_ID)
6282 hRes = CONVERT10_E_OLESTREAM_FMT;
6293 /* Get the TypeID...more info needed for this field */
6294 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6295 if(dwSize != sizeof(pData->dwTypeID))
6297 hRes = CONVERT10_E_OLESTREAM_GET;
6302 if(pData->dwTypeID != 0)
6304 /* Get the length of the OleTypeName */
6305 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6306 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6308 hRes = CONVERT10_E_OLESTREAM_GET;
6313 if(pData->dwOleTypeNameLength > 0)
6315 /* Get the OleTypeName */
6316 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6317 if(dwSize != pData->dwOleTypeNameLength)
6319 hRes = CONVERT10_E_OLESTREAM_GET;
6325 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6326 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6328 hRes = CONVERT10_E_OLESTREAM_GET;
6332 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6333 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6334 pData->pstrOleObjFileName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6335 if(pData->pstrOleObjFileName)
6337 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6338 if(dwSize != pData->dwOleObjFileNameLength)
6340 hRes = CONVERT10_E_OLESTREAM_GET;
6344 hRes = CONVERT10_E_OLESTREAM_GET;
6349 /* Get the Width of the Metafile */
6350 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6351 if(dwSize != sizeof(pData->dwMetaFileWidth))
6353 hRes = CONVERT10_E_OLESTREAM_GET;
6357 /* Get the Height of the Metafile */
6358 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6359 if(dwSize != sizeof(pData->dwMetaFileHeight))
6361 hRes = CONVERT10_E_OLESTREAM_GET;
6367 /* Get the Length of the Data */
6368 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6369 if(dwSize != sizeof(pData->dwDataLength))
6371 hRes = CONVERT10_E_OLESTREAM_GET;
6375 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6377 if(!bStrem1) /* if it is a second OLE stream data */
6379 pData->dwDataLength -= 8;
6380 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6381 if(dwSize != sizeof(pData->strUnknown))
6383 hRes = CONVERT10_E_OLESTREAM_GET;
6389 if(pData->dwDataLength > 0)
6391 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6393 /* Get Data (ex. IStorage, Metafile, or BMP) */
6396 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6397 if(dwSize != pData->dwDataLength)
6399 hRes = CONVERT10_E_OLESTREAM_GET;
6404 hRes = CONVERT10_E_OLESTREAM_GET;
6413 /*************************************************************************
6414 * OLECONVERT_SaveOLE10 [Internal]
6416 * Saves the OLE10 STREAM From memory
6419 * pData [I] Data Structure for the OLESTREAM Data
6420 * pOleStream [I] The OLESTREAM to save
6424 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6427 * This function is used by OleConvertIStorageToOLESTREAM only.
6430 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6433 HRESULT hRes = S_OK;
6437 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6438 if(dwSize != sizeof(pData->dwOleID))
6440 hRes = CONVERT10_E_OLESTREAM_PUT;
6445 /* Set the TypeID */
6446 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6447 if(dwSize != sizeof(pData->dwTypeID))
6449 hRes = CONVERT10_E_OLESTREAM_PUT;
6453 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6455 /* Set the Length of the OleTypeName */
6456 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6457 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6459 hRes = CONVERT10_E_OLESTREAM_PUT;
6464 if(pData->dwOleTypeNameLength > 0)
6466 /* Set the OleTypeName */
6467 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6468 if(dwSize != pData->dwOleTypeNameLength)
6470 hRes = CONVERT10_E_OLESTREAM_PUT;
6477 /* Set the width of the Metafile */
6478 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6479 if(dwSize != sizeof(pData->dwMetaFileWidth))
6481 hRes = CONVERT10_E_OLESTREAM_PUT;
6487 /* Set the height of the Metafile */
6488 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6489 if(dwSize != sizeof(pData->dwMetaFileHeight))
6491 hRes = CONVERT10_E_OLESTREAM_PUT;
6497 /* Set the length of the Data */
6498 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6499 if(dwSize != sizeof(pData->dwDataLength))
6501 hRes = CONVERT10_E_OLESTREAM_PUT;
6507 if(pData->dwDataLength > 0)
6509 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6510 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6511 if(dwSize != pData->dwDataLength)
6513 hRes = CONVERT10_E_OLESTREAM_PUT;
6521 /*************************************************************************
6522 * OLECONVERT_GetOLE20FromOLE10[Internal]
6524 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6525 * opens it, and copies the content to the dest IStorage for
6526 * OleConvertOLESTREAMToIStorage
6530 * pDestStorage [I] The IStorage to copy the data to
6531 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6532 * nBufferLength [I] The size of the buffer
6541 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6545 IStorage *pTempStorage;
6546 DWORD dwNumOfBytesWritten;
6547 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6548 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6550 /* Create a temp File */
6551 GetTempPathW(MAX_PATH, wstrTempDir);
6552 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6553 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6555 if(hFile != INVALID_HANDLE_VALUE)
6557 /* Write IStorage Data to File */
6558 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6561 /* Open and copy temp storage to the Dest Storage */
6562 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6565 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6566 StorageBaseImpl_Release(pTempStorage);
6568 DeleteFileW(wstrTempFile);
6573 /*************************************************************************
6574 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6576 * Saves the OLE10 STREAM From memory
6579 * pStorage [I] The Src IStorage to copy
6580 * pData [I] The Dest Memory to write to.
6583 * The size in bytes allocated for pData
6586 * Memory allocated for pData must be freed by the caller
6588 * Used by OleConvertIStorageToOLESTREAM only.
6591 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6595 DWORD nDataLength = 0;
6596 IStorage *pTempStorage;
6597 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6598 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6602 /* Create temp Storage */
6603 GetTempPathW(MAX_PATH, wstrTempDir);
6604 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6605 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6609 /* Copy Src Storage to the Temp Storage */
6610 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6611 StorageBaseImpl_Release(pTempStorage);
6613 /* Open Temp Storage as a file and copy to memory */
6614 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6615 if(hFile != INVALID_HANDLE_VALUE)
6617 nDataLength = GetFileSize(hFile, NULL);
6618 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6619 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6622 DeleteFileW(wstrTempFile);
6627 /*************************************************************************
6628 * OLECONVERT_CreateOleStream [Internal]
6630 * Creates the "\001OLE" stream in the IStorage if necessary.
6633 * pStorage [I] Dest storage to create the stream in
6639 * This function is used by OleConvertOLESTREAMToIStorage only.
6641 * This stream is still unknown, MS Word seems to have extra data
6642 * but since the data is stored in the OLESTREAM there should be
6643 * no need to recreate the stream. If the stream is manually
6644 * deleted it will create it with this default data.
6647 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6651 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6652 BYTE pOleStreamHeader [] =
6654 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6655 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6656 0x00, 0x00, 0x00, 0x00
6659 /* Create stream if not present */
6660 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6661 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6665 /* Write default Data */
6666 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6667 IStream_Release(pStream);
6671 /* write a string to a stream, preceded by its length */
6672 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6679 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6680 r = IStream_Write( stm, &len, sizeof(len), NULL);
6685 str = CoTaskMemAlloc( len );
6686 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6687 r = IStream_Write( stm, str, len, NULL);
6688 CoTaskMemFree( str );
6692 /* read a string preceded by its length from a stream */
6693 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6696 DWORD len, count = 0;
6700 r = IStream_Read( stm, &len, sizeof(len), &count );
6703 if( count != sizeof(len) )
6704 return E_OUTOFMEMORY;
6706 TRACE("%ld bytes\n",len);
6708 str = CoTaskMemAlloc( len );
6710 return E_OUTOFMEMORY;
6712 r = IStream_Read( stm, str, len, &count );
6717 CoTaskMemFree( str );
6718 return E_OUTOFMEMORY;
6721 TRACE("Read string %s\n",debugstr_an(str,len));
6723 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6724 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6726 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6727 CoTaskMemFree( str );
6735 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6736 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6740 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6742 static const BYTE unknown1[12] =
6743 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6744 0xFF, 0xFF, 0xFF, 0xFF};
6745 static const BYTE unknown2[16] =
6746 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6747 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6749 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6750 debugstr_w(lpszUserType), debugstr_w(szClipName),
6751 debugstr_w(szProgIDName));
6753 /* Create a CompObj stream if it doesn't exist */
6754 r = IStorage_CreateStream(pstg, szwStreamName,
6755 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6759 /* Write CompObj Structure to stream */
6760 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6762 if( SUCCEEDED( r ) )
6763 r = WriteClassStm( pstm, clsid );
6765 if( SUCCEEDED( r ) )
6766 r = STREAM_WriteString( pstm, lpszUserType );
6767 if( SUCCEEDED( r ) )
6768 r = STREAM_WriteString( pstm, szClipName );
6769 if( SUCCEEDED( r ) )
6770 r = STREAM_WriteString( pstm, szProgIDName );
6771 if( SUCCEEDED( r ) )
6772 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6774 IStream_Release( pstm );
6779 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6780 static HRESULT CLSIDFromUserType(LPCWSTR lpszUserType, CLSID *clsid)
6785 HKEY hkey, hkeyclsid;
6786 LPWSTR buffer = NULL;
6788 static const WCHAR szclsid[] = { 'C','L','S','I','D',0 };
6790 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType));
6792 r = RegOpenKeyW( HKEY_CLASSES_ROOT, szclsid, &hkeyclsid );
6794 return E_INVALIDARG;
6796 len = lstrlenW( lpszUserType ) + 1;
6797 buffer = CoTaskMemAlloc( len * sizeof (WCHAR) );
6801 for(i=0; !found; i++ )
6803 r = RegEnumKeyW( hkeyclsid, i, szKey, sizeof(szKey)/sizeof(WCHAR));
6804 if( r != ERROR_SUCCESS )
6807 r = RegOpenKeyW( hkeyclsid, szKey, &hkey );
6808 if( r != ERROR_SUCCESS )
6810 count = len * sizeof (WCHAR);
6811 r = RegQueryValueW( hkey, NULL, buffer, &count );
6812 found = ( r == ERROR_SUCCESS ) &&
6813 ( count == len*sizeof(WCHAR) ) &&
6814 !lstrcmpW( buffer, lpszUserType ) ;
6815 RegCloseKey( hkey );
6820 CoTaskMemFree( buffer );
6821 RegCloseKey( hkeyclsid );
6824 return E_INVALIDARG;
6826 TRACE("clsid is %s\n", debugstr_w( szKey ) );
6828 r = CLSIDFromString( szKey, clsid );
6834 /***********************************************************************
6835 * WriteFmtUserTypeStg (OLE32.@)
6837 HRESULT WINAPI WriteFmtUserTypeStg(
6838 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6841 WCHAR szwClipName[0x40];
6842 WCHAR szCLSIDName[OLESTREAM_MAX_STR_LEN];
6846 LPMALLOC allocator = NULL;
6848 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6850 r = CoGetMalloc(0, &allocator);
6852 return E_OUTOFMEMORY;
6854 /* get the clipboard format name */
6855 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6858 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6862 r = CLSIDFromUserType(lpszUserType, &clsid);
6866 TRACE("CLSID is %s\n",debugstr_guid(&clsid));
6868 /* get the real program ID */
6869 r = ProgIDFromCLSID( &clsid, &wstrProgID);
6873 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6875 /* if we have a good string, write the stream */
6877 r = STORAGE_WriteCompObj( pstg, &clsid,
6878 lpszUserType, szwClipName, wstrProgID );
6882 IMalloc_Free( allocator, wstrProgID);
6888 /******************************************************************************
6889 * ReadFmtUserTypeStg [OLE32.@]
6891 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6895 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6896 unsigned char unknown1[12];
6897 unsigned char unknown2[16];
6899 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6902 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6904 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6905 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6908 WARN("Failed to open stream r = %08lx\n", r);
6912 /* read the various parts of the structure */
6913 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6914 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6916 r = ReadClassStm( stm, &clsid );
6920 r = STREAM_ReadString( stm, &szCLSIDName );
6924 r = STREAM_ReadString( stm, &szOleTypeName );
6928 r = STREAM_ReadString( stm, &szProgIDName );
6932 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6933 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6936 /* ok, success... now we just need to store what we found */
6938 *pcf = RegisterClipboardFormatW( szOleTypeName );
6939 CoTaskMemFree( szOleTypeName );
6941 if( lplpszUserType )
6942 *lplpszUserType = szCLSIDName;
6943 CoTaskMemFree( szProgIDName );
6946 IStream_Release( stm );
6952 /*************************************************************************
6953 * OLECONVERT_CreateCompObjStream [Internal]
6955 * Creates a "\001CompObj" is the destination IStorage if necessary.
6958 * pStorage [I] The dest IStorage to create the CompObj Stream
6960 * strOleTypeName [I] The ProgID
6964 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6967 * This function is used by OleConvertOLESTREAMToIStorage only.
6969 * The stream data is stored in the OLESTREAM and there should be
6970 * no need to recreate the stream. If the stream is manually
6971 * deleted it will attempt to create it by querying the registry.
6975 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6978 HRESULT hStorageRes, hRes = S_OK;
6979 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6980 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6981 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
6983 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6984 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6986 /* Initialize the CompObj structure */
6987 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6988 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6989 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6992 /* Create a CompObj stream if it doesn't exist */
6993 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6994 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6995 if(hStorageRes == S_OK)
6997 /* copy the OleTypeName to the compobj struct */
6998 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6999 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7001 /* copy the OleTypeName to the compobj struct */
7002 /* Note: in the test made, these were Identical */
7003 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7004 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7007 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7008 bufferW, OLESTREAM_MAX_STR_LEN );
7009 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7015 /* Get the CLSID Default Name from the Registry */
7016 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7017 if(hErr == ERROR_SUCCESS)
7019 char strTemp[OLESTREAM_MAX_STR_LEN];
7020 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7021 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
7022 if(hErr == ERROR_SUCCESS)
7024 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7030 /* Write CompObj Structure to stream */
7031 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7033 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7035 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7036 if(IStorageCompObj.dwCLSIDNameLength > 0)
7038 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7040 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7041 if(IStorageCompObj.dwOleTypeNameLength > 0)
7043 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7045 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7046 if(IStorageCompObj.dwProgIDNameLength > 0)
7048 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7050 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7051 IStream_Release(pStream);
7057 /*************************************************************************
7058 * OLECONVERT_CreateOlePresStream[Internal]
7060 * Creates the "\002OlePres000" Stream with the Metafile data
7063 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7064 * dwExtentX [I] Width of the Metafile
7065 * dwExtentY [I] Height of the Metafile
7066 * pData [I] Metafile data
7067 * dwDataLength [I] Size of the Metafile data
7071 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7074 * This function is used by OleConvertOLESTREAMToIStorage only.
7077 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7081 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7082 BYTE pOlePresStreamHeader [] =
7084 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7085 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7086 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7087 0x00, 0x00, 0x00, 0x00
7090 BYTE pOlePresStreamHeaderEmpty [] =
7092 0x00, 0x00, 0x00, 0x00,
7093 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7094 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7095 0x00, 0x00, 0x00, 0x00
7098 /* Create the OlePres000 Stream */
7099 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7100 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7105 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7107 memset(&OlePres, 0, sizeof(OlePres));
7108 /* Do we have any metafile data to save */
7109 if(dwDataLength > 0)
7111 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7112 nHeaderSize = sizeof(pOlePresStreamHeader);
7116 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7117 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7119 /* Set width and height of the metafile */
7120 OlePres.dwExtentX = dwExtentX;
7121 OlePres.dwExtentY = -dwExtentY;
7123 /* Set Data and Length */
7124 if(dwDataLength > sizeof(METAFILEPICT16))
7126 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7127 OlePres.pData = &(pData[8]);
7129 /* Save OlePres000 Data to Stream */
7130 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7131 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7132 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7133 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7134 if(OlePres.dwSize > 0)
7136 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7138 IStream_Release(pStream);
7142 /*************************************************************************
7143 * OLECONVERT_CreateOle10NativeStream [Internal]
7145 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7148 * pStorage [I] Dest storage to create the stream in
7149 * pData [I] Ole10 Native Data (ex. bmp)
7150 * dwDataLength [I] Size of the Ole10 Native Data
7156 * This function is used by OleConvertOLESTREAMToIStorage only.
7158 * Might need to verify the data and return appropriate error message
7161 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7165 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7167 /* Create the Ole10Native Stream */
7168 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7169 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7173 /* Write info to stream */
7174 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7175 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7176 IStream_Release(pStream);
7181 /*************************************************************************
7182 * OLECONVERT_GetOLE10ProgID [Internal]
7184 * Finds the ProgID (or OleTypeID) from the IStorage
7187 * pStorage [I] The Src IStorage to get the ProgID
7188 * strProgID [I] the ProgID string to get
7189 * dwSize [I] the size of the string
7193 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7196 * This function is used by OleConvertIStorageToOLESTREAM only.
7200 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7204 LARGE_INTEGER iSeekPos;
7205 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7206 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7208 /* Open the CompObj Stream */
7209 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7210 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7214 /*Get the OleType from the CompObj Stream */
7215 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7216 iSeekPos.u.HighPart = 0;
7218 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7219 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7220 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7221 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7222 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7223 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7224 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7226 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7229 IStream_Read(pStream, strProgID, *dwSize, NULL);
7231 IStream_Release(pStream);
7236 LPOLESTR wstrProgID;
7238 /* Get the OleType from the registry */
7239 REFCLSID clsid = &(stat.clsid);
7240 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7241 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7244 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7251 /*************************************************************************
7252 * OLECONVERT_GetOle10PresData [Internal]
7254 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7257 * pStorage [I] Src IStroage
7258 * pOleStream [I] Dest OleStream Mem Struct
7264 * This function is used by OleConvertIStorageToOLESTREAM only.
7266 * Memory allocated for pData must be freed by the caller
7270 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7275 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7277 /* Initialize Default data for OLESTREAM */
7278 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7279 pOleStreamData[0].dwTypeID = 2;
7280 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7281 pOleStreamData[1].dwTypeID = 0;
7282 pOleStreamData[0].dwMetaFileWidth = 0;
7283 pOleStreamData[0].dwMetaFileHeight = 0;
7284 pOleStreamData[0].pData = NULL;
7285 pOleStreamData[1].pData = NULL;
7287 /* Open Ole10Native Stream */
7288 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7289 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7293 /* Read Size and Data */
7294 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7295 if(pOleStreamData->dwDataLength > 0)
7297 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7298 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7300 IStream_Release(pStream);
7306 /*************************************************************************
7307 * OLECONVERT_GetOle20PresData[Internal]
7309 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7312 * pStorage [I] Src IStroage
7313 * pOleStreamData [I] Dest OleStream Mem Struct
7319 * This function is used by OleConvertIStorageToOLESTREAM only.
7321 * Memory allocated for pData must be freed by the caller
7323 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7327 OLECONVERT_ISTORAGE_OLEPRES olePress;
7328 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7330 /* Initialize Default data for OLESTREAM */
7331 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7332 pOleStreamData[0].dwTypeID = 2;
7333 pOleStreamData[0].dwMetaFileWidth = 0;
7334 pOleStreamData[0].dwMetaFileHeight = 0;
7335 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7336 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7337 pOleStreamData[1].dwTypeID = 0;
7338 pOleStreamData[1].dwOleTypeNameLength = 0;
7339 pOleStreamData[1].strOleTypeName[0] = 0;
7340 pOleStreamData[1].dwMetaFileWidth = 0;
7341 pOleStreamData[1].dwMetaFileHeight = 0;
7342 pOleStreamData[1].pData = NULL;
7343 pOleStreamData[1].dwDataLength = 0;
7346 /* Open OlePress000 stream */
7347 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7348 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7351 LARGE_INTEGER iSeekPos;
7352 METAFILEPICT16 MetaFilePict;
7353 static const char strMetafilePictName[] = "METAFILEPICT";
7355 /* Set the TypeID for a Metafile */
7356 pOleStreamData[1].dwTypeID = 5;
7358 /* Set the OleTypeName to Metafile */
7359 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7360 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7362 iSeekPos.u.HighPart = 0;
7363 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7365 /* Get Presentation Data */
7366 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7367 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7368 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7369 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7371 /*Set width and Height */
7372 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7373 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7374 if(olePress.dwSize > 0)
7377 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7379 /* Set MetaFilePict struct */
7380 MetaFilePict.mm = 8;
7381 MetaFilePict.xExt = olePress.dwExtentX;
7382 MetaFilePict.yExt = olePress.dwExtentY;
7383 MetaFilePict.hMF = 0;
7385 /* Get Metafile Data */
7386 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7387 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7388 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7390 IStream_Release(pStream);
7394 /*************************************************************************
7395 * OleConvertOLESTREAMToIStorage [OLE32.@]
7400 * DVTARGETDEVICE paramenter is not handled
7401 * Still unsure of some mem fields for OLE 10 Stream
7402 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7403 * and "\001OLE" streams
7406 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7407 LPOLESTREAM pOleStream,
7409 const DVTARGETDEVICE* ptd)
7413 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7415 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7419 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7422 if(pstg == NULL || pOleStream == NULL)
7424 hRes = E_INVALIDARG;
7429 /* Load the OLESTREAM to Memory */
7430 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7435 /* Load the OLESTREAM to Memory (part 2)*/
7436 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7442 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7444 /* Do we have the IStorage Data in the OLESTREAM */
7445 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7447 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7448 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7452 /* It must be an original OLE 1.0 source */
7453 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7458 /* It must be an original OLE 1.0 source */
7459 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7462 /* Create CompObj Stream if necessary */
7463 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7466 /*Create the Ole Stream if necessary */
7467 OLECONVERT_CreateOleStream(pstg);
7472 /* Free allocated memory */
7473 for(i=0; i < 2; i++)
7475 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7476 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7477 pOleStreamData[i].pstrOleObjFileName = NULL;
7482 /*************************************************************************
7483 * OleConvertIStorageToOLESTREAM [OLE32.@]
7490 * Still unsure of some mem fields for OLE 10 Stream
7491 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7492 * and "\001OLE" streams.
7495 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7497 LPOLESTREAM pOleStream)
7500 HRESULT hRes = S_OK;
7502 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7503 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7506 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7508 if(pstg == NULL || pOleStream == NULL)
7510 hRes = E_INVALIDARG;
7514 /* Get the ProgID */
7515 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7516 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7520 /* Was it originally Ole10 */
7521 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7524 IStream_Release(pStream);
7525 /* Get Presentation Data for Ole10Native */
7526 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7530 /* Get Presentation Data (OLE20) */
7531 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7534 /* Save OLESTREAM */
7535 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7538 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7543 /* Free allocated memory */
7544 for(i=0; i < 2; i++)
7546 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7552 /***********************************************************************
7553 * GetConvertStg (OLE32.@)
7555 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7556 FIXME("unimplemented stub!\n");
7560 /******************************************************************************
7561 * StgIsStorageFile [OLE32.@]
7564 StgIsStorageFile(LPCOLESTR fn)
7570 TRACE("(\'%s\')\n", debugstr_w(fn));
7571 hf = CreateFileW(fn, GENERIC_READ,
7572 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7573 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7575 if (hf == INVALID_HANDLE_VALUE)
7576 return STG_E_FILENOTFOUND;
7578 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7580 WARN(" unable to read file\n");
7587 if (bytes_read != 8) {
7588 WARN(" too short\n");
7592 if (!memcmp(magic,STORAGE_magic,8)) {
7597 WARN(" -> Invalid header.\n");