2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
44 #include "storage32.h"
45 #include "ole2.h" /* For Write/ReadClassStm */
48 #include "wine/wingdi16.h"
50 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 OLECHAR *newProperty,
148 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
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;
270 * Check that we obtained an interface.
273 return E_NOINTERFACE;
276 * Query Interface always increases the reference count by one when it is
279 StorageBaseImpl_AddRef(iface);
284 /************************************************************************
285 * Storage32BaseImpl_AddRef (IUnknown)
287 * This method implements the common AddRef for all IStorage32
288 * implementations contained in this file.
290 * See Windows documentation for more details on IUnknown methods.
292 ULONG WINAPI StorageBaseImpl_AddRef(
295 StorageBaseImpl *This = (StorageBaseImpl *)iface;
296 return InterlockedIncrement(&This->ref);
299 /************************************************************************
300 * Storage32BaseImpl_Release (IUnknown)
302 * This method implements the common Release for all IStorage32
303 * implementations contained in this file.
305 * See Windows documentation for more details on IUnknown methods.
307 ULONG WINAPI StorageBaseImpl_Release(
310 StorageBaseImpl *This = (StorageBaseImpl *)iface;
312 * Decrease the reference count on this object.
314 ULONG ref = InterlockedDecrement(&This->ref);
317 * If the reference count goes down to 0, perform suicide.
322 * Since we are using a system of base-classes, we want to call the
323 * destructor of the appropriate derived class. To do this, we are
324 * using virtual functions to implement the destructor.
326 This->v_destructor(This);
332 /************************************************************************
333 * Storage32BaseImpl_OpenStream (IStorage)
335 * This method will open the specified stream object from the current storage.
337 * See Windows documentation for more details on IStorage methods.
339 HRESULT WINAPI StorageBaseImpl_OpenStream(
341 const OLECHAR* pwcsName, /* [string][in] */
342 void* reserved1, /* [unique][in] */
343 DWORD grfMode, /* [in] */
344 DWORD reserved2, /* [in] */
345 IStream** ppstm) /* [out] */
347 StorageBaseImpl *This = (StorageBaseImpl *)iface;
348 IEnumSTATSTGImpl* propertyEnumeration;
349 StgStreamImpl* newStream;
350 StgProperty currentProperty;
351 ULONG foundPropertyIndex;
352 HRESULT res = STG_E_UNKNOWN;
354 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
355 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
358 * Perform a sanity check on the parameters.
360 if ( (pwcsName==NULL) || (ppstm==0) )
367 * Initialize the out parameter
372 * Validate the STGM flags
374 if ( FAILED( validateSTGM(grfMode) ))
376 res = STG_E_INVALIDFLAG;
383 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
384 (grfMode & STGM_DELETEONRELEASE) ||
385 (grfMode & STGM_TRANSACTED) )
387 res = STG_E_INVALIDFUNCTION;
392 * Create a property enumeration to search the properties
394 propertyEnumeration = IEnumSTATSTGImpl_Construct(
395 This->ancestorStorage,
396 This->rootPropertySetIndex);
399 * Search the enumeration for the property with the given name
401 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
407 * Delete the property enumeration since we don't need it anymore
409 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
412 * If it was found, construct the stream object and return a pointer to it.
414 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
415 (currentProperty.propertyType==PROPTYPE_STREAM) )
417 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
421 newStream->grfMode = grfMode;
422 *ppstm = (IStream*)newStream;
425 * Since we are returning a pointer to the interface, we have to
426 * nail down the reference.
428 StgStreamImpl_AddRef(*ppstm);
438 res = STG_E_FILENOTFOUND;
442 TRACE("<-- IStream %p\n", *ppstm);
443 TRACE("<-- %08lx\n", res);
447 /************************************************************************
448 * Storage32BaseImpl_OpenStorage (IStorage)
450 * This method will open a new storage object from the current storage.
452 * See Windows documentation for more details on IStorage methods.
454 HRESULT WINAPI StorageBaseImpl_OpenStorage(
456 const OLECHAR* pwcsName, /* [string][unique][in] */
457 IStorage* pstgPriority, /* [unique][in] */
458 DWORD grfMode, /* [in] */
459 SNB snbExclude, /* [unique][in] */
460 DWORD reserved, /* [in] */
461 IStorage** ppstg) /* [out] */
463 StorageBaseImpl *This = (StorageBaseImpl *)iface;
464 StorageInternalImpl* newStorage;
465 IEnumSTATSTGImpl* propertyEnumeration;
466 StgProperty currentProperty;
467 ULONG foundPropertyIndex;
468 HRESULT res = STG_E_UNKNOWN;
470 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
471 iface, debugstr_w(pwcsName), pstgPriority,
472 grfMode, snbExclude, reserved, ppstg);
475 * Perform a sanity check on the parameters.
477 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
484 if (snbExclude != NULL)
486 res = STG_E_INVALIDPARAMETER;
491 * Validate the STGM flags
493 if ( FAILED( validateSTGM(grfMode) ))
495 res = STG_E_INVALIDFLAG;
502 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
503 (grfMode & STGM_DELETEONRELEASE) ||
504 (grfMode & STGM_PRIORITY) )
506 res = STG_E_INVALIDFUNCTION;
511 * Initialize the out parameter
516 * Create a property enumeration to search the properties
518 propertyEnumeration = IEnumSTATSTGImpl_Construct(
519 This->ancestorStorage,
520 This->rootPropertySetIndex);
523 * Search the enumeration for the property with the given name
525 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
531 * Delete the property enumeration since we don't need it anymore
533 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
536 * If it was found, construct the stream object and return a pointer to it.
538 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
539 (currentProperty.propertyType==PROPTYPE_STORAGE) )
542 * Construct a new Storage object
544 newStorage = StorageInternalImpl_Construct(
545 This->ancestorStorage,
550 *ppstg = (IStorage*)newStorage;
553 * Since we are returning a pointer to the interface,
554 * we have to nail down the reference.
556 StorageBaseImpl_AddRef(*ppstg);
562 res = STG_E_INSUFFICIENTMEMORY;
566 res = STG_E_FILENOTFOUND;
569 TRACE("<-- %08lx\n", res);
573 /************************************************************************
574 * Storage32BaseImpl_EnumElements (IStorage)
576 * This method will create an enumerator object that can be used to
577 * retrieve informatino about all the properties in the storage object.
579 * See Windows documentation for more details on IStorage methods.
581 HRESULT WINAPI StorageBaseImpl_EnumElements(
583 DWORD reserved1, /* [in] */
584 void* reserved2, /* [size_is][unique][in] */
585 DWORD reserved3, /* [in] */
586 IEnumSTATSTG** ppenum) /* [out] */
588 StorageBaseImpl *This = (StorageBaseImpl *)iface;
589 IEnumSTATSTGImpl* newEnum;
591 TRACE("(%p, %ld, %p, %ld, %p)\n",
592 iface, reserved1, reserved2, reserved3, ppenum);
595 * Perform a sanity check on the parameters.
597 if ( (This==0) || (ppenum==0))
601 * Construct the enumerator.
603 newEnum = IEnumSTATSTGImpl_Construct(
604 This->ancestorStorage,
605 This->rootPropertySetIndex);
609 *ppenum = (IEnumSTATSTG*)newEnum;
612 * Don't forget to nail down a reference to the new object before
615 IEnumSTATSTGImpl_AddRef(*ppenum);
620 return E_OUTOFMEMORY;
623 /************************************************************************
624 * Storage32BaseImpl_Stat (IStorage)
626 * This method will retrieve information about this storage object.
628 * See Windows documentation for more details on IStorage methods.
630 HRESULT WINAPI StorageBaseImpl_Stat(
632 STATSTG* pstatstg, /* [out] */
633 DWORD grfStatFlag) /* [in] */
635 StorageBaseImpl *This = (StorageBaseImpl *)iface;
636 StgProperty curProperty;
638 HRESULT res = STG_E_UNKNOWN;
640 TRACE("(%p, %p, %lx)\n",
641 iface, pstatstg, grfStatFlag);
644 * Perform a sanity check on the parameters.
646 if ( (This==0) || (pstatstg==0))
653 * Read the information from the property.
655 readSuccessful = StorageImpl_ReadProperty(
656 This->ancestorStorage,
657 This->rootPropertySetIndex,
662 StorageUtl_CopyPropertyToSTATSTG(
676 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);
678 TRACE("<-- %08lx\n", res);
682 /************************************************************************
683 * Storage32BaseImpl_RenameElement (IStorage)
685 * This method will rename the specified element.
687 * See Windows documentation for more details on IStorage methods.
689 * Implementation notes: The method used to rename consists of creating a clone
690 * of the deleted StgProperty object setting it with the new name and to
691 * perform a DestroyElement of the old StgProperty.
693 HRESULT WINAPI StorageBaseImpl_RenameElement(
695 const OLECHAR* pwcsOldName, /* [in] */
696 const OLECHAR* pwcsNewName) /* [in] */
698 StorageBaseImpl *This = (StorageBaseImpl *)iface;
699 IEnumSTATSTGImpl* propertyEnumeration;
700 StgProperty currentProperty;
701 ULONG foundPropertyIndex;
703 TRACE("(%p, %s, %s)\n",
704 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
707 * Create a property enumeration to search the properties
709 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
710 This->rootPropertySetIndex);
713 * Search the enumeration for the new property name
715 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
719 if (foundPropertyIndex != PROPERTY_NULL)
722 * There is already a property with the new name
724 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
725 return STG_E_FILEALREADYEXISTS;
728 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
731 * Search the enumeration for the old property name
733 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
738 * Delete the property enumeration since we don't need it anymore
740 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
742 if (foundPropertyIndex != PROPERTY_NULL)
744 StgProperty renamedProperty;
745 ULONG renamedPropertyIndex;
748 * Setup a new property for the renamed property
750 renamedProperty.sizeOfNameString =
751 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
753 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
754 return STG_E_INVALIDNAME;
756 strcpyW(renamedProperty.name, pwcsNewName);
758 renamedProperty.propertyType = currentProperty.propertyType;
759 renamedProperty.startingBlock = currentProperty.startingBlock;
760 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
761 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
763 renamedProperty.previousProperty = PROPERTY_NULL;
764 renamedProperty.nextProperty = PROPERTY_NULL;
767 * Bring the dirProperty link in case it is a storage and in which
768 * case the renamed storage elements don't require to be reorganized.
770 renamedProperty.dirProperty = currentProperty.dirProperty;
772 /* call CoFileTime to get the current time
773 renamedProperty.timeStampS1
774 renamedProperty.timeStampD1
775 renamedProperty.timeStampS2
776 renamedProperty.timeStampD2
777 renamedProperty.propertyUniqueID
781 * Obtain a free property in the property chain
783 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
786 * Save the new property into the new property spot
788 StorageImpl_WriteProperty(
789 This->ancestorStorage,
790 renamedPropertyIndex,
794 * Find a spot in the property chain for our newly created property.
798 renamedPropertyIndex,
802 * At this point the renamed property has been inserted in the tree,
803 * now, before to Destroy the old property we must zeroed it's dirProperty
804 * otherwise the DestroyProperty below will zap it all and we do not want
806 * Also, we fake that the old property is a storage so the DestroyProperty
807 * will not do a SetSize(0) on the stream data.
809 * This means that we need to tweek the StgProperty if it is a stream or a
812 StorageImpl_ReadProperty(This->ancestorStorage,
816 currentProperty.dirProperty = PROPERTY_NULL;
817 currentProperty.propertyType = PROPTYPE_STORAGE;
818 StorageImpl_WriteProperty(
819 This->ancestorStorage,
824 * Invoke Destroy to get rid of the ole property and automatically redo
825 * the linking of it's previous and next members...
827 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
833 * There is no property with the old name
835 return STG_E_FILENOTFOUND;
841 /************************************************************************
842 * Storage32BaseImpl_CreateStream (IStorage)
844 * This method will create a stream object within this storage
846 * See Windows documentation for more details on IStorage methods.
848 HRESULT WINAPI StorageBaseImpl_CreateStream(
850 const OLECHAR* pwcsName, /* [string][in] */
851 DWORD grfMode, /* [in] */
852 DWORD reserved1, /* [in] */
853 DWORD reserved2, /* [in] */
854 IStream** ppstm) /* [out] */
856 StorageBaseImpl *This = (StorageBaseImpl *)iface;
857 IEnumSTATSTGImpl* propertyEnumeration;
858 StgStreamImpl* newStream;
859 StgProperty currentProperty, newStreamProperty;
860 ULONG foundPropertyIndex, newPropertyIndex;
862 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
863 iface, debugstr_w(pwcsName), grfMode,
864 reserved1, reserved2, ppstm);
867 * Validate parameters
870 return STG_E_INVALIDPOINTER;
873 return STG_E_INVALIDNAME;
876 * Validate the STGM flags
878 if ( FAILED( validateSTGM(grfMode) ))
879 return STG_E_INVALIDFLAG;
884 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
885 (grfMode & STGM_DELETEONRELEASE) ||
886 (grfMode & STGM_TRANSACTED) )
887 return STG_E_INVALIDFUNCTION;
890 * Initialize the out parameter
895 * Create a property enumeration to search the properties
897 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
898 This->rootPropertySetIndex);
900 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
904 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
906 if (foundPropertyIndex != PROPERTY_NULL)
909 * An element with this name already exists
911 if (grfMode & STGM_CREATE)
913 IStorage_DestroyElement(iface, pwcsName);
916 return STG_E_FILEALREADYEXISTS;
920 * memset the empty property
922 memset(&newStreamProperty, 0, sizeof(StgProperty));
924 newStreamProperty.sizeOfNameString =
925 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
927 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
928 return STG_E_INVALIDNAME;
930 strcpyW(newStreamProperty.name, pwcsName);
932 newStreamProperty.propertyType = PROPTYPE_STREAM;
933 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
934 newStreamProperty.size.u.LowPart = 0;
935 newStreamProperty.size.u.HighPart = 0;
937 newStreamProperty.previousProperty = PROPERTY_NULL;
938 newStreamProperty.nextProperty = PROPERTY_NULL;
939 newStreamProperty.dirProperty = PROPERTY_NULL;
941 /* call CoFileTime to get the current time
942 newStreamProperty.timeStampS1
943 newStreamProperty.timeStampD1
944 newStreamProperty.timeStampS2
945 newStreamProperty.timeStampD2
948 /* newStreamProperty.propertyUniqueID */
951 * Get a free property or create a new one
953 newPropertyIndex = getFreeProperty(This->ancestorStorage);
956 * Save the new property into the new property spot
958 StorageImpl_WriteProperty(
959 This->ancestorStorage,
964 * Find a spot in the property chain for our newly created property.
972 * Open the stream to return it.
974 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
978 *ppstm = (IStream*)newStream;
981 * Since we are returning a pointer to the interface, we have to nail down
984 StgStreamImpl_AddRef(*ppstm);
988 return STG_E_INSUFFICIENTMEMORY;
994 /************************************************************************
995 * Storage32BaseImpl_SetClass (IStorage)
997 * This method will write the specified CLSID in the property of this
1000 * See Windows documentation for more details on IStorage methods.
1002 HRESULT WINAPI StorageBaseImpl_SetClass(
1004 REFCLSID clsid) /* [in] */
1006 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1007 HRESULT hRes = E_FAIL;
1008 StgProperty curProperty;
1011 TRACE("(%p, %p)\n", iface, clsid);
1013 success = StorageImpl_ReadProperty(This->ancestorStorage,
1014 This->rootPropertySetIndex,
1018 curProperty.propertyUniqueID = *clsid;
1020 success = StorageImpl_WriteProperty(This->ancestorStorage,
1021 This->rootPropertySetIndex,
1030 /************************************************************************
1031 ** Storage32Impl implementation
1034 /************************************************************************
1035 * Storage32Impl_CreateStorage (IStorage)
1037 * This method will create the storage object within the provided storage.
1039 * See Windows documentation for more details on IStorage methods.
1041 HRESULT WINAPI StorageImpl_CreateStorage(
1043 const OLECHAR *pwcsName, /* [string][in] */
1044 DWORD grfMode, /* [in] */
1045 DWORD reserved1, /* [in] */
1046 DWORD reserved2, /* [in] */
1047 IStorage **ppstg) /* [out] */
1049 StorageImpl* const This=(StorageImpl*)iface;
1051 IEnumSTATSTGImpl *propertyEnumeration;
1052 StgProperty currentProperty;
1053 StgProperty newProperty;
1054 ULONG foundPropertyIndex;
1055 ULONG newPropertyIndex;
1058 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1059 iface, debugstr_w(pwcsName), grfMode,
1060 reserved1, reserved2, ppstg);
1063 * Validate parameters
1066 return STG_E_INVALIDPOINTER;
1069 return STG_E_INVALIDNAME;
1072 * Validate the STGM flags
1074 if ( FAILED( validateSTGM(grfMode) ) ||
1075 (grfMode & STGM_DELETEONRELEASE) )
1076 return STG_E_INVALIDFLAG;
1079 * Initialize the out parameter
1084 * Create a property enumeration and search the properties
1086 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1087 This->rootPropertySetIndex);
1089 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1092 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1094 if (foundPropertyIndex != PROPERTY_NULL)
1097 * An element with this name already exists
1099 if (grfMode & STGM_CREATE)
1100 IStorage_DestroyElement(iface, pwcsName);
1102 return STG_E_FILEALREADYEXISTS;
1106 * memset the empty property
1108 memset(&newProperty, 0, sizeof(StgProperty));
1110 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1112 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1113 return STG_E_INVALIDNAME;
1115 strcpyW(newProperty.name, pwcsName);
1117 newProperty.propertyType = PROPTYPE_STORAGE;
1118 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1119 newProperty.size.u.LowPart = 0;
1120 newProperty.size.u.HighPart = 0;
1122 newProperty.previousProperty = PROPERTY_NULL;
1123 newProperty.nextProperty = PROPERTY_NULL;
1124 newProperty.dirProperty = PROPERTY_NULL;
1126 /* call CoFileTime to get the current time
1127 newProperty.timeStampS1
1128 newProperty.timeStampD1
1129 newProperty.timeStampS2
1130 newProperty.timeStampD2
1133 /* newStorageProperty.propertyUniqueID */
1136 * Obtain a free property in the property chain
1138 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1141 * Save the new property into the new property spot
1143 StorageImpl_WriteProperty(
1144 This->ancestorStorage,
1149 * Find a spot in the property chain for our newly created property.
1151 updatePropertyChain(
1157 * Open it to get a pointer to return.
1159 hr = IStorage_OpenStorage(
1168 if( (hr != S_OK) || (*ppstg == NULL))
1178 /***************************************************************************
1182 * Get a free property or create a new one.
1184 static ULONG getFreeProperty(
1185 StorageImpl *storage)
1187 ULONG currentPropertyIndex = 0;
1188 ULONG newPropertyIndex = PROPERTY_NULL;
1189 BOOL readSuccessful = TRUE;
1190 StgProperty currentProperty;
1195 * Start by reading the root property
1197 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1198 currentPropertyIndex,
1202 if (currentProperty.sizeOfNameString == 0)
1205 * The property existis and is available, we found it.
1207 newPropertyIndex = currentPropertyIndex;
1213 * We exhausted the property list, we will create more space below
1215 newPropertyIndex = currentPropertyIndex;
1217 currentPropertyIndex++;
1219 } while (newPropertyIndex == PROPERTY_NULL);
1222 * grow the property chain
1224 if (! readSuccessful)
1226 StgProperty emptyProperty;
1227 ULARGE_INTEGER newSize;
1228 ULONG propertyIndex;
1229 ULONG lastProperty = 0;
1230 ULONG blockCount = 0;
1233 * obtain the new count of property blocks
1235 blockCount = BlockChainStream_GetCount(
1236 storage->ancestorStorage->rootBlockChain)+1;
1239 * initialize the size used by the property stream
1241 newSize.u.HighPart = 0;
1242 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1245 * add a property block to the property chain
1247 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1250 * memset the empty property in order to initialize the unused newly
1253 memset(&emptyProperty, 0, sizeof(StgProperty));
1258 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1261 propertyIndex = newPropertyIndex;
1262 propertyIndex < lastProperty;
1265 StorageImpl_WriteProperty(
1266 storage->ancestorStorage,
1272 return newPropertyIndex;
1275 /****************************************************************************
1279 * Case insensitive comparaison of StgProperty.name by first considering
1282 * Returns <0 when newPrpoerty < currentProperty
1283 * >0 when newPrpoerty > currentProperty
1284 * 0 when newPrpoerty == currentProperty
1286 static LONG propertyNameCmp(
1287 OLECHAR *newProperty,
1288 OLECHAR *currentProperty)
1290 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1295 * We compare the string themselves only when they are of the same length
1297 diff = lstrcmpiW( newProperty, currentProperty);
1303 /****************************************************************************
1307 * Properly link this new element in the property chain.
1309 static void updatePropertyChain(
1310 StorageImpl *storage,
1311 ULONG newPropertyIndex,
1312 StgProperty newProperty)
1314 StgProperty currentProperty;
1317 * Read the root property
1319 StorageImpl_ReadProperty(storage->ancestorStorage,
1320 storage->rootPropertySetIndex,
1323 if (currentProperty.dirProperty != PROPERTY_NULL)
1326 * The root storage contains some element, therefore, start the research
1327 * for the appropriate location.
1330 ULONG current, next, previous, currentPropertyId;
1333 * Keep the StgProperty sequence number of the storage first property
1335 currentPropertyId = currentProperty.dirProperty;
1340 StorageImpl_ReadProperty(storage->ancestorStorage,
1341 currentProperty.dirProperty,
1344 previous = currentProperty.previousProperty;
1345 next = currentProperty.nextProperty;
1346 current = currentPropertyId;
1350 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1354 if (previous != PROPERTY_NULL)
1356 StorageImpl_ReadProperty(storage->ancestorStorage,
1363 currentProperty.previousProperty = newPropertyIndex;
1364 StorageImpl_WriteProperty(storage->ancestorStorage,
1372 if (next != PROPERTY_NULL)
1374 StorageImpl_ReadProperty(storage->ancestorStorage,
1381 currentProperty.nextProperty = newPropertyIndex;
1382 StorageImpl_WriteProperty(storage->ancestorStorage,
1391 * Trying to insert an item with the same name in the
1392 * subtree structure.
1397 previous = currentProperty.previousProperty;
1398 next = currentProperty.nextProperty;
1404 * The root storage is empty, link the new property to it's dir property
1406 currentProperty.dirProperty = newPropertyIndex;
1407 StorageImpl_WriteProperty(storage->ancestorStorage,
1408 storage->rootPropertySetIndex,
1414 /*************************************************************************
1417 HRESULT WINAPI StorageImpl_CopyTo(
1419 DWORD ciidExclude, /* [in] */
1420 const IID* rgiidExclude, /* [size_is][unique][in] */
1421 SNB snbExclude, /* [unique][in] */
1422 IStorage* pstgDest) /* [unique][in] */
1424 IEnumSTATSTG *elements = 0;
1425 STATSTG curElement, strStat;
1427 IStorage *pstgTmp, *pstgChild;
1428 IStream *pstrTmp, *pstrChild;
1430 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1431 FIXME("Exclude option not implemented\n");
1433 TRACE("(%p, %ld, %p, %p, %p)\n",
1434 iface, ciidExclude, rgiidExclude,
1435 snbExclude, pstgDest);
1438 * Perform a sanity check
1440 if ( pstgDest == 0 )
1441 return STG_E_INVALIDPOINTER;
1444 * Enumerate the elements
1446 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1454 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1455 IStorage_SetClass( pstgDest, &curElement.clsid );
1460 * Obtain the next element
1462 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1464 if ( hr == S_FALSE )
1466 hr = S_OK; /* done, every element has been copied */
1470 if (curElement.type == STGTY_STORAGE)
1473 * open child source storage
1475 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1476 STGM_READ|STGM_SHARE_EXCLUSIVE,
1477 NULL, 0, &pstgChild );
1483 * Check if destination storage is not a child of the source
1484 * storage, which will cause an infinite loop
1486 if (pstgChild == pstgDest)
1488 IEnumSTATSTG_Release(elements);
1490 return STG_E_ACCESSDENIED;
1494 * create a new storage in destination storage
1496 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1497 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1501 * if it already exist, don't create a new one use this one
1503 if (hr == STG_E_FILEALREADYEXISTS)
1505 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1506 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1507 NULL, 0, &pstgTmp );
1515 * do the copy recursively
1517 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1518 snbExclude, pstgTmp );
1520 IStorage_Release( pstgTmp );
1521 IStorage_Release( pstgChild );
1523 else if (curElement.type == STGTY_STREAM)
1526 * create a new stream in destination storage. If the stream already
1527 * exist, it will be deleted and a new one will be created.
1529 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1530 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1537 * open child stream storage
1539 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1540 STGM_READ|STGM_SHARE_EXCLUSIVE,
1547 * Get the size of the source stream
1549 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1552 * Set the size of the destination stream.
1554 IStream_SetSize(pstrTmp, strStat.cbSize);
1559 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1562 IStream_Release( pstrTmp );
1563 IStream_Release( pstrChild );
1567 WARN("unknown element type: %ld\n", curElement.type);
1570 } while (hr == S_OK);
1575 IEnumSTATSTG_Release(elements);
1580 /*************************************************************************
1581 * MoveElementTo (IStorage)
1583 HRESULT WINAPI StorageImpl_MoveElementTo(
1585 const OLECHAR *pwcsName, /* [string][in] */
1586 IStorage *pstgDest, /* [unique][in] */
1587 const OLECHAR *pwcsNewName,/* [string][in] */
1588 DWORD grfFlags) /* [in] */
1590 FIXME("not implemented!\n");
1594 /*************************************************************************
1597 HRESULT WINAPI StorageImpl_Commit(
1599 DWORD grfCommitFlags)/* [in] */
1601 FIXME("(%ld): stub!\n", grfCommitFlags);
1605 /*************************************************************************
1608 HRESULT WINAPI StorageImpl_Revert(
1611 FIXME("not implemented!\n");
1615 /*************************************************************************
1616 * DestroyElement (IStorage)
1618 * Stategy: This implementation is build this way for simplicity not for speed.
1619 * I always delete the top most element of the enumeration and adjust
1620 * the deleted element pointer all the time. This takes longer to
1621 * do but allow to reinvoke DestroyElement whenever we encounter a
1622 * storage object. The optimisation reside in the usage of another
1623 * enumeration stategy that would give all the leaves of a storage
1624 * first. (postfix order)
1626 HRESULT WINAPI StorageImpl_DestroyElement(
1628 const OLECHAR *pwcsName)/* [string][in] */
1630 StorageImpl* const This=(StorageImpl*)iface;
1632 IEnumSTATSTGImpl* propertyEnumeration;
1635 StgProperty propertyToDelete;
1636 StgProperty parentProperty;
1637 ULONG foundPropertyIndexToDelete;
1638 ULONG typeOfRelation;
1639 ULONG parentPropertyId;
1642 iface, debugstr_w(pwcsName));
1645 * Perform a sanity check on the parameters.
1648 return STG_E_INVALIDPOINTER;
1651 * Create a property enumeration to search the property with the given name
1653 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1654 This->ancestorStorage,
1655 This->rootPropertySetIndex);
1657 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1658 propertyEnumeration,
1662 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1664 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1666 return STG_E_FILENOTFOUND;
1670 * Find the parent property of the property to delete (the one that
1671 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1672 * the parent is This. Otherwise, the parent is one of it's sibling...
1676 * First, read This's StgProperty..
1678 res = StorageImpl_ReadProperty(
1679 This->ancestorStorage,
1680 This->rootPropertySetIndex,
1686 * Second, check to see if by any chance the actual storage (This) is not
1687 * the parent of the property to delete... We never know...
1689 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1692 * Set data as it would have been done in the else part...
1694 typeOfRelation = PROPERTY_RELATION_DIR;
1695 parentPropertyId = This->rootPropertySetIndex;
1700 * Create a property enumeration to search the parent properties, and
1701 * delete it once done.
1703 IEnumSTATSTGImpl* propertyEnumeration2;
1705 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1706 This->ancestorStorage,
1707 This->rootPropertySetIndex);
1709 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1710 propertyEnumeration2,
1711 foundPropertyIndexToDelete,
1715 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1718 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1720 hr = deleteStorageProperty(
1722 foundPropertyIndexToDelete,
1725 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1727 hr = deleteStreamProperty(
1729 foundPropertyIndexToDelete,
1737 * Adjust the property chain
1739 hr = adjustPropertyChain(
1750 /************************************************************************
1751 * StorageImpl_Stat (IStorage)
1753 * This method will retrieve information about this storage object.
1755 * See Windows documentation for more details on IStorage methods.
1757 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1758 STATSTG* pstatstg, /* [out] */
1759 DWORD grfStatFlag) /* [in] */
1761 StorageImpl* const This = (StorageImpl*)iface;
1762 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1764 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1766 CoTaskMemFree(pstatstg->pwcsName);
1767 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1768 strcpyW(pstatstg->pwcsName, This->pwcsName);
1776 /*********************************************************************
1780 * Perform the deletion of a complete storage node
1783 static HRESULT deleteStorageProperty(
1784 StorageImpl *parentStorage,
1785 ULONG indexOfPropertyToDelete,
1786 StgProperty propertyToDelete)
1788 IEnumSTATSTG *elements = 0;
1789 IStorage *childStorage = 0;
1790 STATSTG currentElement;
1792 HRESULT destroyHr = S_OK;
1795 * Open the storage and enumerate it
1797 hr = StorageBaseImpl_OpenStorage(
1798 (IStorage*)parentStorage,
1799 propertyToDelete.name,
1801 STGM_SHARE_EXCLUSIVE,
1812 * Enumerate the elements
1814 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1819 * Obtain the next element
1821 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1824 destroyHr = StorageImpl_DestroyElement(
1825 (IStorage*)childStorage,
1826 (OLECHAR*)currentElement.pwcsName);
1828 CoTaskMemFree(currentElement.pwcsName);
1832 * We need to Reset the enumeration every time because we delete elements
1833 * and the enumeration could be invalid
1835 IEnumSTATSTG_Reset(elements);
1837 } while ((hr == S_OK) && (destroyHr == S_OK));
1840 * Invalidate the property by zeroing it's name member.
1842 propertyToDelete.sizeOfNameString = 0;
1844 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1845 indexOfPropertyToDelete,
1848 IStorage_Release(childStorage);
1849 IEnumSTATSTG_Release(elements);
1854 /*********************************************************************
1858 * Perform the deletion of a stream node
1861 static HRESULT deleteStreamProperty(
1862 StorageImpl *parentStorage,
1863 ULONG indexOfPropertyToDelete,
1864 StgProperty propertyToDelete)
1868 ULARGE_INTEGER size;
1870 size.u.HighPart = 0;
1873 hr = StorageBaseImpl_OpenStream(
1874 (IStorage*)parentStorage,
1875 (OLECHAR*)propertyToDelete.name,
1877 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1889 hr = IStream_SetSize(pis, size);
1897 * Release the stream object.
1899 IStream_Release(pis);
1902 * Invalidate the property by zeroing it's name member.
1904 propertyToDelete.sizeOfNameString = 0;
1907 * Here we should re-read the property so we get the updated pointer
1908 * but since we are here to zap it, I don't do it...
1910 StorageImpl_WriteProperty(
1911 parentStorage->ancestorStorage,
1912 indexOfPropertyToDelete,
1918 /*********************************************************************
1922 * Finds a placeholder for the StgProperty within the Storage
1925 static HRESULT findPlaceholder(
1926 StorageImpl *storage,
1927 ULONG propertyIndexToStore,
1928 ULONG storePropertyIndex,
1931 StgProperty storeProperty;
1936 * Read the storage property
1938 res = StorageImpl_ReadProperty(
1939 storage->ancestorStorage,
1948 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1950 if (storeProperty.previousProperty != PROPERTY_NULL)
1952 return findPlaceholder(
1954 propertyIndexToStore,
1955 storeProperty.previousProperty,
1960 storeProperty.previousProperty = propertyIndexToStore;
1963 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1965 if (storeProperty.nextProperty != PROPERTY_NULL)
1967 return findPlaceholder(
1969 propertyIndexToStore,
1970 storeProperty.nextProperty,
1975 storeProperty.nextProperty = propertyIndexToStore;
1978 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1980 if (storeProperty.dirProperty != PROPERTY_NULL)
1982 return findPlaceholder(
1984 propertyIndexToStore,
1985 storeProperty.dirProperty,
1990 storeProperty.dirProperty = propertyIndexToStore;
1994 hr = StorageImpl_WriteProperty(
1995 storage->ancestorStorage,
2007 /*************************************************************************
2011 * This method takes the previous and the next property link of a property
2012 * to be deleted and find them a place in the Storage.
2014 static HRESULT adjustPropertyChain(
2016 StgProperty propertyToDelete,
2017 StgProperty parentProperty,
2018 ULONG parentPropertyId,
2021 ULONG newLinkProperty = PROPERTY_NULL;
2022 BOOL needToFindAPlaceholder = FALSE;
2023 ULONG storeNode = PROPERTY_NULL;
2024 ULONG toStoreNode = PROPERTY_NULL;
2025 INT relationType = 0;
2029 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2031 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2034 * Set the parent previous to the property to delete previous
2036 newLinkProperty = propertyToDelete.previousProperty;
2038 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2041 * We also need to find a storage for the other link, setup variables
2042 * to do this at the end...
2044 needToFindAPlaceholder = TRUE;
2045 storeNode = propertyToDelete.previousProperty;
2046 toStoreNode = propertyToDelete.nextProperty;
2047 relationType = PROPERTY_RELATION_NEXT;
2050 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2053 * Set the parent previous to the property to delete next
2055 newLinkProperty = propertyToDelete.nextProperty;
2059 * Link it for real...
2061 parentProperty.previousProperty = newLinkProperty;
2064 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2066 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2069 * Set the parent next to the property to delete next previous
2071 newLinkProperty = propertyToDelete.previousProperty;
2073 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2076 * We also need to find a storage for the other link, setup variables
2077 * to do this at the end...
2079 needToFindAPlaceholder = TRUE;
2080 storeNode = propertyToDelete.previousProperty;
2081 toStoreNode = propertyToDelete.nextProperty;
2082 relationType = PROPERTY_RELATION_NEXT;
2085 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2088 * Set the parent next to the property to delete next
2090 newLinkProperty = propertyToDelete.nextProperty;
2094 * Link it for real...
2096 parentProperty.nextProperty = newLinkProperty;
2098 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2100 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2103 * Set the parent dir to the property to delete previous
2105 newLinkProperty = propertyToDelete.previousProperty;
2107 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2110 * We also need to find a storage for the other link, setup variables
2111 * to do this at the end...
2113 needToFindAPlaceholder = TRUE;
2114 storeNode = propertyToDelete.previousProperty;
2115 toStoreNode = propertyToDelete.nextProperty;
2116 relationType = PROPERTY_RELATION_NEXT;
2119 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2122 * Set the parent dir to the property to delete next
2124 newLinkProperty = propertyToDelete.nextProperty;
2128 * Link it for real...
2130 parentProperty.dirProperty = newLinkProperty;
2134 * Write back the parent property
2136 res = StorageImpl_WriteProperty(
2137 This->ancestorStorage,
2146 * If a placeholder is required for the other link, then, find one and
2147 * get out of here...
2149 if (needToFindAPlaceholder)
2151 hr = findPlaceholder(
2162 /******************************************************************************
2163 * SetElementTimes (IStorage)
2165 HRESULT WINAPI StorageImpl_SetElementTimes(
2167 const OLECHAR *pwcsName,/* [string][in] */
2168 const FILETIME *pctime, /* [in] */
2169 const FILETIME *patime, /* [in] */
2170 const FILETIME *pmtime) /* [in] */
2172 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2176 /******************************************************************************
2177 * SetStateBits (IStorage)
2179 HRESULT WINAPI StorageImpl_SetStateBits(
2181 DWORD grfStateBits,/* [in] */
2182 DWORD grfMask) /* [in] */
2184 FIXME("not implemented!\n");
2188 HRESULT StorageImpl_Construct(
2198 StgProperty currentProperty;
2199 BOOL readSuccessful;
2200 ULONG currentPropertyIndex;
2202 if ( FAILED( validateSTGM(openFlags) ))
2203 return STG_E_INVALIDFLAG;
2205 memset(This, 0, sizeof(StorageImpl));
2208 * Initialize the virtual function table.
2210 This->lpVtbl = &Storage32Impl_Vtbl;
2211 This->v_destructor = &StorageImpl_Destroy;
2214 * This is the top-level storage so initialize the ancestor pointer
2217 This->ancestorStorage = This;
2220 * Initialize the physical support of the storage.
2222 This->hFile = hFile;
2225 * Store copy of file path.
2228 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2229 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2230 if (!This->pwcsName)
2231 return STG_E_INSUFFICIENTMEMORY;
2232 strcpyW(This->pwcsName, pwcsName);
2236 * Initialize the big block cache.
2238 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2239 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2240 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2246 if (This->bigBlockFile == 0)
2251 ULARGE_INTEGER size;
2252 BYTE* bigBlockBuffer;
2255 * Initialize all header variables:
2256 * - The big block depot consists of one block and it is at block 0
2257 * - The properties start at block 1
2258 * - There is no small block depot
2260 memset( This->bigBlockDepotStart,
2262 sizeof(This->bigBlockDepotStart));
2264 This->bigBlockDepotCount = 1;
2265 This->bigBlockDepotStart[0] = 0;
2266 This->rootStartBlock = 1;
2267 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2268 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2269 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2270 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2271 This->extBigBlockDepotCount = 0;
2273 StorageImpl_SaveFileHeader(This);
2276 * Add one block for the big block depot and one block for the properties
2278 size.u.HighPart = 0;
2279 size.u.LowPart = This->bigBlockSize * 3;
2280 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2283 * Initialize the big block depot
2285 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2286 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2287 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2288 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2289 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2294 * Load the header for the file.
2296 hr = StorageImpl_LoadFileHeader(This);
2300 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2307 * There is no block depot cached yet.
2309 This->indexBlockDepotCached = 0xFFFFFFFF;
2312 * Start searching for free blocks with block 0.
2314 This->prevFreeBlock = 0;
2317 * Create the block chain abstractions.
2319 if(!(This->rootBlockChain =
2320 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2321 return STG_E_READFAULT;
2323 if(!(This->smallBlockDepotChain =
2324 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2326 return STG_E_READFAULT;
2329 * Write the root property
2333 StgProperty rootProp;
2335 * Initialize the property chain
2337 memset(&rootProp, 0, sizeof(rootProp));
2338 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2339 sizeof(rootProp.name)/sizeof(WCHAR) );
2340 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2341 rootProp.propertyType = PROPTYPE_ROOT;
2342 rootProp.previousProperty = PROPERTY_NULL;
2343 rootProp.nextProperty = PROPERTY_NULL;
2344 rootProp.dirProperty = PROPERTY_NULL;
2345 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2346 rootProp.size.u.HighPart = 0;
2347 rootProp.size.u.LowPart = 0;
2349 StorageImpl_WriteProperty(This, 0, &rootProp);
2353 * Find the ID of the root in the property sets.
2355 currentPropertyIndex = 0;
2359 readSuccessful = StorageImpl_ReadProperty(
2361 currentPropertyIndex,
2366 if ( (currentProperty.sizeOfNameString != 0 ) &&
2367 (currentProperty.propertyType == PROPTYPE_ROOT) )
2369 This->rootPropertySetIndex = currentPropertyIndex;
2373 currentPropertyIndex++;
2375 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2377 if (!readSuccessful)
2380 return STG_E_READFAULT;
2384 * Create the block chain abstraction for the small block root chain.
2386 if(!(This->smallBlockRootChain =
2387 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2388 return STG_E_READFAULT;
2393 void StorageImpl_Destroy(
2396 TRACE("(%p)\n", This);
2399 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2401 BlockChainStream_Destroy(This->smallBlockRootChain);
2402 BlockChainStream_Destroy(This->rootBlockChain);
2403 BlockChainStream_Destroy(This->smallBlockDepotChain);
2405 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2409 /******************************************************************************
2410 * Storage32Impl_GetNextFreeBigBlock
2412 * Returns the index of the next free big block.
2413 * If the big block depot is filled, this method will enlarge it.
2416 ULONG StorageImpl_GetNextFreeBigBlock(
2419 ULONG depotBlockIndexPos;
2421 ULONG depotBlockOffset;
2422 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2423 ULONG nextBlockIndex = BLOCK_SPECIAL;
2425 ULONG freeBlock = BLOCK_UNUSED;
2427 depotIndex = This->prevFreeBlock / blocksPerDepot;
2428 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2431 * Scan the entire big block depot until we find a block marked free
2433 while (nextBlockIndex != BLOCK_UNUSED)
2435 if (depotIndex < COUNT_BBDEPOTINHEADER)
2437 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2440 * Grow the primary depot.
2442 if (depotBlockIndexPos == BLOCK_UNUSED)
2444 depotBlockIndexPos = depotIndex*blocksPerDepot;
2447 * Add a block depot.
2449 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2450 This->bigBlockDepotCount++;
2451 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2454 * Flag it as a block depot.
2456 StorageImpl_SetNextBlockInChain(This,
2460 /* Save new header information.
2462 StorageImpl_SaveFileHeader(This);
2467 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2469 if (depotBlockIndexPos == BLOCK_UNUSED)
2472 * Grow the extended depot.
2474 ULONG extIndex = BLOCK_UNUSED;
2475 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2476 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2478 if (extBlockOffset == 0)
2480 /* We need an extended block.
2482 extIndex = Storage32Impl_AddExtBlockDepot(This);
2483 This->extBigBlockDepotCount++;
2484 depotBlockIndexPos = extIndex + 1;
2487 depotBlockIndexPos = depotIndex * blocksPerDepot;
2490 * Add a block depot and mark it in the extended block.
2492 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2493 This->bigBlockDepotCount++;
2494 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2496 /* Flag the block depot.
2498 StorageImpl_SetNextBlockInChain(This,
2502 /* If necessary, flag the extended depot block.
2504 if (extIndex != BLOCK_UNUSED)
2505 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2507 /* Save header information.
2509 StorageImpl_SaveFileHeader(This);
2513 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2515 if (depotBuffer != 0)
2517 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2518 ( nextBlockIndex != BLOCK_UNUSED))
2520 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2522 if (nextBlockIndex == BLOCK_UNUSED)
2524 freeBlock = (depotIndex * blocksPerDepot) +
2525 (depotBlockOffset/sizeof(ULONG));
2528 depotBlockOffset += sizeof(ULONG);
2531 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2535 depotBlockOffset = 0;
2538 This->prevFreeBlock = freeBlock;
2543 /******************************************************************************
2544 * Storage32Impl_AddBlockDepot
2546 * This will create a depot block, essentially it is a block initialized
2549 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2553 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2556 * Initialize blocks as free
2558 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2560 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2563 /******************************************************************************
2564 * Storage32Impl_GetExtDepotBlock
2566 * Returns the index of the block that corresponds to the specified depot
2567 * index. This method is only for depot indexes equal or greater than
2568 * COUNT_BBDEPOTINHEADER.
2570 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2572 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2573 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2574 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2575 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2576 ULONG blockIndex = BLOCK_UNUSED;
2577 ULONG extBlockIndex = This->extBigBlockDepotStart;
2579 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2581 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2582 return BLOCK_UNUSED;
2584 while (extBlockCount > 0)
2586 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2590 if (extBlockIndex != BLOCK_UNUSED)
2594 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2596 if (depotBuffer != 0)
2598 StorageUtl_ReadDWord(depotBuffer,
2599 extBlockOffset * sizeof(ULONG),
2602 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2609 /******************************************************************************
2610 * Storage32Impl_SetExtDepotBlock
2612 * Associates the specified block index to the specified depot index.
2613 * This method is only for depot indexes equal or greater than
2614 * COUNT_BBDEPOTINHEADER.
2616 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2620 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2621 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2622 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2623 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2624 ULONG extBlockIndex = This->extBigBlockDepotStart;
2626 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2628 while (extBlockCount > 0)
2630 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2634 if (extBlockIndex != BLOCK_UNUSED)
2638 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2640 if (depotBuffer != 0)
2642 StorageUtl_WriteDWord(depotBuffer,
2643 extBlockOffset * sizeof(ULONG),
2646 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2651 /******************************************************************************
2652 * Storage32Impl_AddExtBlockDepot
2654 * Creates an extended depot block.
2656 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2658 ULONG numExtBlocks = This->extBigBlockDepotCount;
2659 ULONG nextExtBlock = This->extBigBlockDepotStart;
2660 BYTE* depotBuffer = NULL;
2661 ULONG index = BLOCK_UNUSED;
2662 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2663 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2664 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2666 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2667 blocksPerDepotBlock;
2669 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2672 * The first extended block.
2674 This->extBigBlockDepotStart = index;
2680 * Follow the chain to the last one.
2682 for (i = 0; i < (numExtBlocks - 1); i++)
2684 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2688 * Add the new extended block to the chain.
2690 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2691 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2692 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2696 * Initialize this block.
2698 depotBuffer = StorageImpl_GetBigBlock(This, index);
2699 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2700 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2705 /******************************************************************************
2706 * Storage32Impl_FreeBigBlock
2708 * This method will flag the specified block as free in the big block depot.
2710 void StorageImpl_FreeBigBlock(
2714 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2716 if (blockIndex < This->prevFreeBlock)
2717 This->prevFreeBlock = blockIndex;
2720 /************************************************************************
2721 * Storage32Impl_GetNextBlockInChain
2723 * This method will retrieve the block index of the next big block in
2726 * Params: This - Pointer to the Storage object.
2727 * blockIndex - Index of the block to retrieve the chain
2729 * nextBlockIndex - receives the return value.
2731 * Returns: This method returns the index of the next block in the chain.
2732 * It will return the constants:
2733 * BLOCK_SPECIAL - If the block given was not part of a
2735 * BLOCK_END_OF_CHAIN - If the block given was the last in
2737 * BLOCK_UNUSED - If the block given was not past of a chain
2739 * BLOCK_EXTBBDEPOT - This block is part of the extended
2742 * See Windows documentation for more details on IStorage methods.
2744 HRESULT StorageImpl_GetNextBlockInChain(
2747 ULONG* nextBlockIndex)
2749 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2750 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2751 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2753 ULONG depotBlockIndexPos;
2756 *nextBlockIndex = BLOCK_SPECIAL;
2758 if(depotBlockCount >= This->bigBlockDepotCount)
2760 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2761 This->bigBlockDepotCount);
2762 return STG_E_READFAULT;
2766 * Cache the currently accessed depot block.
2768 if (depotBlockCount != This->indexBlockDepotCached)
2770 This->indexBlockDepotCached = depotBlockCount;
2772 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2774 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2779 * We have to look in the extended depot.
2781 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2784 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2787 return STG_E_READFAULT;
2789 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2791 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2792 This->blockDepotCached[index] = *nextBlockIndex;
2794 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2797 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2802 /******************************************************************************
2803 * Storage32Impl_GetNextExtendedBlock
2805 * Given an extended block this method will return the next extended block.
2808 * The last ULONG of an extended block is the block index of the next
2809 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2813 * - The index of the next extended block
2814 * - BLOCK_UNUSED: there is no next extended block.
2815 * - Any other return values denotes failure.
2817 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2819 ULONG nextBlockIndex = BLOCK_SPECIAL;
2820 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2823 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2827 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2829 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2832 return nextBlockIndex;
2835 /******************************************************************************
2836 * Storage32Impl_SetNextBlockInChain
2838 * This method will write the index of the specified block's next block
2839 * in the big block depot.
2841 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2844 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2845 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2846 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2849 void StorageImpl_SetNextBlockInChain(
2854 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2855 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2856 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2857 ULONG depotBlockIndexPos;
2860 assert(depotBlockCount < This->bigBlockDepotCount);
2861 assert(blockIndex != nextBlock);
2863 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2865 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2870 * We have to look in the extended depot.
2872 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2875 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2879 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2880 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2884 * Update the cached block depot, if necessary.
2886 if (depotBlockCount == This->indexBlockDepotCached)
2888 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2892 /******************************************************************************
2893 * Storage32Impl_LoadFileHeader
2895 * This method will read in the file header, i.e. big block index -1.
2897 HRESULT StorageImpl_LoadFileHeader(
2900 HRESULT hr = STG_E_FILENOTFOUND;
2901 void* headerBigBlock = NULL;
2905 * Get a pointer to the big block of data containing the header.
2907 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2910 * Extract the information from the header.
2912 if (headerBigBlock!=0)
2915 * Check for the "magic number" signature and return an error if it is not
2918 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2920 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2921 return STG_E_OLDFORMAT;
2924 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2926 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2927 return STG_E_INVALIDHEADER;
2930 StorageUtl_ReadWord(
2932 OFFSET_BIGBLOCKSIZEBITS,
2933 &This->bigBlockSizeBits);
2935 StorageUtl_ReadWord(
2937 OFFSET_SMALLBLOCKSIZEBITS,
2938 &This->smallBlockSizeBits);
2940 StorageUtl_ReadDWord(
2942 OFFSET_BBDEPOTCOUNT,
2943 &This->bigBlockDepotCount);
2945 StorageUtl_ReadDWord(
2947 OFFSET_ROOTSTARTBLOCK,
2948 &This->rootStartBlock);
2950 StorageUtl_ReadDWord(
2952 OFFSET_SBDEPOTSTART,
2953 &This->smallBlockDepotStart);
2955 StorageUtl_ReadDWord(
2957 OFFSET_EXTBBDEPOTSTART,
2958 &This->extBigBlockDepotStart);
2960 StorageUtl_ReadDWord(
2962 OFFSET_EXTBBDEPOTCOUNT,
2963 &This->extBigBlockDepotCount);
2965 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2967 StorageUtl_ReadDWord(
2969 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2970 &(This->bigBlockDepotStart[index]));
2974 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2978 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2979 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2983 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2984 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2988 * Right now, the code is making some assumptions about the size of the
2989 * blocks, just make sure they are what we're expecting.
2991 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2992 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2994 WARN("Broken OLE storage file\n");
2995 hr = STG_E_INVALIDHEADER;
3001 * Release the block.
3003 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3009 /******************************************************************************
3010 * Storage32Impl_SaveFileHeader
3012 * This method will save to the file the header, i.e. big block -1.
3014 void StorageImpl_SaveFileHeader(
3017 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3022 * Get a pointer to the big block of data containing the header.
3024 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3027 * If the block read failed, the file is probably new.
3032 * Initialize for all unknown fields.
3034 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3037 * Initialize the magic number.
3039 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3042 * And a bunch of things we don't know what they mean
3044 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3045 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3046 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3047 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3051 * Write the information to the header.
3053 StorageUtl_WriteWord(
3055 OFFSET_BIGBLOCKSIZEBITS,
3056 This->bigBlockSizeBits);
3058 StorageUtl_WriteWord(
3060 OFFSET_SMALLBLOCKSIZEBITS,
3061 This->smallBlockSizeBits);
3063 StorageUtl_WriteDWord(
3065 OFFSET_BBDEPOTCOUNT,
3066 This->bigBlockDepotCount);
3068 StorageUtl_WriteDWord(
3070 OFFSET_ROOTSTARTBLOCK,
3071 This->rootStartBlock);
3073 StorageUtl_WriteDWord(
3075 OFFSET_SBDEPOTSTART,
3076 This->smallBlockDepotStart);
3078 StorageUtl_WriteDWord(
3080 OFFSET_SBDEPOTCOUNT,
3081 This->smallBlockDepotChain ?
3082 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3084 StorageUtl_WriteDWord(
3086 OFFSET_EXTBBDEPOTSTART,
3087 This->extBigBlockDepotStart);
3089 StorageUtl_WriteDWord(
3091 OFFSET_EXTBBDEPOTCOUNT,
3092 This->extBigBlockDepotCount);
3094 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3096 StorageUtl_WriteDWord(
3098 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3099 (This->bigBlockDepotStart[index]));
3103 * Write the big block back to the file.
3105 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3108 /******************************************************************************
3109 * Storage32Impl_ReadProperty
3111 * This method will read the specified property from the property chain.
3113 BOOL StorageImpl_ReadProperty(
3116 StgProperty* buffer)
3118 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3119 ULARGE_INTEGER offsetInPropSet;
3120 BOOL readSuccessful;
3123 offsetInPropSet.u.HighPart = 0;
3124 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3126 readSuccessful = BlockChainStream_ReadAt(
3127 This->rootBlockChain,
3135 /* replace the name of root entry (often "Root Entry") by the file name */
3136 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3137 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3139 memset(buffer->name, 0, sizeof(buffer->name));
3143 PROPERTY_NAME_BUFFER_LEN );
3144 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3146 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3148 StorageUtl_ReadWord(
3150 OFFSET_PS_NAMELENGTH,
3151 &buffer->sizeOfNameString);
3153 StorageUtl_ReadDWord(
3155 OFFSET_PS_PREVIOUSPROP,
3156 &buffer->previousProperty);
3158 StorageUtl_ReadDWord(
3161 &buffer->nextProperty);
3163 StorageUtl_ReadDWord(
3166 &buffer->dirProperty);
3168 StorageUtl_ReadGUID(
3171 &buffer->propertyUniqueID);
3173 StorageUtl_ReadDWord(
3176 &buffer->timeStampS1);
3178 StorageUtl_ReadDWord(
3181 &buffer->timeStampD1);
3183 StorageUtl_ReadDWord(
3186 &buffer->timeStampS2);
3188 StorageUtl_ReadDWord(
3191 &buffer->timeStampD2);
3193 StorageUtl_ReadDWord(
3195 OFFSET_PS_STARTBLOCK,
3196 &buffer->startingBlock);
3198 StorageUtl_ReadDWord(
3201 &buffer->size.u.LowPart);
3203 buffer->size.u.HighPart = 0;
3206 return readSuccessful;
3209 /*********************************************************************
3210 * Write the specified property into the property chain
3212 BOOL StorageImpl_WriteProperty(
3215 StgProperty* buffer)
3217 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3218 ULARGE_INTEGER offsetInPropSet;
3219 BOOL writeSuccessful;
3222 offsetInPropSet.u.HighPart = 0;
3223 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3225 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3228 currentProperty + OFFSET_PS_NAME,
3230 PROPERTY_NAME_BUFFER_LEN );
3232 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3234 StorageUtl_WriteWord(
3236 OFFSET_PS_NAMELENGTH,
3237 buffer->sizeOfNameString);
3239 StorageUtl_WriteDWord(
3241 OFFSET_PS_PREVIOUSPROP,
3242 buffer->previousProperty);
3244 StorageUtl_WriteDWord(
3247 buffer->nextProperty);
3249 StorageUtl_WriteDWord(
3252 buffer->dirProperty);
3254 StorageUtl_WriteGUID(
3257 &buffer->propertyUniqueID);
3259 StorageUtl_WriteDWord(
3262 buffer->timeStampS1);
3264 StorageUtl_WriteDWord(
3267 buffer->timeStampD1);
3269 StorageUtl_WriteDWord(
3272 buffer->timeStampS2);
3274 StorageUtl_WriteDWord(
3277 buffer->timeStampD2);
3279 StorageUtl_WriteDWord(
3281 OFFSET_PS_STARTBLOCK,
3282 buffer->startingBlock);
3284 StorageUtl_WriteDWord(
3287 buffer->size.u.LowPart);
3289 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3294 return writeSuccessful;
3297 BOOL StorageImpl_ReadBigBlock(
3302 void* bigBlockBuffer;
3304 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3306 if (bigBlockBuffer!=0)
3308 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3310 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3318 BOOL StorageImpl_WriteBigBlock(
3323 void* bigBlockBuffer;
3325 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3327 if (bigBlockBuffer!=0)
3329 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3331 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3339 void* StorageImpl_GetROBigBlock(
3343 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3346 void* StorageImpl_GetBigBlock(
3350 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3353 void StorageImpl_ReleaseBigBlock(
3357 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3360 /******************************************************************************
3361 * Storage32Impl_SmallBlocksToBigBlocks
3363 * This method will convert a small block chain to a big block chain.
3364 * The small block chain will be destroyed.
3366 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3368 SmallBlockChainStream** ppsbChain)
3370 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3371 ULARGE_INTEGER size, offset;
3372 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3373 ULONG propertyIndex;
3374 BOOL successRead, successWrite;
3375 StgProperty chainProperty;
3377 BlockChainStream *bbTempChain = NULL;
3378 BlockChainStream *bigBlockChain = NULL;
3381 * Create a temporary big block chain that doesn't have
3382 * an associated property. This temporary chain will be
3383 * used to copy data from small blocks to big blocks.
3385 bbTempChain = BlockChainStream_Construct(This,
3388 if(!bbTempChain) return NULL;
3390 * Grow the big block chain.
3392 size = SmallBlockChainStream_GetSize(*ppsbChain);
3393 BlockChainStream_SetSize(bbTempChain, size);
3396 * Copy the contents of the small block chain to the big block chain
3397 * by small block size increments.
3399 offset.u.LowPart = 0;
3400 offset.u.HighPart = 0;
3404 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3407 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3409 DEF_SMALL_BLOCK_SIZE,
3412 cbTotalRead += cbRead;
3414 successWrite = BlockChainStream_WriteAt(bbTempChain,
3419 cbTotalWritten += cbWritten;
3421 offset.u.LowPart += This->smallBlockSize;
3423 } while (successRead && successWrite);
3424 HeapFree(GetProcessHeap(),0,buffer);
3426 assert(cbTotalRead == cbTotalWritten);
3429 * Destroy the small block chain.
3431 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3432 size.u.HighPart = 0;
3434 SmallBlockChainStream_SetSize(*ppsbChain, size);
3435 SmallBlockChainStream_Destroy(*ppsbChain);
3439 * Change the property information. This chain is now a big block chain
3440 * and it doesn't reside in the small blocks chain anymore.
3442 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3444 chainProperty.startingBlock = bbHeadOfChain;
3446 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3449 * Destroy the temporary propertyless big block chain.
3450 * Create a new big block chain associated with this property.
3452 BlockChainStream_Destroy(bbTempChain);
3453 bigBlockChain = BlockChainStream_Construct(This,
3457 return bigBlockChain;
3460 /******************************************************************************
3461 ** Storage32InternalImpl implementation
3464 StorageInternalImpl* StorageInternalImpl_Construct(
3465 StorageImpl* ancestorStorage,
3466 ULONG rootPropertyIndex)
3468 StorageInternalImpl* newStorage;
3471 * Allocate space for the new storage object
3473 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3477 memset(newStorage, 0, sizeof(StorageInternalImpl));
3480 * Initialize the virtual function table.
3482 newStorage->lpVtbl = &Storage32InternalImpl_Vtbl;
3483 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3486 * Keep the ancestor storage pointer and nail a reference to it.
3488 newStorage->ancestorStorage = ancestorStorage;
3489 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3492 * Keep the index of the root property set for this storage,
3494 newStorage->rootPropertySetIndex = rootPropertyIndex;
3502 void StorageInternalImpl_Destroy(
3503 StorageInternalImpl* This)
3505 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3506 HeapFree(GetProcessHeap(), 0, This);
3509 /******************************************************************************
3511 ** Storage32InternalImpl_Commit
3513 ** The non-root storages cannot be opened in transacted mode thus this function
3516 HRESULT WINAPI StorageInternalImpl_Commit(
3518 DWORD grfCommitFlags) /* [in] */
3523 /******************************************************************************
3525 ** Storage32InternalImpl_Revert
3527 ** The non-root storages cannot be opened in transacted mode thus this function
3530 HRESULT WINAPI StorageInternalImpl_Revert(
3536 /******************************************************************************
3537 ** IEnumSTATSTGImpl implementation
3540 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3541 StorageImpl* parentStorage,
3542 ULONG firstPropertyNode)
3544 IEnumSTATSTGImpl* newEnumeration;
3546 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3548 if (newEnumeration!=0)
3551 * Set-up the virtual function table and reference count.
3553 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3554 newEnumeration->ref = 0;
3557 * We want to nail-down the reference to the storage in case the
3558 * enumeration out-lives the storage in the client application.
3560 newEnumeration->parentStorage = parentStorage;
3561 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3563 newEnumeration->firstPropertyNode = firstPropertyNode;
3566 * Initialize the search stack
3568 newEnumeration->stackSize = 0;
3569 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3570 newEnumeration->stackToVisit =
3571 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3574 * Make sure the current node of the iterator is the first one.
3576 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3579 return newEnumeration;
3582 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3584 IStorage_Release((IStorage*)This->parentStorage);
3585 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3586 HeapFree(GetProcessHeap(), 0, This);
3589 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3590 IEnumSTATSTG* iface,
3594 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3597 * Perform a sanity check on the parameters.
3600 return E_INVALIDARG;
3603 * Initialize the return parameter.
3608 * Compare the riid with the interface IDs implemented by this object.
3610 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3612 *ppvObject = (IEnumSTATSTG*)This;
3614 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3616 *ppvObject = (IEnumSTATSTG*)This;
3620 * Check that we obtained an interface.
3622 if ((*ppvObject)==0)
3623 return E_NOINTERFACE;
3626 * Query Interface always increases the reference count by one when it is
3629 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3634 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3635 IEnumSTATSTG* iface)
3637 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3638 return InterlockedIncrement(&This->ref);
3641 ULONG WINAPI IEnumSTATSTGImpl_Release(
3642 IEnumSTATSTG* iface)
3644 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3648 newRef = InterlockedDecrement(&This->ref);
3651 * If the reference count goes down to 0, perform suicide.
3655 IEnumSTATSTGImpl_Destroy(This);
3661 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3662 IEnumSTATSTG* iface,
3665 ULONG* pceltFetched)
3667 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3669 StgProperty currentProperty;
3670 STATSTG* currentReturnStruct = rgelt;
3671 ULONG objectFetched = 0;
3672 ULONG currentSearchNode;
3675 * Perform a sanity check on the parameters.
3677 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3678 return E_INVALIDARG;
3681 * To avoid the special case, get another pointer to a ULONG value if
3682 * the caller didn't supply one.
3684 if (pceltFetched==0)
3685 pceltFetched = &objectFetched;
3688 * Start the iteration, we will iterate until we hit the end of the
3689 * linked list or until we hit the number of items to iterate through
3694 * Start with the node at the top of the stack.
3696 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3698 while ( ( *pceltFetched < celt) &&
3699 ( currentSearchNode!=PROPERTY_NULL) )
3702 * Remove the top node from the stack
3704 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3707 * Read the property from the storage.
3709 StorageImpl_ReadProperty(This->parentStorage,
3714 * Copy the information to the return buffer.
3716 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3721 * Step to the next item in the iteration
3724 currentReturnStruct++;
3727 * Push the next search node in the search stack.
3729 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3732 * continue the iteration.
3734 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3737 if (*pceltFetched == celt)
3744 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3745 IEnumSTATSTG* iface,
3748 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3750 StgProperty currentProperty;
3751 ULONG objectFetched = 0;
3752 ULONG currentSearchNode;
3755 * Start with the node at the top of the stack.
3757 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3759 while ( (objectFetched < celt) &&
3760 (currentSearchNode!=PROPERTY_NULL) )
3763 * Remove the top node from the stack
3765 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3768 * Read the property from the storage.
3770 StorageImpl_ReadProperty(This->parentStorage,
3775 * Step to the next item in the iteration
3780 * Push the next search node in the search stack.
3782 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3785 * continue the iteration.
3787 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3790 if (objectFetched == celt)
3796 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3797 IEnumSTATSTG* iface)
3799 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3801 StgProperty rootProperty;
3802 BOOL readSuccessful;
3805 * Re-initialize the search stack to an empty stack
3807 This->stackSize = 0;
3810 * Read the root property from the storage.
3812 readSuccessful = StorageImpl_ReadProperty(
3813 This->parentStorage,
3814 This->firstPropertyNode,
3819 assert(rootProperty.sizeOfNameString!=0);
3822 * Push the search node in the search stack.
3824 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3830 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3831 IEnumSTATSTG* iface,
3832 IEnumSTATSTG** ppenum)
3834 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3836 IEnumSTATSTGImpl* newClone;
3839 * Perform a sanity check on the parameters.
3842 return E_INVALIDARG;
3844 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3845 This->firstPropertyNode);
3849 * The new clone enumeration must point to the same current node as
3852 newClone->stackSize = This->stackSize ;
3853 newClone->stackMaxSize = This->stackMaxSize ;
3854 newClone->stackToVisit =
3855 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3858 newClone->stackToVisit,
3860 sizeof(ULONG) * newClone->stackSize);
3862 *ppenum = (IEnumSTATSTG*)newClone;
3865 * Don't forget to nail down a reference to the clone before
3868 IEnumSTATSTGImpl_AddRef(*ppenum);
3873 INT IEnumSTATSTGImpl_FindParentProperty(
3874 IEnumSTATSTGImpl *This,
3875 ULONG childProperty,
3876 StgProperty *currentProperty,
3879 ULONG currentSearchNode;
3883 * To avoid the special case, get another pointer to a ULONG value if
3884 * the caller didn't supply one.
3887 thisNodeId = &foundNode;
3890 * Start with the node at the top of the stack.
3892 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3895 while (currentSearchNode!=PROPERTY_NULL)
3898 * Store the current node in the returned parameters
3900 *thisNodeId = currentSearchNode;
3903 * Remove the top node from the stack
3905 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3908 * Read the property from the storage.
3910 StorageImpl_ReadProperty(
3911 This->parentStorage,
3915 if (currentProperty->previousProperty == childProperty)
3916 return PROPERTY_RELATION_PREVIOUS;
3918 else if (currentProperty->nextProperty == childProperty)
3919 return PROPERTY_RELATION_NEXT;
3921 else if (currentProperty->dirProperty == childProperty)
3922 return PROPERTY_RELATION_DIR;
3925 * Push the next search node in the search stack.
3927 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3930 * continue the iteration.
3932 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3935 return PROPERTY_NULL;
3938 ULONG IEnumSTATSTGImpl_FindProperty(
3939 IEnumSTATSTGImpl* This,
3940 const OLECHAR* lpszPropName,
3941 StgProperty* currentProperty)
3943 ULONG currentSearchNode;
3946 * Start with the node at the top of the stack.
3948 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3950 while (currentSearchNode!=PROPERTY_NULL)
3953 * Remove the top node from the stack
3955 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3958 * Read the property from the storage.
3960 StorageImpl_ReadProperty(This->parentStorage,
3964 if ( propertyNameCmp(
3965 (OLECHAR*)currentProperty->name,
3966 (OLECHAR*)lpszPropName) == 0)
3967 return currentSearchNode;
3970 * Push the next search node in the search stack.
3972 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3975 * continue the iteration.
3977 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3980 return PROPERTY_NULL;
3983 void IEnumSTATSTGImpl_PushSearchNode(
3984 IEnumSTATSTGImpl* This,
3987 StgProperty rootProperty;
3988 BOOL readSuccessful;
3991 * First, make sure we're not trying to push an unexisting node.
3993 if (nodeToPush==PROPERTY_NULL)
3997 * First push the node to the stack
3999 if (This->stackSize == This->stackMaxSize)
4001 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4003 This->stackToVisit = HeapReAlloc(
4007 sizeof(ULONG) * This->stackMaxSize);
4010 This->stackToVisit[This->stackSize] = nodeToPush;
4014 * Read the root property from the storage.
4016 readSuccessful = StorageImpl_ReadProperty(
4017 This->parentStorage,
4023 assert(rootProperty.sizeOfNameString!=0);
4026 * Push the previous search node in the search stack.
4028 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4032 ULONG IEnumSTATSTGImpl_PopSearchNode(
4033 IEnumSTATSTGImpl* This,
4038 if (This->stackSize == 0)
4039 return PROPERTY_NULL;
4041 topNode = This->stackToVisit[This->stackSize-1];
4049 /******************************************************************************
4050 ** StorageUtl implementation
4053 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4055 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4058 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4060 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4063 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4065 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4068 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4070 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4073 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4075 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4076 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4077 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4079 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4082 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4084 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4085 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4086 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4088 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4091 void StorageUtl_CopyPropertyToSTATSTG(
4092 STATSTG* destination,
4093 StgProperty* source,
4097 * The copy of the string occurs only when the flag is not set
4099 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4100 (source->name == NULL) ||
4101 (source->name[0] == 0) )
4103 destination->pwcsName = 0;
4107 destination->pwcsName =
4108 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4110 strcpyW((LPWSTR)destination->pwcsName, source->name);
4113 switch (source->propertyType)
4115 case PROPTYPE_STORAGE:
4117 destination->type = STGTY_STORAGE;
4119 case PROPTYPE_STREAM:
4120 destination->type = STGTY_STREAM;
4123 destination->type = STGTY_STREAM;
4127 destination->cbSize = source->size;
4129 currentReturnStruct->mtime = {0}; TODO
4130 currentReturnStruct->ctime = {0};
4131 currentReturnStruct->atime = {0};
4133 destination->grfMode = 0;
4134 destination->grfLocksSupported = 0;
4135 destination->clsid = source->propertyUniqueID;
4136 destination->grfStateBits = 0;
4137 destination->reserved = 0;
4140 /******************************************************************************
4141 ** BlockChainStream implementation
4144 BlockChainStream* BlockChainStream_Construct(
4145 StorageImpl* parentStorage,
4146 ULONG* headOfStreamPlaceHolder,
4147 ULONG propertyIndex)
4149 BlockChainStream* newStream;
4152 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4154 newStream->parentStorage = parentStorage;
4155 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4156 newStream->ownerPropertyIndex = propertyIndex;
4157 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4158 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4159 newStream->numBlocks = 0;
4161 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4163 while (blockIndex != BLOCK_END_OF_CHAIN)
4165 newStream->numBlocks++;
4166 newStream->tailIndex = blockIndex;
4168 if(FAILED(StorageImpl_GetNextBlockInChain(
4173 HeapFree(GetProcessHeap(), 0, newStream);
4181 void BlockChainStream_Destroy(BlockChainStream* This)
4183 HeapFree(GetProcessHeap(), 0, This);
4186 /******************************************************************************
4187 * BlockChainStream_GetHeadOfChain
4189 * Returns the head of this stream chain.
4190 * Some special chains don't have properties, their heads are kept in
4191 * This->headOfStreamPlaceHolder.
4194 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4196 StgProperty chainProperty;
4197 BOOL readSuccessful;
4199 if (This->headOfStreamPlaceHolder != 0)
4200 return *(This->headOfStreamPlaceHolder);
4202 if (This->ownerPropertyIndex != PROPERTY_NULL)
4204 readSuccessful = StorageImpl_ReadProperty(
4205 This->parentStorage,
4206 This->ownerPropertyIndex,
4211 return chainProperty.startingBlock;
4215 return BLOCK_END_OF_CHAIN;
4218 /******************************************************************************
4219 * BlockChainStream_GetCount
4221 * Returns the number of blocks that comprises this chain.
4222 * This is not the size of the stream as the last block may not be full!
4225 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4230 blockIndex = BlockChainStream_GetHeadOfChain(This);
4232 while (blockIndex != BLOCK_END_OF_CHAIN)
4236 if(FAILED(StorageImpl_GetNextBlockInChain(
4237 This->parentStorage,
4246 /******************************************************************************
4247 * BlockChainStream_ReadAt
4249 * Reads a specified number of bytes from this chain at the specified offset.
4250 * bytesRead may be NULL.
4251 * Failure will be returned if the specified number of bytes has not been read.
4253 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4254 ULARGE_INTEGER offset,
4259 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4260 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4261 ULONG bytesToReadInBuffer;
4264 BYTE* bigBlockBuffer;
4267 * Find the first block in the stream that contains part of the buffer.
4269 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4270 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4271 (blockNoInSequence < This->lastBlockNoInSequence) )
4273 blockIndex = BlockChainStream_GetHeadOfChain(This);
4274 This->lastBlockNoInSequence = blockNoInSequence;
4278 ULONG temp = blockNoInSequence;
4280 blockIndex = This->lastBlockNoInSequenceIndex;
4281 blockNoInSequence -= This->lastBlockNoInSequence;
4282 This->lastBlockNoInSequence = temp;
4285 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4287 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4289 blockNoInSequence--;
4292 This->lastBlockNoInSequenceIndex = blockIndex;
4295 * Start reading the buffer.
4298 bufferWalker = buffer;
4300 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4303 * Calculate how many bytes we can copy from this big block.
4305 bytesToReadInBuffer =
4306 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4309 * Copy those bytes to the buffer
4312 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4314 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4316 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4319 * Step to the next big block.
4321 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4324 bufferWalker += bytesToReadInBuffer;
4325 size -= bytesToReadInBuffer;
4326 *bytesRead += bytesToReadInBuffer;
4327 offsetInBlock = 0; /* There is no offset on the next block */
4334 /******************************************************************************
4335 * BlockChainStream_WriteAt
4337 * Writes the specified number of bytes to this chain at the specified offset.
4338 * bytesWritten may be NULL.
4339 * Will fail if not all specified number of bytes have been written.
4341 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4342 ULARGE_INTEGER offset,
4345 ULONG* bytesWritten)
4347 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4348 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4352 BYTE* bigBlockBuffer;
4355 * Find the first block in the stream that contains part of the buffer.
4357 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4358 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4359 (blockNoInSequence < This->lastBlockNoInSequence) )
4361 blockIndex = BlockChainStream_GetHeadOfChain(This);
4362 This->lastBlockNoInSequence = blockNoInSequence;
4366 ULONG temp = blockNoInSequence;
4368 blockIndex = This->lastBlockNoInSequenceIndex;
4369 blockNoInSequence -= This->lastBlockNoInSequence;
4370 This->lastBlockNoInSequence = temp;
4373 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4375 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4378 blockNoInSequence--;
4381 This->lastBlockNoInSequenceIndex = blockIndex;
4384 * Here, I'm casting away the constness on the buffer variable
4385 * This is OK since we don't intend to modify that buffer.
4388 bufferWalker = (BYTE*)buffer;
4390 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4393 * Calculate how many bytes we can copy from this big block.
4396 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4399 * Copy those bytes to the buffer
4401 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4403 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4405 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4408 * Step to the next big block.
4410 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4413 bufferWalker += bytesToWrite;
4414 size -= bytesToWrite;
4415 *bytesWritten += bytesToWrite;
4416 offsetInBlock = 0; /* There is no offset on the next block */
4422 /******************************************************************************
4423 * BlockChainStream_Shrink
4425 * Shrinks this chain in the big block depot.
4427 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4428 ULARGE_INTEGER newSize)
4430 ULONG blockIndex, extraBlock;
4435 * Reset the last accessed block cache.
4437 This->lastBlockNoInSequence = 0xFFFFFFFF;
4438 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4441 * Figure out how many blocks are needed to contain the new size
4443 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4445 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4448 blockIndex = BlockChainStream_GetHeadOfChain(This);
4451 * Go to the new end of chain
4453 while (count < numBlocks)
4455 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4461 /* Get the next block before marking the new end */
4462 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4466 /* Mark the new end of chain */
4467 StorageImpl_SetNextBlockInChain(
4468 This->parentStorage,
4470 BLOCK_END_OF_CHAIN);
4472 This->tailIndex = blockIndex;
4473 This->numBlocks = numBlocks;
4476 * Mark the extra blocks as free
4478 while (extraBlock != BLOCK_END_OF_CHAIN)
4480 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4483 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4484 extraBlock = blockIndex;
4490 /******************************************************************************
4491 * BlockChainStream_Enlarge
4493 * Grows this chain in the big block depot.
4495 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4496 ULARGE_INTEGER newSize)
4498 ULONG blockIndex, currentBlock;
4500 ULONG oldNumBlocks = 0;
4502 blockIndex = BlockChainStream_GetHeadOfChain(This);
4505 * Empty chain. Create the head.
4507 if (blockIndex == BLOCK_END_OF_CHAIN)
4509 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4510 StorageImpl_SetNextBlockInChain(This->parentStorage,
4512 BLOCK_END_OF_CHAIN);
4514 if (This->headOfStreamPlaceHolder != 0)
4516 *(This->headOfStreamPlaceHolder) = blockIndex;
4520 StgProperty chainProp;
4521 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4523 StorageImpl_ReadProperty(
4524 This->parentStorage,
4525 This->ownerPropertyIndex,
4528 chainProp.startingBlock = blockIndex;
4530 StorageImpl_WriteProperty(
4531 This->parentStorage,
4532 This->ownerPropertyIndex,
4536 This->tailIndex = blockIndex;
4537 This->numBlocks = 1;
4541 * Figure out how many blocks are needed to contain this stream
4543 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4545 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4549 * Go to the current end of chain
4551 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4553 currentBlock = blockIndex;
4555 while (blockIndex != BLOCK_END_OF_CHAIN)
4558 currentBlock = blockIndex;
4560 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4565 This->tailIndex = currentBlock;
4568 currentBlock = This->tailIndex;
4569 oldNumBlocks = This->numBlocks;
4572 * Add new blocks to the chain
4574 if (oldNumBlocks < newNumBlocks)
4576 while (oldNumBlocks < newNumBlocks)
4578 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4580 StorageImpl_SetNextBlockInChain(
4581 This->parentStorage,
4585 StorageImpl_SetNextBlockInChain(
4586 This->parentStorage,
4588 BLOCK_END_OF_CHAIN);
4590 currentBlock = blockIndex;
4594 This->tailIndex = blockIndex;
4595 This->numBlocks = newNumBlocks;
4601 /******************************************************************************
4602 * BlockChainStream_SetSize
4604 * Sets the size of this stream. The big block depot will be updated.
4605 * The file will grow if we grow the chain.
4607 * TODO: Free the actual blocks in the file when we shrink the chain.
4608 * Currently, the blocks are still in the file. So the file size
4609 * doesn't shrink even if we shrink streams.
4611 BOOL BlockChainStream_SetSize(
4612 BlockChainStream* This,
4613 ULARGE_INTEGER newSize)
4615 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4617 if (newSize.u.LowPart == size.u.LowPart)
4620 if (newSize.u.LowPart < size.u.LowPart)
4622 BlockChainStream_Shrink(This, newSize);
4626 ULARGE_INTEGER fileSize =
4627 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4629 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4632 * Make sure the file stays a multiple of blocksize
4634 if ((diff % This->parentStorage->bigBlockSize) != 0)
4635 diff += (This->parentStorage->bigBlockSize -
4636 (diff % This->parentStorage->bigBlockSize) );
4638 fileSize.u.LowPart += diff;
4639 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4641 BlockChainStream_Enlarge(This, newSize);
4647 /******************************************************************************
4648 * BlockChainStream_GetSize
4650 * Returns the size of this chain.
4651 * Will return the block count if this chain doesn't have a property.
4653 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4655 StgProperty chainProperty;
4657 if(This->headOfStreamPlaceHolder == NULL)
4660 * This chain is a data stream read the property and return
4661 * the appropriate size
4663 StorageImpl_ReadProperty(
4664 This->parentStorage,
4665 This->ownerPropertyIndex,
4668 return chainProperty.size;
4673 * this chain is a chain that does not have a property, figure out the
4674 * size by making the product number of used blocks times the
4677 ULARGE_INTEGER result;
4678 result.u.HighPart = 0;
4681 BlockChainStream_GetCount(This) *
4682 This->parentStorage->bigBlockSize;
4688 /******************************************************************************
4689 ** SmallBlockChainStream implementation
4692 SmallBlockChainStream* SmallBlockChainStream_Construct(
4693 StorageImpl* parentStorage,
4694 ULONG propertyIndex)
4696 SmallBlockChainStream* newStream;
4698 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4700 newStream->parentStorage = parentStorage;
4701 newStream->ownerPropertyIndex = propertyIndex;
4706 void SmallBlockChainStream_Destroy(
4707 SmallBlockChainStream* This)
4709 HeapFree(GetProcessHeap(), 0, This);
4712 /******************************************************************************
4713 * SmallBlockChainStream_GetHeadOfChain
4715 * Returns the head of this chain of small blocks.
4717 ULONG SmallBlockChainStream_GetHeadOfChain(
4718 SmallBlockChainStream* This)
4720 StgProperty chainProperty;
4721 BOOL readSuccessful;
4723 if (This->ownerPropertyIndex)
4725 readSuccessful = StorageImpl_ReadProperty(
4726 This->parentStorage,
4727 This->ownerPropertyIndex,
4732 return chainProperty.startingBlock;
4737 return BLOCK_END_OF_CHAIN;
4740 /******************************************************************************
4741 * SmallBlockChainStream_GetNextBlockInChain
4743 * Returns the index of the next small block in this chain.
4746 * - BLOCK_END_OF_CHAIN: end of this chain
4747 * - BLOCK_UNUSED: small block 'blockIndex' is free
4749 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4750 SmallBlockChainStream* This,
4752 ULONG* nextBlockInChain)
4754 ULARGE_INTEGER offsetOfBlockInDepot;
4759 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4761 offsetOfBlockInDepot.u.HighPart = 0;
4762 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4765 * Read those bytes in the buffer from the small block file.
4767 success = BlockChainStream_ReadAt(
4768 This->parentStorage->smallBlockDepotChain,
4769 offsetOfBlockInDepot,
4776 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4780 return STG_E_READFAULT;
4783 /******************************************************************************
4784 * SmallBlockChainStream_SetNextBlockInChain
4786 * Writes the index of the next block of the specified block in the small
4788 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4789 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4791 void SmallBlockChainStream_SetNextBlockInChain(
4792 SmallBlockChainStream* This,
4796 ULARGE_INTEGER offsetOfBlockInDepot;
4800 offsetOfBlockInDepot.u.HighPart = 0;
4801 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4803 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4806 * Read those bytes in the buffer from the small block file.
4808 BlockChainStream_WriteAt(
4809 This->parentStorage->smallBlockDepotChain,
4810 offsetOfBlockInDepot,
4816 /******************************************************************************
4817 * SmallBlockChainStream_FreeBlock
4819 * Flag small block 'blockIndex' as free in the small block depot.
4821 void SmallBlockChainStream_FreeBlock(
4822 SmallBlockChainStream* This,
4825 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4828 /******************************************************************************
4829 * SmallBlockChainStream_GetNextFreeBlock
4831 * Returns the index of a free small block. The small block depot will be
4832 * enlarged if necessary. The small block chain will also be enlarged if
4835 ULONG SmallBlockChainStream_GetNextFreeBlock(
4836 SmallBlockChainStream* This)
4838 ULARGE_INTEGER offsetOfBlockInDepot;
4841 ULONG blockIndex = 0;
4842 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4843 BOOL success = TRUE;
4844 ULONG smallBlocksPerBigBlock;
4846 offsetOfBlockInDepot.u.HighPart = 0;
4849 * Scan the small block depot for a free block
4851 while (nextBlockIndex != BLOCK_UNUSED)
4853 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4855 success = BlockChainStream_ReadAt(
4856 This->parentStorage->smallBlockDepotChain,
4857 offsetOfBlockInDepot,
4863 * If we run out of space for the small block depot, enlarge it
4867 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4869 if (nextBlockIndex != BLOCK_UNUSED)
4875 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4877 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4878 ULONG nextBlock, newsbdIndex;
4879 BYTE* smallBlockDepot;
4881 nextBlock = sbdIndex;
4882 while (nextBlock != BLOCK_END_OF_CHAIN)
4884 sbdIndex = nextBlock;
4885 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4888 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4889 if (sbdIndex != BLOCK_END_OF_CHAIN)
4890 StorageImpl_SetNextBlockInChain(
4891 This->parentStorage,
4895 StorageImpl_SetNextBlockInChain(
4896 This->parentStorage,
4898 BLOCK_END_OF_CHAIN);
4901 * Initialize all the small blocks to free
4904 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4906 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4907 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4912 * We have just created the small block depot.
4914 StgProperty rootProp;
4918 * Save it in the header
4920 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4921 StorageImpl_SaveFileHeader(This->parentStorage);
4924 * And allocate the first big block that will contain small blocks
4927 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4929 StorageImpl_SetNextBlockInChain(
4930 This->parentStorage,
4932 BLOCK_END_OF_CHAIN);
4934 StorageImpl_ReadProperty(
4935 This->parentStorage,
4936 This->parentStorage->rootPropertySetIndex,
4939 rootProp.startingBlock = sbStartIndex;
4940 rootProp.size.u.HighPart = 0;
4941 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
4943 StorageImpl_WriteProperty(
4944 This->parentStorage,
4945 This->parentStorage->rootPropertySetIndex,
4951 smallBlocksPerBigBlock =
4952 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4955 * Verify if we have to allocate big blocks to contain small blocks
4957 if (blockIndex % smallBlocksPerBigBlock == 0)
4959 StgProperty rootProp;
4960 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4962 StorageImpl_ReadProperty(
4963 This->parentStorage,
4964 This->parentStorage->rootPropertySetIndex,
4967 if (rootProp.size.u.LowPart <
4968 (blocksRequired * This->parentStorage->bigBlockSize))
4970 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
4972 BlockChainStream_SetSize(
4973 This->parentStorage->smallBlockRootChain,
4976 StorageImpl_WriteProperty(
4977 This->parentStorage,
4978 This->parentStorage->rootPropertySetIndex,
4986 /******************************************************************************
4987 * SmallBlockChainStream_ReadAt
4989 * Reads a specified number of bytes from this chain at the specified offset.
4990 * bytesRead may be NULL.
4991 * Failure will be returned if the specified number of bytes has not been read.
4993 BOOL SmallBlockChainStream_ReadAt(
4994 SmallBlockChainStream* This,
4995 ULARGE_INTEGER offset,
5000 ULARGE_INTEGER offsetInBigBlockFile;
5001 ULONG blockNoInSequence =
5002 offset.u.LowPart / This->parentStorage->smallBlockSize;
5004 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5005 ULONG bytesToReadInBuffer;
5007 ULONG bytesReadFromBigBlockFile;
5011 * This should never happen on a small block file.
5013 assert(offset.u.HighPart==0);
5016 * Find the first block in the stream that contains part of the buffer.
5018 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5020 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5022 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5025 blockNoInSequence--;
5029 * Start reading the buffer.
5032 bufferWalker = buffer;
5034 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5037 * Calculate how many bytes we can copy from this small block.
5039 bytesToReadInBuffer =
5040 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5043 * Calculate the offset of the small block in the small block file.
5045 offsetInBigBlockFile.u.HighPart = 0;
5046 offsetInBigBlockFile.u.LowPart =
5047 blockIndex * This->parentStorage->smallBlockSize;
5049 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5052 * Read those bytes in the buffer from the small block file.
5054 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5055 offsetInBigBlockFile,
5056 bytesToReadInBuffer,
5058 &bytesReadFromBigBlockFile);
5060 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5063 * Step to the next big block.
5065 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5067 bufferWalker += bytesToReadInBuffer;
5068 size -= bytesToReadInBuffer;
5069 *bytesRead += bytesToReadInBuffer;
5070 offsetInBlock = 0; /* There is no offset on the next block */
5076 /******************************************************************************
5077 * SmallBlockChainStream_WriteAt
5079 * Writes the specified number of bytes to this chain at the specified offset.
5080 * bytesWritten may be NULL.
5081 * Will fail if not all specified number of bytes have been written.
5083 BOOL SmallBlockChainStream_WriteAt(
5084 SmallBlockChainStream* This,
5085 ULARGE_INTEGER offset,
5088 ULONG* bytesWritten)
5090 ULARGE_INTEGER offsetInBigBlockFile;
5091 ULONG blockNoInSequence =
5092 offset.u.LowPart / This->parentStorage->smallBlockSize;
5094 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5095 ULONG bytesToWriteInBuffer;
5097 ULONG bytesWrittenFromBigBlockFile;
5101 * This should never happen on a small block file.
5103 assert(offset.u.HighPart==0);
5106 * Find the first block in the stream that contains part of the buffer.
5108 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5110 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5112 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5114 blockNoInSequence--;
5118 * Start writing the buffer.
5120 * Here, I'm casting away the constness on the buffer variable
5121 * This is OK since we don't intend to modify that buffer.
5124 bufferWalker = (BYTE*)buffer;
5125 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5128 * Calculate how many bytes we can copy to this small block.
5130 bytesToWriteInBuffer =
5131 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5134 * Calculate the offset of the small block in the small block file.
5136 offsetInBigBlockFile.u.HighPart = 0;
5137 offsetInBigBlockFile.u.LowPart =
5138 blockIndex * This->parentStorage->smallBlockSize;
5140 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5143 * Write those bytes in the buffer to the small block file.
5145 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5146 offsetInBigBlockFile,
5147 bytesToWriteInBuffer,
5149 &bytesWrittenFromBigBlockFile);
5151 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5154 * Step to the next big block.
5156 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5159 bufferWalker += bytesToWriteInBuffer;
5160 size -= bytesToWriteInBuffer;
5161 *bytesWritten += bytesToWriteInBuffer;
5162 offsetInBlock = 0; /* There is no offset on the next block */
5168 /******************************************************************************
5169 * SmallBlockChainStream_Shrink
5171 * Shrinks this chain in the small block depot.
5173 BOOL SmallBlockChainStream_Shrink(
5174 SmallBlockChainStream* This,
5175 ULARGE_INTEGER newSize)
5177 ULONG blockIndex, extraBlock;
5181 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5183 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5186 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5189 * Go to the new end of chain
5191 while (count < numBlocks)
5193 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5200 * If the count is 0, we have a special case, the head of the chain was
5205 StgProperty chainProp;
5207 StorageImpl_ReadProperty(This->parentStorage,
5208 This->ownerPropertyIndex,
5211 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5213 StorageImpl_WriteProperty(This->parentStorage,
5214 This->ownerPropertyIndex,
5218 * We start freeing the chain at the head block.
5220 extraBlock = blockIndex;
5224 /* Get the next block before marking the new end */
5225 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5229 /* Mark the new end of chain */
5230 SmallBlockChainStream_SetNextBlockInChain(
5233 BLOCK_END_OF_CHAIN);
5237 * Mark the extra blocks as free
5239 while (extraBlock != BLOCK_END_OF_CHAIN)
5241 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5244 SmallBlockChainStream_FreeBlock(This, extraBlock);
5245 extraBlock = blockIndex;
5251 /******************************************************************************
5252 * SmallBlockChainStream_Enlarge
5254 * Grows this chain in the small block depot.
5256 BOOL SmallBlockChainStream_Enlarge(
5257 SmallBlockChainStream* This,
5258 ULARGE_INTEGER newSize)
5260 ULONG blockIndex, currentBlock;
5262 ULONG oldNumBlocks = 0;
5264 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5269 if (blockIndex == BLOCK_END_OF_CHAIN)
5272 StgProperty chainProp;
5274 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5277 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5279 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5282 blockIndex = chainProp.startingBlock;
5283 SmallBlockChainStream_SetNextBlockInChain(
5286 BLOCK_END_OF_CHAIN);
5289 currentBlock = blockIndex;
5292 * Figure out how many blocks are needed to contain this stream
5294 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5296 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5300 * Go to the current end of chain
5302 while (blockIndex != BLOCK_END_OF_CHAIN)
5305 currentBlock = blockIndex;
5306 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5311 * Add new blocks to the chain
5313 while (oldNumBlocks < newNumBlocks)
5315 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5316 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5318 SmallBlockChainStream_SetNextBlockInChain(
5321 BLOCK_END_OF_CHAIN);
5323 currentBlock = blockIndex;
5330 /******************************************************************************
5331 * SmallBlockChainStream_GetCount
5333 * Returns the number of blocks that comprises this chain.
5334 * This is not the size of this chain as the last block may not be full!
5336 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5341 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5343 while (blockIndex != BLOCK_END_OF_CHAIN)
5347 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5354 /******************************************************************************
5355 * SmallBlockChainStream_SetSize
5357 * Sets the size of this stream.
5358 * The file will grow if we grow the chain.
5360 * TODO: Free the actual blocks in the file when we shrink the chain.
5361 * Currently, the blocks are still in the file. So the file size
5362 * doesn't shrink even if we shrink streams.
5364 BOOL SmallBlockChainStream_SetSize(
5365 SmallBlockChainStream* This,
5366 ULARGE_INTEGER newSize)
5368 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5370 if (newSize.u.LowPart == size.u.LowPart)
5373 if (newSize.u.LowPart < size.u.LowPart)
5375 SmallBlockChainStream_Shrink(This, newSize);
5379 SmallBlockChainStream_Enlarge(This, newSize);
5385 /******************************************************************************
5386 * SmallBlockChainStream_GetSize
5388 * Returns the size of this chain.
5390 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5392 StgProperty chainProperty;
5394 StorageImpl_ReadProperty(
5395 This->parentStorage,
5396 This->ownerPropertyIndex,
5399 return chainProperty.size;
5402 /******************************************************************************
5403 * StgCreateDocfile [OLE32.@]
5405 HRESULT WINAPI StgCreateDocfile(
5409 IStorage **ppstgOpen)
5411 StorageImpl* newStorage = 0;
5412 HANDLE hFile = INVALID_HANDLE_VALUE;
5417 DWORD fileAttributes;
5418 WCHAR tempFileName[MAX_PATH];
5420 TRACE("(%s, %lx, %ld, %p)\n",
5421 debugstr_w(pwcsName), grfMode,
5422 reserved, ppstgOpen);
5425 * Validate the parameters
5428 return STG_E_INVALIDPOINTER;
5431 * Validate the STGM flags
5433 if ( FAILED( validateSTGM(grfMode) ))
5434 return STG_E_INVALIDFLAG;
5437 * Generate a unique name.
5441 WCHAR tempPath[MAX_PATH];
5442 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5444 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5445 return STG_E_INVALIDFLAG;
5446 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5447 return STG_E_INVALIDFLAG;
5449 memset(tempPath, 0, sizeof(tempPath));
5450 memset(tempFileName, 0, sizeof(tempFileName));
5452 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5455 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5456 pwcsName = tempFileName;
5458 return STG_E_INSUFFICIENTMEMORY;
5460 creationMode = TRUNCATE_EXISTING;
5464 creationMode = GetCreationModeFromSTGM(grfMode);
5468 * Interpret the STGM value grfMode
5470 shareMode = GetShareModeFromSTGM(grfMode);
5471 accessMode = GetAccessModeFromSTGM(grfMode);
5473 if (grfMode & STGM_DELETEONRELEASE)
5474 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5476 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5478 if (grfMode & STGM_TRANSACTED)
5479 FIXME("Transacted mode not implemented.\n");
5482 * Initialize the "out" parameter.
5486 hFile = CreateFileW(pwcsName,
5494 if (hFile == INVALID_HANDLE_VALUE)
5500 * Allocate and initialize the new IStorage32object.
5502 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5504 if (newStorage == 0)
5505 return STG_E_INSUFFICIENTMEMORY;
5507 hr = StorageImpl_Construct(
5518 HeapFree(GetProcessHeap(), 0, newStorage);
5523 * Get an "out" pointer for the caller.
5525 hr = StorageBaseImpl_QueryInterface(
5526 (IStorage*)newStorage,
5527 (REFIID)&IID_IStorage,
5533 /******************************************************************************
5534 * StgCreateStorageEx [OLE32.@]
5536 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5538 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5539 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5540 return STG_E_UNIMPLEMENTEDFUNCTION;
5543 /******************************************************************************
5544 * StgOpenStorage [OLE32.@]
5546 HRESULT WINAPI StgOpenStorage(
5547 const OLECHAR *pwcsName,
5548 IStorage *pstgPriority,
5552 IStorage **ppstgOpen)
5554 StorageImpl* newStorage = 0;
5559 WCHAR fullname[MAX_PATH];
5562 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5563 debugstr_w(pwcsName), pstgPriority, grfMode,
5564 snbExclude, reserved, ppstgOpen);
5567 * Perform a sanity check
5569 if (( pwcsName == 0) || (ppstgOpen == 0) )
5571 hr = STG_E_INVALIDPOINTER;
5576 * Validate the STGM flags
5578 if ( FAILED( validateSTGM(grfMode) ))
5580 hr = STG_E_INVALIDFLAG;
5585 * Interpret the STGM value grfMode
5587 shareMode = GetShareModeFromSTGM(grfMode);
5588 accessMode = GetAccessModeFromSTGM(grfMode);
5591 * Initialize the "out" parameter.
5595 hFile = CreateFileW( pwcsName,
5600 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5603 length = GetFileSize(hFile, NULL);
5605 if (hFile==INVALID_HANDLE_VALUE)
5607 DWORD last_error = GetLastError();
5613 case ERROR_FILE_NOT_FOUND:
5614 hr = STG_E_FILENOTFOUND;
5617 case ERROR_PATH_NOT_FOUND:
5618 hr = STG_E_PATHNOTFOUND;
5621 case ERROR_ACCESS_DENIED:
5622 case ERROR_WRITE_PROTECT:
5623 hr = STG_E_ACCESSDENIED;
5626 case ERROR_SHARING_VIOLATION:
5627 hr = STG_E_SHAREVIOLATION;
5638 * Allocate and initialize the new IStorage32object.
5640 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5642 if (newStorage == 0)
5644 hr = STG_E_INSUFFICIENTMEMORY;
5648 /* if the file's length was zero, initialize the storage */
5649 hr = StorageImpl_Construct(
5660 HeapFree(GetProcessHeap(), 0, newStorage);
5662 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5664 if(hr == STG_E_INVALIDHEADER)
5665 hr = STG_E_FILEALREADYEXISTS;
5669 /* prepare the file name string given in lieu of the root property name */
5670 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5671 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5672 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5675 * Get an "out" pointer for the caller.
5677 hr = StorageBaseImpl_QueryInterface(
5678 (IStorage*)newStorage,
5679 (REFIID)&IID_IStorage,
5683 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5687 /******************************************************************************
5688 * StgCreateDocfileOnILockBytes [OLE32.@]
5690 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5694 IStorage** ppstgOpen)
5696 StorageImpl* newStorage = 0;
5700 * Validate the parameters
5702 if ((ppstgOpen == 0) || (plkbyt == 0))
5703 return STG_E_INVALIDPOINTER;
5706 * Allocate and initialize the new IStorage object.
5708 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5710 if (newStorage == 0)
5711 return STG_E_INSUFFICIENTMEMORY;
5713 hr = StorageImpl_Construct(
5724 HeapFree(GetProcessHeap(), 0, newStorage);
5729 * Get an "out" pointer for the caller.
5731 hr = StorageBaseImpl_QueryInterface(
5732 (IStorage*)newStorage,
5733 (REFIID)&IID_IStorage,
5739 /******************************************************************************
5740 * StgOpenStorageOnILockBytes [OLE32.@]
5742 HRESULT WINAPI StgOpenStorageOnILockBytes(
5744 IStorage *pstgPriority,
5748 IStorage **ppstgOpen)
5750 StorageImpl* newStorage = 0;
5754 * Perform a sanity check
5756 if ((plkbyt == 0) || (ppstgOpen == 0))
5757 return STG_E_INVALIDPOINTER;
5760 * Validate the STGM flags
5762 if ( FAILED( validateSTGM(grfMode) ))
5763 return STG_E_INVALIDFLAG;
5766 * Initialize the "out" parameter.
5771 * Allocate and initialize the new IStorage object.
5773 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5775 if (newStorage == 0)
5776 return STG_E_INSUFFICIENTMEMORY;
5778 hr = StorageImpl_Construct(
5789 HeapFree(GetProcessHeap(), 0, newStorage);
5794 * Get an "out" pointer for the caller.
5796 hr = StorageBaseImpl_QueryInterface(
5797 (IStorage*)newStorage,
5798 (REFIID)&IID_IStorage,
5804 /******************************************************************************
5805 * StgSetTimes [ole32.@]
5806 * StgSetTimes [OLE32.@]
5810 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *a,
5811 FILETIME const *b, FILETIME const *c )
5813 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5817 /******************************************************************************
5818 * StgIsStorageILockBytes [OLE32.@]
5820 * Determines if the ILockBytes contains a storage object.
5822 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5825 ULARGE_INTEGER offset;
5827 offset.u.HighPart = 0;
5828 offset.u.LowPart = 0;
5830 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5832 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5838 /******************************************************************************
5839 * WriteClassStg [OLE32.@]
5841 * This method will store the specified CLSID in the specified storage object
5843 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5849 hRes = IStorage_SetClass(pStg, rclsid);
5854 /***********************************************************************
5855 * ReadClassStg (OLE32.@)
5857 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5859 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5869 * read a STATSTG structure (contains the clsid) from the storage
5871 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5874 *pclsid=pstatstg.clsid;
5879 /***********************************************************************
5880 * OleLoadFromStream (OLE32.@)
5882 * This function loads an object from stream
5884 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5888 LPPERSISTSTREAM xstm;
5890 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5892 res=ReadClassStm(pStm,&clsid);
5893 if (!SUCCEEDED(res))
5895 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5896 if (!SUCCEEDED(res))
5898 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5899 if (!SUCCEEDED(res)) {
5900 IUnknown_Release((IUnknown*)*ppvObj);
5903 res=IPersistStream_Load(xstm,pStm);
5904 IPersistStream_Release(xstm);
5905 /* FIXME: all refcounts ok at this point? I think they should be:
5908 * xstm : 0 (released)
5913 /***********************************************************************
5914 * OleSaveToStream (OLE32.@)
5916 * This function saves an object with the IPersistStream interface on it
5917 * to the specified stream.
5919 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5925 TRACE("(%p,%p)\n",pPStm,pStm);
5927 res=IPersistStream_GetClassID(pPStm,&clsid);
5929 if (SUCCEEDED(res)){
5931 res=WriteClassStm(pStm,&clsid);
5935 res=IPersistStream_Save(pPStm,pStm,TRUE);
5938 TRACE("Finished Save\n");
5942 /****************************************************************************
5943 * This method validate a STGM parameter that can contain the values below
5945 * STGM_DIRECT 0x00000000
5946 * STGM_TRANSACTED 0x00010000
5947 * STGM_SIMPLE 0x08000000
5949 * STGM_READ 0x00000000
5950 * STGM_WRITE 0x00000001
5951 * STGM_READWRITE 0x00000002
5953 * STGM_SHARE_DENY_NONE 0x00000040
5954 * STGM_SHARE_DENY_READ 0x00000030
5955 * STGM_SHARE_DENY_WRITE 0x00000020
5956 * STGM_SHARE_EXCLUSIVE 0x00000010
5958 * STGM_PRIORITY 0x00040000
5959 * STGM_DELETEONRELEASE 0x04000000
5961 * STGM_CREATE 0x00001000
5962 * STGM_CONVERT 0x00020000
5963 * STGM_FAILIFTHERE 0x00000000
5965 * STGM_NOSCRATCH 0x00100000
5966 * STGM_NOSNAPSHOT 0x00200000
5968 static HRESULT validateSTGM(DWORD stgm)
5970 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5971 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5972 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5974 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5975 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5976 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5978 BOOL bSTGM_SHARE_DENY_NONE =
5979 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5981 BOOL bSTGM_SHARE_DENY_READ =
5982 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5984 BOOL bSTGM_SHARE_DENY_WRITE =
5985 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5987 BOOL bSTGM_SHARE_EXCLUSIVE =
5988 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5990 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5991 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5993 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5994 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5997 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5999 if ( ! bSTGM_DIRECT )
6000 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
6004 * STGM_WRITE | STGM_READWRITE | STGM_READ
6007 if( bSTGM_WRITE && bSTGM_READWRITE )
6011 * STGM_SHARE_DENY_NONE | others
6012 * (I assume here that DENY_READ implies DENY_WRITE)
6014 if ( bSTGM_SHARE_DENY_NONE )
6015 if ( bSTGM_SHARE_DENY_READ ||
6016 bSTGM_SHARE_DENY_WRITE ||
6017 bSTGM_SHARE_EXCLUSIVE)
6021 * STGM_CREATE | STGM_CONVERT
6022 * if both are false, STGM_FAILIFTHERE is set to TRUE
6024 if ( bSTGM_CREATE && bSTGM_CONVERT )
6028 * STGM_NOSCRATCH requires STGM_TRANSACTED
6030 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6034 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6035 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6037 if (bSTGM_NOSNAPSHOT)
6039 if ( ! ( bSTGM_TRANSACTED &&
6040 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6047 /****************************************************************************
6048 * GetShareModeFromSTGM
6050 * This method will return a share mode flag from a STGM value.
6051 * The STGM value is assumed valid.
6053 static DWORD GetShareModeFromSTGM(DWORD stgm)
6055 DWORD dwShareMode = 0;
6056 BOOL bSTGM_SHARE_DENY_NONE =
6057 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6059 BOOL bSTGM_SHARE_DENY_READ =
6060 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6062 BOOL bSTGM_SHARE_DENY_WRITE =
6063 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6065 BOOL bSTGM_SHARE_EXCLUSIVE =
6066 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6068 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6071 if (bSTGM_SHARE_DENY_NONE)
6072 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6074 if (bSTGM_SHARE_DENY_WRITE)
6075 dwShareMode = FILE_SHARE_READ;
6080 /****************************************************************************
6081 * GetAccessModeFromSTGM
6083 * This method will return an access mode flag from a STGM value.
6084 * The STGM value is assumed valid.
6086 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6088 DWORD dwDesiredAccess = GENERIC_READ;
6089 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6090 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6091 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6094 dwDesiredAccess = GENERIC_READ;
6097 dwDesiredAccess |= GENERIC_WRITE;
6099 if (bSTGM_READWRITE)
6100 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6102 return dwDesiredAccess;
6105 /****************************************************************************
6106 * GetCreationModeFromSTGM
6108 * This method will return a creation mode flag from a STGM value.
6109 * The STGM value is assumed valid.
6111 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6113 if ( stgm & STGM_CREATE)
6114 return CREATE_ALWAYS;
6115 if (stgm & STGM_CONVERT) {
6116 FIXME("STGM_CONVERT not implemented!\n");
6119 /* All other cases */
6120 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6121 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6126 /*************************************************************************
6127 * OLECONVERT_LoadOLE10 [Internal]
6129 * Loads the OLE10 STREAM to memory
6132 * pOleStream [I] The OLESTREAM
6133 * pData [I] Data Structure for the OLESTREAM Data
6137 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6138 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6141 * This function is used by OleConvertOLESTREAMToIStorage only.
6143 * Memory allocated for pData must be freed by the caller
6145 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6148 HRESULT hRes = S_OK;
6152 pData->pData = NULL;
6153 pData->pstrOleObjFileName = (CHAR *) NULL;
6155 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6158 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6159 if(dwSize != sizeof(pData->dwOleID))
6161 hRes = CONVERT10_E_OLESTREAM_GET;
6163 else if(pData->dwOleID != OLESTREAM_ID)
6165 hRes = CONVERT10_E_OLESTREAM_FMT;
6176 /* Get the TypeID...more info needed for this field */
6177 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6178 if(dwSize != sizeof(pData->dwTypeID))
6180 hRes = CONVERT10_E_OLESTREAM_GET;
6185 if(pData->dwTypeID != 0)
6187 /* Get the length of the OleTypeName */
6188 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6189 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6191 hRes = CONVERT10_E_OLESTREAM_GET;
6196 if(pData->dwOleTypeNameLength > 0)
6198 /* Get the OleTypeName */
6199 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6200 if(dwSize != pData->dwOleTypeNameLength)
6202 hRes = CONVERT10_E_OLESTREAM_GET;
6208 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6209 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6211 hRes = CONVERT10_E_OLESTREAM_GET;
6215 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6216 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6217 pData->pstrOleObjFileName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6218 if(pData->pstrOleObjFileName)
6220 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6221 if(dwSize != pData->dwOleObjFileNameLength)
6223 hRes = CONVERT10_E_OLESTREAM_GET;
6227 hRes = CONVERT10_E_OLESTREAM_GET;
6232 /* Get the Width of the Metafile */
6233 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6234 if(dwSize != sizeof(pData->dwMetaFileWidth))
6236 hRes = CONVERT10_E_OLESTREAM_GET;
6240 /* Get the Height of the Metafile */
6241 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6242 if(dwSize != sizeof(pData->dwMetaFileHeight))
6244 hRes = CONVERT10_E_OLESTREAM_GET;
6250 /* Get the Length of the Data */
6251 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6252 if(dwSize != sizeof(pData->dwDataLength))
6254 hRes = CONVERT10_E_OLESTREAM_GET;
6258 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6260 if(!bStrem1) /* if it is a second OLE stream data */
6262 pData->dwDataLength -= 8;
6263 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6264 if(dwSize != sizeof(pData->strUnknown))
6266 hRes = CONVERT10_E_OLESTREAM_GET;
6272 if(pData->dwDataLength > 0)
6274 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6276 /* Get Data (ex. IStorage, Metafile, or BMP) */
6279 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6280 if(dwSize != pData->dwDataLength)
6282 hRes = CONVERT10_E_OLESTREAM_GET;
6287 hRes = CONVERT10_E_OLESTREAM_GET;
6296 /*************************************************************************
6297 * OLECONVERT_SaveOLE10 [Internal]
6299 * Saves the OLE10 STREAM From memory
6302 * pData [I] Data Structure for the OLESTREAM Data
6303 * pOleStream [I] The OLESTREAM to save
6307 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6310 * This function is used by OleConvertIStorageToOLESTREAM only.
6313 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6316 HRESULT hRes = S_OK;
6320 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6321 if(dwSize != sizeof(pData->dwOleID))
6323 hRes = CONVERT10_E_OLESTREAM_PUT;
6328 /* Set the TypeID */
6329 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6330 if(dwSize != sizeof(pData->dwTypeID))
6332 hRes = CONVERT10_E_OLESTREAM_PUT;
6336 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6338 /* Set the Length of the OleTypeName */
6339 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6340 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6342 hRes = CONVERT10_E_OLESTREAM_PUT;
6347 if(pData->dwOleTypeNameLength > 0)
6349 /* Set the OleTypeName */
6350 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6351 if(dwSize != pData->dwOleTypeNameLength)
6353 hRes = CONVERT10_E_OLESTREAM_PUT;
6360 /* Set the width of the Metafile */
6361 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6362 if(dwSize != sizeof(pData->dwMetaFileWidth))
6364 hRes = CONVERT10_E_OLESTREAM_PUT;
6370 /* Set the height of the Metafile */
6371 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6372 if(dwSize != sizeof(pData->dwMetaFileHeight))
6374 hRes = CONVERT10_E_OLESTREAM_PUT;
6380 /* Set the length of the Data */
6381 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6382 if(dwSize != sizeof(pData->dwDataLength))
6384 hRes = CONVERT10_E_OLESTREAM_PUT;
6390 if(pData->dwDataLength > 0)
6392 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6393 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6394 if(dwSize != pData->dwDataLength)
6396 hRes = CONVERT10_E_OLESTREAM_PUT;
6404 /*************************************************************************
6405 * OLECONVERT_GetOLE20FromOLE10[Internal]
6407 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6408 * opens it, and copies the content to the dest IStorage for
6409 * OleConvertOLESTREAMToIStorage
6413 * pDestStorage [I] The IStorage to copy the data to
6414 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6415 * nBufferLength [I] The size of the buffer
6424 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6428 IStorage *pTempStorage;
6429 DWORD dwNumOfBytesWritten;
6430 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6431 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6433 /* Create a temp File */
6434 GetTempPathW(MAX_PATH, wstrTempDir);
6435 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6436 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6438 if(hFile != INVALID_HANDLE_VALUE)
6440 /* Write IStorage Data to File */
6441 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6444 /* Open and copy temp storage to the Dest Storage */
6445 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6448 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6449 StorageBaseImpl_Release(pTempStorage);
6451 DeleteFileW(wstrTempFile);
6456 /*************************************************************************
6457 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6459 * Saves the OLE10 STREAM From memory
6462 * pStorage [I] The Src IStorage to copy
6463 * pData [I] The Dest Memory to write to.
6466 * The size in bytes allocated for pData
6469 * Memory allocated for pData must be freed by the caller
6471 * Used by OleConvertIStorageToOLESTREAM only.
6474 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6478 DWORD nDataLength = 0;
6479 IStorage *pTempStorage;
6480 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6481 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6485 /* Create temp Storage */
6486 GetTempPathW(MAX_PATH, wstrTempDir);
6487 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6488 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6492 /* Copy Src Storage to the Temp Storage */
6493 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6494 StorageBaseImpl_Release(pTempStorage);
6496 /* Open Temp Storage as a file and copy to memory */
6497 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6498 if(hFile != INVALID_HANDLE_VALUE)
6500 nDataLength = GetFileSize(hFile, NULL);
6501 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6502 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6505 DeleteFileW(wstrTempFile);
6510 /*************************************************************************
6511 * OLECONVERT_CreateOleStream [Internal]
6513 * Creates the "\001OLE" stream in the IStorage if necessary.
6516 * pStorage [I] Dest storage to create the stream in
6522 * This function is used by OleConvertOLESTREAMToIStorage only.
6524 * This stream is still unknown, MS Word seems to have extra data
6525 * but since the data is stored in the OLESTREAM there should be
6526 * no need to recreate the stream. If the stream is manually
6527 * deleted it will create it with this default data.
6530 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6534 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6535 BYTE pOleStreamHeader [] =
6537 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6538 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6539 0x00, 0x00, 0x00, 0x00
6542 /* Create stream if not present */
6543 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6544 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6548 /* Write default Data */
6549 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6550 IStream_Release(pStream);
6554 /* write a string to a stream, preceded by its length */
6555 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6562 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6563 r = IStream_Write( stm, &len, sizeof(len), NULL);
6568 str = CoTaskMemAlloc( len );
6569 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6570 r = IStream_Write( stm, str, len, NULL);
6571 CoTaskMemFree( str );
6575 /* read a string preceded by its length from a stream */
6576 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6579 DWORD len, count = 0;
6583 r = IStream_Read( stm, &len, sizeof(len), &count );
6586 if( count != sizeof(len) )
6587 return E_OUTOFMEMORY;
6589 TRACE("%ld bytes\n",len);
6591 str = CoTaskMemAlloc( len );
6593 return E_OUTOFMEMORY;
6595 r = IStream_Read( stm, str, len, &count );
6600 CoTaskMemFree( str );
6601 return E_OUTOFMEMORY;
6604 TRACE("Read string %s\n",debugstr_an(str,len));
6606 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6607 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6609 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6610 CoTaskMemFree( str );
6618 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6619 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6623 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6625 static const BYTE unknown1[12] =
6626 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6627 0xFF, 0xFF, 0xFF, 0xFF};
6628 static const BYTE unknown2[16] =
6629 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6630 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6632 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6633 debugstr_w(lpszUserType), debugstr_w(szClipName),
6634 debugstr_w(szProgIDName));
6636 /* Create a CompObj stream if it doesn't exist */
6637 r = IStorage_CreateStream(pstg, szwStreamName,
6638 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6642 /* Write CompObj Structure to stream */
6643 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6645 if( SUCCEEDED( r ) )
6646 r = WriteClassStm( pstm, clsid );
6648 if( SUCCEEDED( r ) )
6649 r = STREAM_WriteString( pstm, lpszUserType );
6650 if( SUCCEEDED( r ) )
6651 r = STREAM_WriteString( pstm, szClipName );
6652 if( SUCCEEDED( r ) )
6653 r = STREAM_WriteString( pstm, szProgIDName );
6654 if( SUCCEEDED( r ) )
6655 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6657 IStream_Release( pstm );
6662 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6663 static HRESULT CLSIDFromUserType(LPCWSTR lpszUserType, CLSID *clsid)
6668 HKEY hkey, hkeyclsid;
6669 LPWSTR buffer = NULL;
6671 static const WCHAR szclsid[] = { 'C','L','S','I','D',0 };
6673 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType));
6675 r = RegOpenKeyW( HKEY_CLASSES_ROOT, szclsid, &hkeyclsid );
6677 return E_INVALIDARG;
6679 len = lstrlenW( lpszUserType ) + 1;
6680 buffer = CoTaskMemAlloc( len * sizeof (WCHAR) );
6684 for(i=0; !found; i++ )
6686 r = RegEnumKeyW( hkeyclsid, i, szKey, sizeof(szKey)/sizeof(WCHAR));
6687 if( r != ERROR_SUCCESS )
6690 r = RegOpenKeyW( hkeyclsid, szKey, &hkey );
6691 if( r != ERROR_SUCCESS )
6693 count = len * sizeof (WCHAR);
6694 r = RegQueryValueW( hkey, NULL, buffer, &count );
6695 found = ( r == ERROR_SUCCESS ) &&
6696 ( count == len*sizeof(WCHAR) ) &&
6697 !lstrcmpW( buffer, lpszUserType ) ;
6698 RegCloseKey( hkey );
6703 CoTaskMemFree( buffer );
6704 RegCloseKey( hkeyclsid );
6707 return E_INVALIDARG;
6709 TRACE("clsid is %s\n", debugstr_w( szKey ) );
6711 r = CLSIDFromString( szKey, clsid );
6717 /***********************************************************************
6718 * WriteFmtUserTypeStg (OLE32.@)
6720 HRESULT WINAPI WriteFmtUserTypeStg(
6721 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6724 WCHAR szwClipName[0x40];
6725 WCHAR szCLSIDName[OLESTREAM_MAX_STR_LEN];
6729 LPMALLOC allocator = NULL;
6731 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6733 r = CoGetMalloc(0, &allocator);
6735 return E_OUTOFMEMORY;
6737 /* get the clipboard format name */
6738 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6741 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6745 r = CLSIDFromUserType(lpszUserType, &clsid);
6749 TRACE("CLSID is %s\n",debugstr_guid(&clsid));
6751 /* get the real program ID */
6752 r = ProgIDFromCLSID( &clsid, &wstrProgID);
6756 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6758 /* if we have a good string, write the stream */
6760 r = STORAGE_WriteCompObj( pstg, &clsid,
6761 lpszUserType, szwClipName, wstrProgID );
6765 IMalloc_Free( allocator, wstrProgID);
6771 /******************************************************************************
6772 * ReadFmtUserTypeStg [OLE32.@]
6774 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6778 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6779 unsigned char unknown1[12];
6780 unsigned char unknown2[16];
6782 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6785 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6787 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6788 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6791 ERR("Failed to open stream\n");
6795 /* read the various parts of the structure */
6796 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6797 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6799 r = ReadClassStm( stm, &clsid );
6803 r = STREAM_ReadString( stm, &szCLSIDName );
6807 r = STREAM_ReadString( stm, &szOleTypeName );
6811 r = STREAM_ReadString( stm, &szProgIDName );
6815 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6816 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6819 /* ok, success... now we just need to store what we found */
6821 *pcf = RegisterClipboardFormatW( szOleTypeName );
6822 CoTaskMemFree( szOleTypeName );
6824 if( lplpszUserType )
6825 *lplpszUserType = szCLSIDName;
6826 CoTaskMemFree( szProgIDName );
6829 IStream_Release( stm );
6835 /*************************************************************************
6836 * OLECONVERT_CreateCompObjStream [Internal]
6838 * Creates a "\001CompObj" is the destination IStorage if necessary.
6841 * pStorage [I] The dest IStorage to create the CompObj Stream
6843 * strOleTypeName [I] The ProgID
6847 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6850 * This function is used by OleConvertOLESTREAMToIStorage only.
6852 * The stream data is stored in the OLESTREAM and there should be
6853 * no need to recreate the stream. If the stream is manually
6854 * deleted it will attempt to create it by querying the registry.
6858 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6861 HRESULT hStorageRes, hRes = S_OK;
6862 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6863 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6864 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
6866 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6867 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6869 /* Initialize the CompObj structure */
6870 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6871 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6872 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6875 /* Create a CompObj stream if it doesn't exist */
6876 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6877 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6878 if(hStorageRes == S_OK)
6880 /* copy the OleTypeName to the compobj struct */
6881 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6882 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6884 /* copy the OleTypeName to the compobj struct */
6885 /* Note: in the test made, these were Identical */
6886 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6887 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6890 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
6891 bufferW, OLESTREAM_MAX_STR_LEN );
6892 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
6898 /* Get the CLSID Default Name from the Registry */
6899 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6900 if(hErr == ERROR_SUCCESS)
6902 char strTemp[OLESTREAM_MAX_STR_LEN];
6903 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6904 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6905 if(hErr == ERROR_SUCCESS)
6907 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6913 /* Write CompObj Structure to stream */
6914 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6916 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6918 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6919 if(IStorageCompObj.dwCLSIDNameLength > 0)
6921 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6923 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6924 if(IStorageCompObj.dwOleTypeNameLength > 0)
6926 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6928 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6929 if(IStorageCompObj.dwProgIDNameLength > 0)
6931 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6933 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6934 IStream_Release(pStream);
6940 /*************************************************************************
6941 * OLECONVERT_CreateOlePresStream[Internal]
6943 * Creates the "\002OlePres000" Stream with the Metafile data
6946 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6947 * dwExtentX [I] Width of the Metafile
6948 * dwExtentY [I] Height of the Metafile
6949 * pData [I] Metafile data
6950 * dwDataLength [I] Size of the Metafile data
6954 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6957 * This function is used by OleConvertOLESTREAMToIStorage only.
6960 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6964 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6965 BYTE pOlePresStreamHeader [] =
6967 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6968 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6969 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6970 0x00, 0x00, 0x00, 0x00
6973 BYTE pOlePresStreamHeaderEmpty [] =
6975 0x00, 0x00, 0x00, 0x00,
6976 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6977 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6978 0x00, 0x00, 0x00, 0x00
6981 /* Create the OlePres000 Stream */
6982 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6983 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6988 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6990 memset(&OlePres, 0, sizeof(OlePres));
6991 /* Do we have any metafile data to save */
6992 if(dwDataLength > 0)
6994 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6995 nHeaderSize = sizeof(pOlePresStreamHeader);
6999 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7000 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7002 /* Set width and height of the metafile */
7003 OlePres.dwExtentX = dwExtentX;
7004 OlePres.dwExtentY = -dwExtentY;
7006 /* Set Data and Length */
7007 if(dwDataLength > sizeof(METAFILEPICT16))
7009 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7010 OlePres.pData = &(pData[8]);
7012 /* Save OlePres000 Data to Stream */
7013 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7014 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7015 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7016 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7017 if(OlePres.dwSize > 0)
7019 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7021 IStream_Release(pStream);
7025 /*************************************************************************
7026 * OLECONVERT_CreateOle10NativeStream [Internal]
7028 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7031 * pStorage [I] Dest storage to create the stream in
7032 * pData [I] Ole10 Native Data (ex. bmp)
7033 * dwDataLength [I] Size of the Ole10 Native Data
7039 * This function is used by OleConvertOLESTREAMToIStorage only.
7041 * Might need to verify the data and return appropriate error message
7044 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7048 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7050 /* Create the Ole10Native Stream */
7051 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7052 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7056 /* Write info to stream */
7057 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7058 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7059 IStream_Release(pStream);
7064 /*************************************************************************
7065 * OLECONVERT_GetOLE10ProgID [Internal]
7067 * Finds the ProgID (or OleTypeID) from the IStorage
7070 * pStorage [I] The Src IStorage to get the ProgID
7071 * strProgID [I] the ProgID string to get
7072 * dwSize [I] the size of the string
7076 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7079 * This function is used by OleConvertIStorageToOLESTREAM only.
7083 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7087 LARGE_INTEGER iSeekPos;
7088 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7089 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7091 /* Open the CompObj Stream */
7092 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7093 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7097 /*Get the OleType from the CompObj Stream */
7098 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7099 iSeekPos.u.HighPart = 0;
7101 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7102 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7103 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7104 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7105 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7106 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7107 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7109 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7112 IStream_Read(pStream, strProgID, *dwSize, NULL);
7114 IStream_Release(pStream);
7119 LPOLESTR wstrProgID;
7121 /* Get the OleType from the registry */
7122 REFCLSID clsid = &(stat.clsid);
7123 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7124 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7127 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7134 /*************************************************************************
7135 * OLECONVERT_GetOle10PresData [Internal]
7137 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7140 * pStorage [I] Src IStroage
7141 * pOleStream [I] Dest OleStream Mem Struct
7147 * This function is used by OleConvertIStorageToOLESTREAM only.
7149 * Memory allocated for pData must be freed by the caller
7153 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7158 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7160 /* Initialize Default data for OLESTREAM */
7161 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7162 pOleStreamData[0].dwTypeID = 2;
7163 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7164 pOleStreamData[1].dwTypeID = 0;
7165 pOleStreamData[0].dwMetaFileWidth = 0;
7166 pOleStreamData[0].dwMetaFileHeight = 0;
7167 pOleStreamData[0].pData = NULL;
7168 pOleStreamData[1].pData = NULL;
7170 /* Open Ole10Native Stream */
7171 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7172 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7176 /* Read Size and Data */
7177 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7178 if(pOleStreamData->dwDataLength > 0)
7180 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7181 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7183 IStream_Release(pStream);
7189 /*************************************************************************
7190 * OLECONVERT_GetOle20PresData[Internal]
7192 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7195 * pStorage [I] Src IStroage
7196 * pOleStreamData [I] Dest OleStream Mem Struct
7202 * This function is used by OleConvertIStorageToOLESTREAM only.
7204 * Memory allocated for pData must be freed by the caller
7206 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7210 OLECONVERT_ISTORAGE_OLEPRES olePress;
7211 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7213 /* Initialize Default data for OLESTREAM */
7214 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7215 pOleStreamData[0].dwTypeID = 2;
7216 pOleStreamData[0].dwMetaFileWidth = 0;
7217 pOleStreamData[0].dwMetaFileHeight = 0;
7218 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7219 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7220 pOleStreamData[1].dwTypeID = 0;
7221 pOleStreamData[1].dwOleTypeNameLength = 0;
7222 pOleStreamData[1].strOleTypeName[0] = 0;
7223 pOleStreamData[1].dwMetaFileWidth = 0;
7224 pOleStreamData[1].dwMetaFileHeight = 0;
7225 pOleStreamData[1].pData = NULL;
7226 pOleStreamData[1].dwDataLength = 0;
7229 /* Open OlePress000 stream */
7230 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7231 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7234 LARGE_INTEGER iSeekPos;
7235 METAFILEPICT16 MetaFilePict;
7236 static const char strMetafilePictName[] = "METAFILEPICT";
7238 /* Set the TypeID for a Metafile */
7239 pOleStreamData[1].dwTypeID = 5;
7241 /* Set the OleTypeName to Metafile */
7242 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7243 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7245 iSeekPos.u.HighPart = 0;
7246 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7248 /* Get Presentation Data */
7249 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7250 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7251 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7252 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7254 /*Set width and Height */
7255 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7256 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7257 if(olePress.dwSize > 0)
7260 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7262 /* Set MetaFilePict struct */
7263 MetaFilePict.mm = 8;
7264 MetaFilePict.xExt = olePress.dwExtentX;
7265 MetaFilePict.yExt = olePress.dwExtentY;
7266 MetaFilePict.hMF = 0;
7268 /* Get Metafile Data */
7269 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7270 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7271 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7273 IStream_Release(pStream);
7277 /*************************************************************************
7278 * OleConvertOLESTREAMToIStorage [OLE32.@]
7283 * DVTARGETDEVICE paramenter is not handled
7284 * Still unsure of some mem fields for OLE 10 Stream
7285 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7286 * and "\001OLE" streams
7289 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7290 LPOLESTREAM pOleStream,
7292 const DVTARGETDEVICE* ptd)
7296 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7298 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7302 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7305 if(pstg == NULL || pOleStream == NULL)
7307 hRes = E_INVALIDARG;
7312 /* Load the OLESTREAM to Memory */
7313 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7318 /* Load the OLESTREAM to Memory (part 2)*/
7319 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7325 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7327 /* Do we have the IStorage Data in the OLESTREAM */
7328 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7330 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7331 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7335 /* It must be an original OLE 1.0 source */
7336 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7341 /* It must be an original OLE 1.0 source */
7342 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7345 /* Create CompObj Stream if necessary */
7346 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7349 /*Create the Ole Stream if necessary */
7350 OLECONVERT_CreateOleStream(pstg);
7355 /* Free allocated memory */
7356 for(i=0; i < 2; i++)
7358 if(pOleStreamData[i].pData != NULL)
7360 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7362 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7364 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7365 pOleStreamData[i].pstrOleObjFileName = NULL;
7371 /*************************************************************************
7372 * OleConvertIStorageToOLESTREAM [OLE32.@]
7379 * Still unsure of some mem fields for OLE 10 Stream
7380 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7381 * and "\001OLE" streams.
7384 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7386 LPOLESTREAM pOleStream)
7389 HRESULT hRes = S_OK;
7391 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7392 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7395 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7397 if(pstg == NULL || pOleStream == NULL)
7399 hRes = E_INVALIDARG;
7403 /* Get the ProgID */
7404 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7405 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7409 /* Was it originally Ole10 */
7410 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7413 IStream_Release(pStream);
7414 /* Get Presentation Data for Ole10Native */
7415 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7419 /* Get Presentation Data (OLE20) */
7420 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7423 /* Save OLESTREAM */
7424 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7427 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7432 /* Free allocated memory */
7433 for(i=0; i < 2; i++)
7435 if(pOleStreamData[i].pData != NULL)
7437 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7444 /***********************************************************************
7445 * GetConvertStg (OLE32.@)
7447 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7448 FIXME("unimplemented stub!\n");
7452 /******************************************************************************
7453 * StgIsStorageFile [OLE32.@]
7456 StgIsStorageFile(LPCOLESTR fn)
7462 TRACE("(\'%s\')\n", debugstr_w(fn));
7463 hf = CreateFileW(fn, GENERIC_READ,
7464 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7465 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7467 if (hf == INVALID_HANDLE_VALUE)
7468 return STG_E_FILENOTFOUND;
7470 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7472 WARN(" unable to read file\n");
7479 if (bytes_read != 8) {
7480 WARN(" too short\n");
7484 if (!memcmp(magic,STORAGE_magic,8)) {
7489 WARN(" -> Invalid header.\n");