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
32 #define NONAMELESSUNION
33 #define NONAMELESSSTRUCT
34 #include "winbase.h" /* for lstrlenW() and the likes */
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
40 #include "storage32.h"
41 #include "ole2.h" /* For Write/ReadClassStm */
44 #include "wine/wingdi16.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(storage);
51 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
52 #define OLESTREAM_ID 0x501
53 #define OLESTREAM_MAX_STR_LEN 255
55 static const char rootPropertyName[] = "Root Entry";
58 /* OLESTREAM memory structure to use for Get and Put Routines */
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
64 DWORD dwOleTypeNameLength;
65 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
66 CHAR *pstrOleObjFileName;
67 DWORD dwOleObjFileNameLength;
68 DWORD dwMetaFileWidth;
69 DWORD dwMetaFileHeight;
70 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
73 }OLECONVERT_OLESTREAM_DATA;
75 /* CompObj Stream structure */
76 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
81 DWORD dwCLSIDNameLength;
82 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
83 DWORD dwOleTypeNameLength;
84 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
85 DWORD dwProgIDNameLength;
86 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
88 }OLECONVERT_ISTORAGE_COMPOBJ;
91 /* Ole Presention Stream structure */
92 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
100 }OLECONVERT_ISTORAGE_OLEPRES;
104 /***********************************************************************
105 * Forward declaration of internal functions used by the method DestroyElement
107 static HRESULT deleteStorageProperty(
108 StorageImpl *parentStorage,
109 ULONG foundPropertyIndexToDelete,
110 StgProperty propertyToDelete);
112 static HRESULT deleteStreamProperty(
113 StorageImpl *parentStorage,
114 ULONG foundPropertyIndexToDelete,
115 StgProperty propertyToDelete);
117 static HRESULT findPlaceholder(
118 StorageImpl *storage,
119 ULONG propertyIndexToStore,
120 ULONG storagePropertyIndex,
123 static HRESULT adjustPropertyChain(
125 StgProperty propertyToDelete,
126 StgProperty parentProperty,
127 ULONG parentPropertyId,
130 /***********************************************************************
131 * Declaration of the functions used to manipulate StgProperty
134 static ULONG getFreeProperty(
135 StorageImpl *storage);
137 static void updatePropertyChain(
138 StorageImpl *storage,
139 ULONG newPropertyIndex,
140 StgProperty newProperty);
142 static LONG propertyNameCmp(
143 OLECHAR *newProperty,
144 OLECHAR *currentProperty);
147 /***********************************************************************
148 * Declaration of miscellaneous functions...
150 static HRESULT validateSTGM(DWORD stgmValue);
152 static DWORD GetShareModeFromSTGM(DWORD stgm);
153 static DWORD GetAccessModeFromSTGM(DWORD stgm);
154 static DWORD GetCreationModeFromSTGM(DWORD stgm);
157 * Virtual function table for the IStorage32Impl class.
159 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
161 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
162 StorageBaseImpl_QueryInterface,
163 StorageBaseImpl_AddRef,
164 StorageBaseImpl_Release,
165 StorageBaseImpl_CreateStream,
166 StorageBaseImpl_OpenStream,
167 StorageImpl_CreateStorage,
168 StorageBaseImpl_OpenStorage,
170 StorageImpl_MoveElementTo,
173 StorageBaseImpl_EnumElements,
174 StorageImpl_DestroyElement,
175 StorageBaseImpl_RenameElement,
176 StorageImpl_SetElementTimes,
177 StorageBaseImpl_SetClass,
178 StorageImpl_SetStateBits,
183 * Virtual function table for the Storage32InternalImpl class.
185 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
187 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
188 StorageBaseImpl_QueryInterface,
189 StorageBaseImpl_AddRef,
190 StorageBaseImpl_Release,
191 StorageBaseImpl_CreateStream,
192 StorageBaseImpl_OpenStream,
193 StorageImpl_CreateStorage,
194 StorageBaseImpl_OpenStorage,
196 StorageImpl_MoveElementTo,
197 StorageInternalImpl_Commit,
198 StorageInternalImpl_Revert,
199 StorageBaseImpl_EnumElements,
200 StorageImpl_DestroyElement,
201 StorageBaseImpl_RenameElement,
202 StorageImpl_SetElementTimes,
203 StorageBaseImpl_SetClass,
204 StorageImpl_SetStateBits,
209 * Virtual function table for the IEnumSTATSTGImpl class.
211 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
213 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
214 IEnumSTATSTGImpl_QueryInterface,
215 IEnumSTATSTGImpl_AddRef,
216 IEnumSTATSTGImpl_Release,
217 IEnumSTATSTGImpl_Next,
218 IEnumSTATSTGImpl_Skip,
219 IEnumSTATSTGImpl_Reset,
220 IEnumSTATSTGImpl_Clone
227 /************************************************************************
228 ** Storage32BaseImpl implementatiion
231 /************************************************************************
232 * Storage32BaseImpl_QueryInterface (IUnknown)
234 * This method implements the common QueryInterface for all IStorage32
235 * implementations contained in this file.
237 * See Windows documentation for more details on IUnknown methods.
239 HRESULT WINAPI StorageBaseImpl_QueryInterface(
244 ICOM_THIS(StorageBaseImpl,iface);
246 * Perform a sanity check on the parameters.
248 if ( (This==0) || (ppvObject==0) )
252 * Initialize the return parameter.
257 * Compare the riid with the interface IDs implemented by this object.
259 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
261 *ppvObject = (IStorage*)This;
263 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
265 *ppvObject = (IStorage*)This;
269 * Check that we obtained an interface.
272 return E_NOINTERFACE;
275 * Query Interface always increases the reference count by one when it is
278 StorageBaseImpl_AddRef(iface);
283 /************************************************************************
284 * Storage32BaseImpl_AddRef (IUnknown)
286 * This method implements the common AddRef for all IStorage32
287 * implementations contained in this file.
289 * See Windows documentation for more details on IUnknown methods.
291 ULONG WINAPI StorageBaseImpl_AddRef(
294 ICOM_THIS(StorageBaseImpl,iface);
300 /************************************************************************
301 * Storage32BaseImpl_Release (IUnknown)
303 * This method implements the common Release for all IStorage32
304 * implementations contained in this file.
306 * See Windows documentation for more details on IUnknown methods.
308 ULONG WINAPI StorageBaseImpl_Release(
311 ICOM_THIS(StorageBaseImpl,iface);
313 * Decrease the reference count on this object.
318 * If the reference count goes down to 0, perform suicide.
323 * Since we are using a system of base-classes, we want to call the
324 * destructor of the appropriate derived class. To do this, we are
325 * using virtual functions to implement the destructor.
327 This->v_destructor(This);
335 /************************************************************************
336 * Storage32BaseImpl_OpenStream (IStorage)
338 * This method will open the specified stream object from the current storage.
340 * See Windows documentation for more details on IStorage methods.
342 HRESULT WINAPI StorageBaseImpl_OpenStream(
344 const OLECHAR* pwcsName, /* [string][in] */
345 void* reserved1, /* [unique][in] */
346 DWORD grfMode, /* [in] */
347 DWORD reserved2, /* [in] */
348 IStream** ppstm) /* [out] */
350 ICOM_THIS(StorageBaseImpl,iface);
351 IEnumSTATSTGImpl* propertyEnumeration;
352 StgStreamImpl* newStream;
353 StgProperty currentProperty;
354 ULONG foundPropertyIndex;
355 HRESULT res = STG_E_UNKNOWN;
357 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
358 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
361 * Perform a sanity check on the parameters.
363 if ( (pwcsName==NULL) || (ppstm==0) )
370 * Initialize the out parameter
375 * Validate the STGM flags
377 if ( FAILED( validateSTGM(grfMode) ))
379 res = STG_E_INVALIDFLAG;
386 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
387 (grfMode & STGM_DELETEONRELEASE) ||
388 (grfMode & STGM_TRANSACTED) )
390 res = STG_E_INVALIDFUNCTION;
395 * Create a property enumeration to search the properties
397 propertyEnumeration = IEnumSTATSTGImpl_Construct(
398 This->ancestorStorage,
399 This->rootPropertySetIndex);
402 * Search the enumeration for the property with the given name
404 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
410 * Delete the property enumeration since we don't need it anymore
412 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
415 * If it was found, construct the stream object and return a pointer to it.
417 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
418 (currentProperty.propertyType==PROPTYPE_STREAM) )
420 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
424 newStream->grfMode = grfMode;
425 *ppstm = (IStream*)newStream;
428 * Since we are returning a pointer to the interface, we have to
429 * nail down the reference.
431 StgStreamImpl_AddRef(*ppstm);
441 res = STG_E_FILENOTFOUND;
445 TRACE("<-- IStream %p\n", *ppstm);
446 TRACE("<-- %08lx\n", res);
450 /************************************************************************
451 * Storage32BaseImpl_OpenStorage (IStorage)
453 * This method will open a new storage object from the current storage.
455 * See Windows documentation for more details on IStorage methods.
457 HRESULT WINAPI StorageBaseImpl_OpenStorage(
459 const OLECHAR* pwcsName, /* [string][unique][in] */
460 IStorage* pstgPriority, /* [unique][in] */
461 DWORD grfMode, /* [in] */
462 SNB snbExclude, /* [unique][in] */
463 DWORD reserved, /* [in] */
464 IStorage** ppstg) /* [out] */
466 ICOM_THIS(StorageBaseImpl,iface);
467 StorageInternalImpl* newStorage;
468 IEnumSTATSTGImpl* propertyEnumeration;
469 StgProperty currentProperty;
470 ULONG foundPropertyIndex;
471 HRESULT res = STG_E_UNKNOWN;
473 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
474 iface, debugstr_w(pwcsName), pstgPriority,
475 grfMode, snbExclude, reserved, ppstg);
478 * Perform a sanity check on the parameters.
480 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
487 if (snbExclude != NULL)
489 res = STG_E_INVALIDPARAMETER;
494 * Validate the STGM flags
496 if ( FAILED( validateSTGM(grfMode) ))
498 res = STG_E_INVALIDFLAG;
505 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
506 (grfMode & STGM_DELETEONRELEASE) ||
507 (grfMode & STGM_PRIORITY) )
509 res = STG_E_INVALIDFUNCTION;
514 * Initialize the out parameter
519 * Create a property enumeration to search the properties
521 propertyEnumeration = IEnumSTATSTGImpl_Construct(
522 This->ancestorStorage,
523 This->rootPropertySetIndex);
526 * Search the enumeration for the property with the given name
528 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
534 * Delete the property enumeration since we don't need it anymore
536 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
539 * If it was found, construct the stream object and return a pointer to it.
541 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
542 (currentProperty.propertyType==PROPTYPE_STORAGE) )
545 * Construct a new Storage object
547 newStorage = StorageInternalImpl_Construct(
548 This->ancestorStorage,
553 *ppstg = (IStorage*)newStorage;
556 * Since we are returning a pointer to the interface,
557 * we have to nail down the reference.
559 StorageBaseImpl_AddRef(*ppstg);
565 res = STG_E_INSUFFICIENTMEMORY;
569 res = STG_E_FILENOTFOUND;
572 TRACE("<-- %08lx\n", res);
576 /************************************************************************
577 * Storage32BaseImpl_EnumElements (IStorage)
579 * This method will create an enumerator object that can be used to
580 * retrieve informatino about all the properties in the storage object.
582 * See Windows documentation for more details on IStorage methods.
584 HRESULT WINAPI StorageBaseImpl_EnumElements(
586 DWORD reserved1, /* [in] */
587 void* reserved2, /* [size_is][unique][in] */
588 DWORD reserved3, /* [in] */
589 IEnumSTATSTG** ppenum) /* [out] */
591 ICOM_THIS(StorageBaseImpl,iface);
592 IEnumSTATSTGImpl* newEnum;
594 TRACE("(%p, %ld, %p, %ld, %p)\n",
595 iface, reserved1, reserved2, reserved3, ppenum);
598 * Perform a sanity check on the parameters.
600 if ( (This==0) || (ppenum==0))
604 * Construct the enumerator.
606 newEnum = IEnumSTATSTGImpl_Construct(
607 This->ancestorStorage,
608 This->rootPropertySetIndex);
612 *ppenum = (IEnumSTATSTG*)newEnum;
615 * Don't forget to nail down a reference to the new object before
618 IEnumSTATSTGImpl_AddRef(*ppenum);
623 return E_OUTOFMEMORY;
626 /************************************************************************
627 * Storage32BaseImpl_Stat (IStorage)
629 * This method will retrieve information about this storage object.
631 * See Windows documentation for more details on IStorage methods.
633 HRESULT WINAPI StorageBaseImpl_Stat(
635 STATSTG* pstatstg, /* [out] */
636 DWORD grfStatFlag) /* [in] */
638 ICOM_THIS(StorageBaseImpl,iface);
639 StgProperty curProperty;
641 HRESULT res = STG_E_UNKNOWN;
643 TRACE("(%p, %p, %lx)\n",
644 iface, pstatstg, grfStatFlag);
647 * Perform a sanity check on the parameters.
649 if ( (This==0) || (pstatstg==0))
656 * Read the information from the property.
658 readSuccessful = StorageImpl_ReadProperty(
659 This->ancestorStorage,
660 This->rootPropertySetIndex,
665 StorageUtl_CopyPropertyToSTATSTG(
679 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.s.LowPart, pstatstg->cbSize.s.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
681 TRACE("<-- %08lx\n", res);
685 /************************************************************************
686 * Storage32BaseImpl_RenameElement (IStorage)
688 * This method will rename the specified element.
690 * See Windows documentation for more details on IStorage methods.
692 * Implementation notes: The method used to rename consists of creating a clone
693 * of the deleted StgProperty object setting it with the new name and to
694 * perform a DestroyElement of the old StgProperty.
696 HRESULT WINAPI StorageBaseImpl_RenameElement(
698 const OLECHAR* pwcsOldName, /* [in] */
699 const OLECHAR* pwcsNewName) /* [in] */
701 ICOM_THIS(StorageBaseImpl,iface);
702 IEnumSTATSTGImpl* propertyEnumeration;
703 StgProperty currentProperty;
704 ULONG foundPropertyIndex;
706 TRACE("(%p, %s, %s)\n",
707 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
710 * Create a property enumeration to search the properties
712 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
713 This->rootPropertySetIndex);
716 * Search the enumeration for the new property name
718 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
722 if (foundPropertyIndex != PROPERTY_NULL)
725 * There is already a property with the new name
727 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
728 return STG_E_FILEALREADYEXISTS;
731 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
734 * Search the enumeration for the old property name
736 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
741 * Delete the property enumeration since we don't need it anymore
743 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
745 if (foundPropertyIndex != PROPERTY_NULL)
747 StgProperty renamedProperty;
748 ULONG renamedPropertyIndex;
751 * Setup a new property for the renamed property
753 renamedProperty.sizeOfNameString =
754 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
756 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
757 return STG_E_INVALIDNAME;
759 strcpyW(renamedProperty.name, pwcsNewName);
761 renamedProperty.propertyType = currentProperty.propertyType;
762 renamedProperty.startingBlock = currentProperty.startingBlock;
763 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
764 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
766 renamedProperty.previousProperty = PROPERTY_NULL;
767 renamedProperty.nextProperty = PROPERTY_NULL;
770 * Bring the dirProperty link in case it is a storage and in which
771 * case the renamed storage elements don't require to be reorganized.
773 renamedProperty.dirProperty = currentProperty.dirProperty;
775 /* call CoFileTime to get the current time
776 renamedProperty.timeStampS1
777 renamedProperty.timeStampD1
778 renamedProperty.timeStampS2
779 renamedProperty.timeStampD2
780 renamedProperty.propertyUniqueID
784 * Obtain a free property in the property chain
786 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
789 * Save the new property into the new property spot
791 StorageImpl_WriteProperty(
792 This->ancestorStorage,
793 renamedPropertyIndex,
797 * Find a spot in the property chain for our newly created property.
801 renamedPropertyIndex,
805 * At this point the renamed property has been inserted in the tree,
806 * now, before to Destroy the old property we must zeroed it's dirProperty
807 * otherwise the DestroyProperty below will zap it all and we do not want
809 * Also, we fake that the old property is a storage so the DestroyProperty
810 * will not do a SetSize(0) on the stream data.
812 * This means that we need to tweek the StgProperty if it is a stream or a
815 StorageImpl_ReadProperty(This->ancestorStorage,
819 currentProperty.dirProperty = PROPERTY_NULL;
820 currentProperty.propertyType = PROPTYPE_STORAGE;
821 StorageImpl_WriteProperty(
822 This->ancestorStorage,
827 * Invoke Destroy to get rid of the ole property and automatically redo
828 * the linking of it's previous and next members...
830 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
836 * There is no property with the old name
838 return STG_E_FILENOTFOUND;
844 /************************************************************************
845 * Storage32BaseImpl_CreateStream (IStorage)
847 * This method will create a stream object within this storage
849 * See Windows documentation for more details on IStorage methods.
851 HRESULT WINAPI StorageBaseImpl_CreateStream(
853 const OLECHAR* pwcsName, /* [string][in] */
854 DWORD grfMode, /* [in] */
855 DWORD reserved1, /* [in] */
856 DWORD reserved2, /* [in] */
857 IStream** ppstm) /* [out] */
859 ICOM_THIS(StorageBaseImpl,iface);
860 IEnumSTATSTGImpl* propertyEnumeration;
861 StgStreamImpl* newStream;
862 StgProperty currentProperty, newStreamProperty;
863 ULONG foundPropertyIndex, newPropertyIndex;
865 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
866 iface, debugstr_w(pwcsName), grfMode,
867 reserved1, reserved2, ppstm);
870 * Validate parameters
873 return STG_E_INVALIDPOINTER;
876 return STG_E_INVALIDNAME;
879 * Validate the STGM flags
881 if ( FAILED( validateSTGM(grfMode) ))
882 return STG_E_INVALIDFLAG;
887 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
888 (grfMode & STGM_DELETEONRELEASE) ||
889 (grfMode & STGM_TRANSACTED) )
890 return STG_E_INVALIDFUNCTION;
893 * Initialize the out parameter
898 * Create a property enumeration to search the properties
900 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
901 This->rootPropertySetIndex);
903 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
907 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
909 if (foundPropertyIndex != PROPERTY_NULL)
912 * An element with this name already exists
914 if (grfMode & STGM_CREATE)
916 IStorage_DestroyElement(iface, pwcsName);
919 return STG_E_FILEALREADYEXISTS;
923 * memset the empty property
925 memset(&newStreamProperty, 0, sizeof(StgProperty));
927 newStreamProperty.sizeOfNameString =
928 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
930 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
931 return STG_E_INVALIDNAME;
933 strcpyW(newStreamProperty.name, pwcsName);
935 newStreamProperty.propertyType = PROPTYPE_STREAM;
936 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
937 newStreamProperty.size.s.LowPart = 0;
938 newStreamProperty.size.s.HighPart = 0;
940 newStreamProperty.previousProperty = PROPERTY_NULL;
941 newStreamProperty.nextProperty = PROPERTY_NULL;
942 newStreamProperty.dirProperty = PROPERTY_NULL;
944 /* call CoFileTime to get the current time
945 newStreamProperty.timeStampS1
946 newStreamProperty.timeStampD1
947 newStreamProperty.timeStampS2
948 newStreamProperty.timeStampD2
951 /* newStreamProperty.propertyUniqueID */
954 * Get a free property or create a new one
956 newPropertyIndex = getFreeProperty(This->ancestorStorage);
959 * Save the new property into the new property spot
961 StorageImpl_WriteProperty(
962 This->ancestorStorage,
967 * Find a spot in the property chain for our newly created property.
975 * Open the stream to return it.
977 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
981 *ppstm = (IStream*)newStream;
984 * Since we are returning a pointer to the interface, we have to nail down
987 StgStreamImpl_AddRef(*ppstm);
991 return STG_E_INSUFFICIENTMEMORY;
997 /************************************************************************
998 * Storage32BaseImpl_SetClass (IStorage)
1000 * This method will write the specified CLSID in the property of this
1003 * See Windows documentation for more details on IStorage methods.
1005 HRESULT WINAPI StorageBaseImpl_SetClass(
1007 REFCLSID clsid) /* [in] */
1009 ICOM_THIS(StorageBaseImpl,iface);
1010 HRESULT hRes = E_FAIL;
1011 StgProperty curProperty;
1014 TRACE("(%p, %p)\n", iface, clsid);
1016 success = StorageImpl_ReadProperty(This->ancestorStorage,
1017 This->rootPropertySetIndex,
1021 curProperty.propertyUniqueID = *clsid;
1023 success = StorageImpl_WriteProperty(This->ancestorStorage,
1024 This->rootPropertySetIndex,
1033 /************************************************************************
1034 ** Storage32Impl implementation
1037 /************************************************************************
1038 * Storage32Impl_CreateStorage (IStorage)
1040 * This method will create the storage object within the provided storage.
1042 * See Windows documentation for more details on IStorage methods.
1044 HRESULT WINAPI StorageImpl_CreateStorage(
1046 const OLECHAR *pwcsName, /* [string][in] */
1047 DWORD grfMode, /* [in] */
1048 DWORD reserved1, /* [in] */
1049 DWORD reserved2, /* [in] */
1050 IStorage **ppstg) /* [out] */
1052 StorageImpl* const This=(StorageImpl*)iface;
1054 IEnumSTATSTGImpl *propertyEnumeration;
1055 StgProperty currentProperty;
1056 StgProperty newProperty;
1057 ULONG foundPropertyIndex;
1058 ULONG newPropertyIndex;
1061 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1062 iface, debugstr_w(pwcsName), grfMode,
1063 reserved1, reserved2, ppstg);
1066 * Validate parameters
1069 return STG_E_INVALIDPOINTER;
1072 return STG_E_INVALIDNAME;
1075 * Validate the STGM flags
1077 if ( FAILED( validateSTGM(grfMode) ) ||
1078 (grfMode & STGM_DELETEONRELEASE) )
1079 return STG_E_INVALIDFLAG;
1082 * Initialize the out parameter
1087 * Create a property enumeration and search the properties
1089 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1090 This->rootPropertySetIndex);
1092 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1095 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1097 if (foundPropertyIndex != PROPERTY_NULL)
1100 * An element with this name already exists
1102 if (grfMode & STGM_CREATE)
1103 IStorage_DestroyElement(iface, pwcsName);
1105 return STG_E_FILEALREADYEXISTS;
1109 * memset the empty property
1111 memset(&newProperty, 0, sizeof(StgProperty));
1113 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1115 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1116 return STG_E_INVALIDNAME;
1118 strcpyW(newProperty.name, pwcsName);
1120 newProperty.propertyType = PROPTYPE_STORAGE;
1121 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1122 newProperty.size.s.LowPart = 0;
1123 newProperty.size.s.HighPart = 0;
1125 newProperty.previousProperty = PROPERTY_NULL;
1126 newProperty.nextProperty = PROPERTY_NULL;
1127 newProperty.dirProperty = PROPERTY_NULL;
1129 /* call CoFileTime to get the current time
1130 newProperty.timeStampS1
1131 newProperty.timeStampD1
1132 newProperty.timeStampS2
1133 newProperty.timeStampD2
1136 /* newStorageProperty.propertyUniqueID */
1139 * Obtain a free property in the property chain
1141 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1144 * Save the new property into the new property spot
1146 StorageImpl_WriteProperty(
1147 This->ancestorStorage,
1152 * Find a spot in the property chain for our newly created property.
1154 updatePropertyChain(
1160 * Open it to get a pointer to return.
1162 hr = IStorage_OpenStorage(
1171 if( (hr != S_OK) || (*ppstg == NULL))
1181 /***************************************************************************
1185 * Get a free property or create a new one.
1187 static ULONG getFreeProperty(
1188 StorageImpl *storage)
1190 ULONG currentPropertyIndex = 0;
1191 ULONG newPropertyIndex = PROPERTY_NULL;
1192 BOOL readSuccessful = TRUE;
1193 StgProperty currentProperty;
1198 * Start by reading the root property
1200 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1201 currentPropertyIndex,
1205 if (currentProperty.sizeOfNameString == 0)
1208 * The property existis and is available, we found it.
1210 newPropertyIndex = currentPropertyIndex;
1216 * We exhausted the property list, we will create more space below
1218 newPropertyIndex = currentPropertyIndex;
1220 currentPropertyIndex++;
1222 } while (newPropertyIndex == PROPERTY_NULL);
1225 * grow the property chain
1227 if (! readSuccessful)
1229 StgProperty emptyProperty;
1230 ULARGE_INTEGER newSize;
1231 ULONG propertyIndex;
1232 ULONG lastProperty = 0;
1233 ULONG blockCount = 0;
1236 * obtain the new count of property blocks
1238 blockCount = BlockChainStream_GetCount(
1239 storage->ancestorStorage->rootBlockChain)+1;
1242 * initialize the size used by the property stream
1244 newSize.s.HighPart = 0;
1245 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1248 * add a property block to the property chain
1250 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1253 * memset the empty property in order to initialize the unused newly
1256 memset(&emptyProperty, 0, sizeof(StgProperty));
1261 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1264 propertyIndex = newPropertyIndex;
1265 propertyIndex < lastProperty;
1268 StorageImpl_WriteProperty(
1269 storage->ancestorStorage,
1275 return newPropertyIndex;
1278 /****************************************************************************
1282 * Case insensitive comparaison of StgProperty.name by first considering
1285 * Returns <0 when newPrpoerty < currentProperty
1286 * >0 when newPrpoerty > currentProperty
1287 * 0 when newPrpoerty == currentProperty
1289 static LONG propertyNameCmp(
1290 OLECHAR *newProperty,
1291 OLECHAR *currentProperty)
1293 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1298 * We compare the string themselves only when they are of the same lenght
1300 diff = lstrcmpiW( newProperty, currentProperty);
1306 /****************************************************************************
1310 * Properly link this new element in the property chain.
1312 static void updatePropertyChain(
1313 StorageImpl *storage,
1314 ULONG newPropertyIndex,
1315 StgProperty newProperty)
1317 StgProperty currentProperty;
1320 * Read the root property
1322 StorageImpl_ReadProperty(storage->ancestorStorage,
1323 storage->rootPropertySetIndex,
1326 if (currentProperty.dirProperty != PROPERTY_NULL)
1329 * The root storage contains some element, therefore, start the research
1330 * for the appropriate location.
1333 ULONG current, next, previous, currentPropertyId;
1336 * Keep the StgProperty sequence number of the storage first property
1338 currentPropertyId = currentProperty.dirProperty;
1343 StorageImpl_ReadProperty(storage->ancestorStorage,
1344 currentProperty.dirProperty,
1347 previous = currentProperty.previousProperty;
1348 next = currentProperty.nextProperty;
1349 current = currentPropertyId;
1353 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1357 if (previous != PROPERTY_NULL)
1359 StorageImpl_ReadProperty(storage->ancestorStorage,
1366 currentProperty.previousProperty = newPropertyIndex;
1367 StorageImpl_WriteProperty(storage->ancestorStorage,
1375 if (next != PROPERTY_NULL)
1377 StorageImpl_ReadProperty(storage->ancestorStorage,
1384 currentProperty.nextProperty = newPropertyIndex;
1385 StorageImpl_WriteProperty(storage->ancestorStorage,
1394 * Trying to insert an item with the same name in the
1395 * subtree structure.
1400 previous = currentProperty.previousProperty;
1401 next = currentProperty.nextProperty;
1407 * The root storage is empty, link the new property to it's dir property
1409 currentProperty.dirProperty = newPropertyIndex;
1410 StorageImpl_WriteProperty(storage->ancestorStorage,
1411 storage->rootPropertySetIndex,
1417 /*************************************************************************
1420 HRESULT WINAPI StorageImpl_CopyTo(
1422 DWORD ciidExclude, /* [in] */
1423 const IID* rgiidExclude, /* [size_is][unique][in] */
1424 SNB snbExclude, /* [unique][in] */
1425 IStorage* pstgDest) /* [unique][in] */
1427 IEnumSTATSTG *elements = 0;
1428 STATSTG curElement, strStat;
1430 IStorage *pstgTmp, *pstgChild;
1431 IStream *pstrTmp, *pstrChild;
1433 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1434 FIXME("Exclude option not implemented\n");
1436 TRACE("(%p, %ld, %p, %p, %p)\n",
1437 iface, ciidExclude, rgiidExclude,
1438 snbExclude, pstgDest);
1441 * Perform a sanity check
1443 if ( pstgDest == 0 )
1444 return STG_E_INVALIDPOINTER;
1447 * Enumerate the elements
1449 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1457 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1458 IStorage_SetClass( pstgDest, &curElement.clsid );
1463 * Obtain the next element
1465 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1467 if ( hr == S_FALSE )
1469 hr = S_OK; /* done, every element has been copied */
1473 if (curElement.type == STGTY_STORAGE)
1476 * open child source storage
1478 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1479 STGM_READ|STGM_SHARE_EXCLUSIVE,
1480 NULL, 0, &pstgChild );
1486 * Check if destination storage is not a child of the source
1487 * storage, which will cause an infinite loop
1489 if (pstgChild == pstgDest)
1491 IEnumSTATSTG_Release(elements);
1493 return STG_E_ACCESSDENIED;
1497 * create a new storage in destination storage
1499 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1500 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1504 * if it already exist, don't create a new one use this one
1506 if (hr == STG_E_FILEALREADYEXISTS)
1508 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1509 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1510 NULL, 0, &pstgTmp );
1518 * do the copy recursively
1520 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1521 snbExclude, pstgTmp );
1523 IStorage_Release( pstgTmp );
1524 IStorage_Release( pstgChild );
1526 else if (curElement.type == STGTY_STREAM)
1529 * create a new stream in destination storage. If the stream already
1530 * exist, it will be deleted and a new one will be created.
1532 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1533 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1540 * open child stream storage
1542 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1543 STGM_READ|STGM_SHARE_EXCLUSIVE,
1550 * Get the size of the source stream
1552 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1555 * Set the size of the destination stream.
1557 IStream_SetSize(pstrTmp, strStat.cbSize);
1562 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1565 IStream_Release( pstrTmp );
1566 IStream_Release( pstrChild );
1570 WARN("unknown element type: %ld\n", curElement.type);
1573 } while (hr == S_OK);
1578 IEnumSTATSTG_Release(elements);
1583 /*************************************************************************
1584 * MoveElementTo (IStorage)
1586 HRESULT WINAPI StorageImpl_MoveElementTo(
1588 const OLECHAR *pwcsName, /* [string][in] */
1589 IStorage *pstgDest, /* [unique][in] */
1590 const OLECHAR *pwcsNewName,/* [string][in] */
1591 DWORD grfFlags) /* [in] */
1593 FIXME("not implemented!\n");
1597 /*************************************************************************
1600 HRESULT WINAPI StorageImpl_Commit(
1602 DWORD grfCommitFlags)/* [in] */
1604 FIXME("(%ld): stub!\n", grfCommitFlags);
1608 /*************************************************************************
1611 HRESULT WINAPI StorageImpl_Revert(
1614 FIXME("not implemented!\n");
1618 /*************************************************************************
1619 * DestroyElement (IStorage)
1621 * Stategy: This implementation is build this way for simplicity not for speed.
1622 * I always delete the top most element of the enumeration and adjust
1623 * the deleted element pointer all the time. This takes longer to
1624 * do but allow to reinvoke DestroyElement whenever we encounter a
1625 * storage object. The optimisation reside in the usage of another
1626 * enumeration stategy that would give all the leaves of a storage
1627 * first. (postfix order)
1629 HRESULT WINAPI StorageImpl_DestroyElement(
1631 const OLECHAR *pwcsName)/* [string][in] */
1633 StorageImpl* const This=(StorageImpl*)iface;
1635 IEnumSTATSTGImpl* propertyEnumeration;
1638 StgProperty propertyToDelete;
1639 StgProperty parentProperty;
1640 ULONG foundPropertyIndexToDelete;
1641 ULONG typeOfRelation;
1642 ULONG parentPropertyId;
1645 iface, debugstr_w(pwcsName));
1648 * Perform a sanity check on the parameters.
1651 return STG_E_INVALIDPOINTER;
1654 * Create a property enumeration to search the property with the given name
1656 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1657 This->ancestorStorage,
1658 This->rootPropertySetIndex);
1660 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1661 propertyEnumeration,
1665 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1667 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1669 return STG_E_FILENOTFOUND;
1673 * Find the parent property of the property to delete (the one that
1674 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1675 * the parent is This. Otherwise, the parent is one of it's sibling...
1679 * First, read This's StgProperty..
1681 res = StorageImpl_ReadProperty(
1682 This->ancestorStorage,
1683 This->rootPropertySetIndex,
1689 * Second, check to see if by any chance the actual storage (This) is not
1690 * the parent of the property to delete... We never know...
1692 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1695 * Set data as it would have been done in the else part...
1697 typeOfRelation = PROPERTY_RELATION_DIR;
1698 parentPropertyId = This->rootPropertySetIndex;
1703 * Create a property enumeration to search the parent properties, and
1704 * delete it once done.
1706 IEnumSTATSTGImpl* propertyEnumeration2;
1708 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1709 This->ancestorStorage,
1710 This->rootPropertySetIndex);
1712 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1713 propertyEnumeration2,
1714 foundPropertyIndexToDelete,
1718 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1721 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1723 hr = deleteStorageProperty(
1725 foundPropertyIndexToDelete,
1728 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1730 hr = deleteStreamProperty(
1732 foundPropertyIndexToDelete,
1740 * Adjust the property chain
1742 hr = adjustPropertyChain(
1753 /************************************************************************
1754 * StorageImpl_Stat (IStorage)
1756 * This method will retrieve information about this storage object.
1758 * See Windows documentation for more details on IStorage methods.
1760 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1761 STATSTG* pstatstg, /* [out] */
1762 DWORD grfStatFlag) /* [in] */
1764 StorageImpl* const This = (StorageImpl*)iface;
1765 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1767 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1769 CoTaskMemFree(pstatstg->pwcsName);
1770 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1771 strcpyW(pstatstg->pwcsName, This->pwcsName);
1779 /*********************************************************************
1783 * Perform the deletion of a complete storage node
1786 static HRESULT deleteStorageProperty(
1787 StorageImpl *parentStorage,
1788 ULONG indexOfPropertyToDelete,
1789 StgProperty propertyToDelete)
1791 IEnumSTATSTG *elements = 0;
1792 IStorage *childStorage = 0;
1793 STATSTG currentElement;
1795 HRESULT destroyHr = S_OK;
1798 * Open the storage and enumerate it
1800 hr = StorageBaseImpl_OpenStorage(
1801 (IStorage*)parentStorage,
1802 propertyToDelete.name,
1804 STGM_SHARE_EXCLUSIVE,
1815 * Enumerate the elements
1817 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1822 * Obtain the next element
1824 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1827 destroyHr = StorageImpl_DestroyElement(
1828 (IStorage*)childStorage,
1829 (OLECHAR*)currentElement.pwcsName);
1831 CoTaskMemFree(currentElement.pwcsName);
1835 * We need to Reset the enumeration every time because we delete elements
1836 * and the enumeration could be invalid
1838 IEnumSTATSTG_Reset(elements);
1840 } while ((hr == S_OK) && (destroyHr == S_OK));
1843 * Invalidate the property by zeroing it's name member.
1845 propertyToDelete.sizeOfNameString = 0;
1847 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1848 indexOfPropertyToDelete,
1851 IStorage_Release(childStorage);
1852 IEnumSTATSTG_Release(elements);
1857 /*********************************************************************
1861 * Perform the deletion of a stream node
1864 static HRESULT deleteStreamProperty(
1865 StorageImpl *parentStorage,
1866 ULONG indexOfPropertyToDelete,
1867 StgProperty propertyToDelete)
1871 ULARGE_INTEGER size;
1873 size.s.HighPart = 0;
1876 hr = StorageBaseImpl_OpenStream(
1877 (IStorage*)parentStorage,
1878 (OLECHAR*)propertyToDelete.name,
1880 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1892 hr = IStream_SetSize(pis, size);
1900 * Release the stream object.
1902 IStream_Release(pis);
1905 * Invalidate the property by zeroing it's name member.
1907 propertyToDelete.sizeOfNameString = 0;
1910 * Here we should re-read the property so we get the updated pointer
1911 * but since we are here to zap it, I don't do it...
1913 StorageImpl_WriteProperty(
1914 parentStorage->ancestorStorage,
1915 indexOfPropertyToDelete,
1921 /*********************************************************************
1925 * Finds a placeholder for the StgProperty within the Storage
1928 static HRESULT findPlaceholder(
1929 StorageImpl *storage,
1930 ULONG propertyIndexToStore,
1931 ULONG storePropertyIndex,
1934 StgProperty storeProperty;
1939 * Read the storage property
1941 res = StorageImpl_ReadProperty(
1942 storage->ancestorStorage,
1951 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1953 if (storeProperty.previousProperty != PROPERTY_NULL)
1955 return findPlaceholder(
1957 propertyIndexToStore,
1958 storeProperty.previousProperty,
1963 storeProperty.previousProperty = propertyIndexToStore;
1966 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1968 if (storeProperty.nextProperty != PROPERTY_NULL)
1970 return findPlaceholder(
1972 propertyIndexToStore,
1973 storeProperty.nextProperty,
1978 storeProperty.nextProperty = propertyIndexToStore;
1981 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1983 if (storeProperty.dirProperty != PROPERTY_NULL)
1985 return findPlaceholder(
1987 propertyIndexToStore,
1988 storeProperty.dirProperty,
1993 storeProperty.dirProperty = propertyIndexToStore;
1997 hr = StorageImpl_WriteProperty(
1998 storage->ancestorStorage,
2010 /*************************************************************************
2014 * This method takes the previous and the next property link of a property
2015 * to be deleted and find them a place in the Storage.
2017 static HRESULT adjustPropertyChain(
2019 StgProperty propertyToDelete,
2020 StgProperty parentProperty,
2021 ULONG parentPropertyId,
2024 ULONG newLinkProperty = PROPERTY_NULL;
2025 BOOL needToFindAPlaceholder = FALSE;
2026 ULONG storeNode = PROPERTY_NULL;
2027 ULONG toStoreNode = PROPERTY_NULL;
2028 INT relationType = 0;
2032 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2034 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2037 * Set the parent previous to the property to delete previous
2039 newLinkProperty = propertyToDelete.previousProperty;
2041 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2044 * We also need to find a storage for the other link, setup variables
2045 * to do this at the end...
2047 needToFindAPlaceholder = TRUE;
2048 storeNode = propertyToDelete.previousProperty;
2049 toStoreNode = propertyToDelete.nextProperty;
2050 relationType = PROPERTY_RELATION_NEXT;
2053 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2056 * Set the parent previous to the property to delete next
2058 newLinkProperty = propertyToDelete.nextProperty;
2062 * Link it for real...
2064 parentProperty.previousProperty = newLinkProperty;
2067 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2069 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2072 * Set the parent next to the property to delete next previous
2074 newLinkProperty = propertyToDelete.previousProperty;
2076 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2079 * We also need to find a storage for the other link, setup variables
2080 * to do this at the end...
2082 needToFindAPlaceholder = TRUE;
2083 storeNode = propertyToDelete.previousProperty;
2084 toStoreNode = propertyToDelete.nextProperty;
2085 relationType = PROPERTY_RELATION_NEXT;
2088 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2091 * Set the parent next to the property to delete next
2093 newLinkProperty = propertyToDelete.nextProperty;
2097 * Link it for real...
2099 parentProperty.nextProperty = newLinkProperty;
2101 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2103 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2106 * Set the parent dir to the property to delete previous
2108 newLinkProperty = propertyToDelete.previousProperty;
2110 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2113 * We also need to find a storage for the other link, setup variables
2114 * to do this at the end...
2116 needToFindAPlaceholder = TRUE;
2117 storeNode = propertyToDelete.previousProperty;
2118 toStoreNode = propertyToDelete.nextProperty;
2119 relationType = PROPERTY_RELATION_NEXT;
2122 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2125 * Set the parent dir to the property to delete next
2127 newLinkProperty = propertyToDelete.nextProperty;
2131 * Link it for real...
2133 parentProperty.dirProperty = newLinkProperty;
2137 * Write back the parent property
2139 res = StorageImpl_WriteProperty(
2140 This->ancestorStorage,
2149 * If a placeholder is required for the other link, then, find one and
2150 * get out of here...
2152 if (needToFindAPlaceholder)
2154 hr = findPlaceholder(
2165 /******************************************************************************
2166 * SetElementTimes (IStorage)
2168 HRESULT WINAPI StorageImpl_SetElementTimes(
2170 const OLECHAR *pwcsName,/* [string][in] */
2171 const FILETIME *pctime, /* [in] */
2172 const FILETIME *patime, /* [in] */
2173 const FILETIME *pmtime) /* [in] */
2175 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2179 /******************************************************************************
2180 * SetStateBits (IStorage)
2182 HRESULT WINAPI StorageImpl_SetStateBits(
2184 DWORD grfStateBits,/* [in] */
2185 DWORD grfMask) /* [in] */
2187 FIXME("not implemented!\n");
2191 HRESULT StorageImpl_Construct(
2201 StgProperty currentProperty;
2202 BOOL readSuccessful;
2203 ULONG currentPropertyIndex;
2205 if ( FAILED( validateSTGM(openFlags) ))
2206 return STG_E_INVALIDFLAG;
2208 memset(This, 0, sizeof(StorageImpl));
2211 * Initialize the virtual function table.
2213 This->lpVtbl = &Storage32Impl_Vtbl;
2214 This->v_destructor = &StorageImpl_Destroy;
2217 * This is the top-level storage so initialize the ancestor pointer
2220 This->ancestorStorage = This;
2223 * Initialize the physical support of the storage.
2225 This->hFile = hFile;
2228 * Store copy of file path.
2231 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2232 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2233 if (!This->pwcsName)
2234 return STG_E_INSUFFICIENTMEMORY;
2235 strcpyW(This->pwcsName, pwcsName);
2239 * Initialize the big block cache.
2241 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2242 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2243 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2249 if (This->bigBlockFile == 0)
2254 ULARGE_INTEGER size;
2255 BYTE* bigBlockBuffer;
2258 * Initialize all header variables:
2259 * - The big block depot consists of one block and it is at block 0
2260 * - The properties start at block 1
2261 * - There is no small block depot
2263 memset( This->bigBlockDepotStart,
2265 sizeof(This->bigBlockDepotStart));
2267 This->bigBlockDepotCount = 1;
2268 This->bigBlockDepotStart[0] = 0;
2269 This->rootStartBlock = 1;
2270 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2271 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2272 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2273 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2274 This->extBigBlockDepotCount = 0;
2276 StorageImpl_SaveFileHeader(This);
2279 * Add one block for the big block depot and one block for the properties
2281 size.s.HighPart = 0;
2282 size.s.LowPart = This->bigBlockSize * 3;
2283 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2286 * Initialize the big block depot
2288 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2289 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2290 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2291 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2292 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2297 * Load the header for the file.
2299 hr = StorageImpl_LoadFileHeader(This);
2303 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2310 * There is no block depot cached yet.
2312 This->indexBlockDepotCached = 0xFFFFFFFF;
2315 * Start searching for free blocks with block 0.
2317 This->prevFreeBlock = 0;
2320 * Create the block chain abstractions.
2322 if(!(This->rootBlockChain =
2323 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2324 return STG_E_READFAULT;
2326 if(!(This->smallBlockDepotChain =
2327 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2329 return STG_E_READFAULT;
2332 * Write the root property
2336 StgProperty rootProp;
2338 * Initialize the property chain
2340 memset(&rootProp, 0, sizeof(rootProp));
2341 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2342 sizeof(rootProp.name)/sizeof(WCHAR) );
2343 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2344 rootProp.propertyType = PROPTYPE_ROOT;
2345 rootProp.previousProperty = PROPERTY_NULL;
2346 rootProp.nextProperty = PROPERTY_NULL;
2347 rootProp.dirProperty = PROPERTY_NULL;
2348 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2349 rootProp.size.s.HighPart = 0;
2350 rootProp.size.s.LowPart = 0;
2352 StorageImpl_WriteProperty(This, 0, &rootProp);
2356 * Find the ID of the root in the property sets.
2358 currentPropertyIndex = 0;
2362 readSuccessful = StorageImpl_ReadProperty(
2364 currentPropertyIndex,
2369 if ( (currentProperty.sizeOfNameString != 0 ) &&
2370 (currentProperty.propertyType == PROPTYPE_ROOT) )
2372 This->rootPropertySetIndex = currentPropertyIndex;
2376 currentPropertyIndex++;
2378 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2380 if (!readSuccessful)
2383 return STG_E_READFAULT;
2387 * Create the block chain abstraction for the small block root chain.
2389 if(!(This->smallBlockRootChain =
2390 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2391 return STG_E_READFAULT;
2396 void StorageImpl_Destroy(
2399 TRACE("(%p)\n", This);
2402 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2404 BlockChainStream_Destroy(This->smallBlockRootChain);
2405 BlockChainStream_Destroy(This->rootBlockChain);
2406 BlockChainStream_Destroy(This->smallBlockDepotChain);
2408 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2412 /******************************************************************************
2413 * Storage32Impl_GetNextFreeBigBlock
2415 * Returns the index of the next free big block.
2416 * If the big block depot is filled, this method will enlarge it.
2419 ULONG StorageImpl_GetNextFreeBigBlock(
2422 ULONG depotBlockIndexPos;
2424 ULONG depotBlockOffset;
2425 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2426 ULONG nextBlockIndex = BLOCK_SPECIAL;
2428 ULONG freeBlock = BLOCK_UNUSED;
2430 depotIndex = This->prevFreeBlock / blocksPerDepot;
2431 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2434 * Scan the entire big block depot until we find a block marked free
2436 while (nextBlockIndex != BLOCK_UNUSED)
2438 if (depotIndex < COUNT_BBDEPOTINHEADER)
2440 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2443 * Grow the primary depot.
2445 if (depotBlockIndexPos == BLOCK_UNUSED)
2447 depotBlockIndexPos = depotIndex*blocksPerDepot;
2450 * Add a block depot.
2452 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2453 This->bigBlockDepotCount++;
2454 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2457 * Flag it as a block depot.
2459 StorageImpl_SetNextBlockInChain(This,
2463 /* Save new header information.
2465 StorageImpl_SaveFileHeader(This);
2470 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2472 if (depotBlockIndexPos == BLOCK_UNUSED)
2475 * Grow the extended depot.
2477 ULONG extIndex = BLOCK_UNUSED;
2478 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2479 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2481 if (extBlockOffset == 0)
2483 /* We need an extended block.
2485 extIndex = Storage32Impl_AddExtBlockDepot(This);
2486 This->extBigBlockDepotCount++;
2487 depotBlockIndexPos = extIndex + 1;
2490 depotBlockIndexPos = depotIndex * blocksPerDepot;
2493 * Add a block depot and mark it in the extended block.
2495 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2496 This->bigBlockDepotCount++;
2497 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2499 /* Flag the block depot.
2501 StorageImpl_SetNextBlockInChain(This,
2505 /* If necessary, flag the extended depot block.
2507 if (extIndex != BLOCK_UNUSED)
2508 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2510 /* Save header information.
2512 StorageImpl_SaveFileHeader(This);
2516 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2518 if (depotBuffer != 0)
2520 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2521 ( nextBlockIndex != BLOCK_UNUSED))
2523 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2525 if (nextBlockIndex == BLOCK_UNUSED)
2527 freeBlock = (depotIndex * blocksPerDepot) +
2528 (depotBlockOffset/sizeof(ULONG));
2531 depotBlockOffset += sizeof(ULONG);
2534 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2538 depotBlockOffset = 0;
2541 This->prevFreeBlock = freeBlock;
2546 /******************************************************************************
2547 * Storage32Impl_AddBlockDepot
2549 * This will create a depot block, essentially it is a block initialized
2552 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2556 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2559 * Initialize blocks as free
2561 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2563 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2566 /******************************************************************************
2567 * Storage32Impl_GetExtDepotBlock
2569 * Returns the index of the block that corresponds to the specified depot
2570 * index. This method is only for depot indexes equal or greater than
2571 * COUNT_BBDEPOTINHEADER.
2573 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2575 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2576 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2577 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2578 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2579 ULONG blockIndex = BLOCK_UNUSED;
2580 ULONG extBlockIndex = This->extBigBlockDepotStart;
2582 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2584 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2585 return BLOCK_UNUSED;
2587 while (extBlockCount > 0)
2589 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2593 if (extBlockIndex != BLOCK_UNUSED)
2597 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2599 if (depotBuffer != 0)
2601 StorageUtl_ReadDWord(depotBuffer,
2602 extBlockOffset * sizeof(ULONG),
2605 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2612 /******************************************************************************
2613 * Storage32Impl_SetExtDepotBlock
2615 * Associates the specified block index to the specified depot index.
2616 * This method is only for depot indexes equal or greater than
2617 * COUNT_BBDEPOTINHEADER.
2619 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2623 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2624 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2625 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2626 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2627 ULONG extBlockIndex = This->extBigBlockDepotStart;
2629 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2631 while (extBlockCount > 0)
2633 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2637 if (extBlockIndex != BLOCK_UNUSED)
2641 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2643 if (depotBuffer != 0)
2645 StorageUtl_WriteDWord(depotBuffer,
2646 extBlockOffset * sizeof(ULONG),
2649 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2654 /******************************************************************************
2655 * Storage32Impl_AddExtBlockDepot
2657 * Creates an extended depot block.
2659 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2661 ULONG numExtBlocks = This->extBigBlockDepotCount;
2662 ULONG nextExtBlock = This->extBigBlockDepotStart;
2663 BYTE* depotBuffer = NULL;
2664 ULONG index = BLOCK_UNUSED;
2665 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2666 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2667 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2669 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2670 blocksPerDepotBlock;
2672 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2675 * The first extended block.
2677 This->extBigBlockDepotStart = index;
2683 * Follow the chain to the last one.
2685 for (i = 0; i < (numExtBlocks - 1); i++)
2687 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2691 * Add the new extended block to the chain.
2693 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2694 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2695 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2699 * Initialize this block.
2701 depotBuffer = StorageImpl_GetBigBlock(This, index);
2702 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2703 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2708 /******************************************************************************
2709 * Storage32Impl_FreeBigBlock
2711 * This method will flag the specified block as free in the big block depot.
2713 void StorageImpl_FreeBigBlock(
2717 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2719 if (blockIndex < This->prevFreeBlock)
2720 This->prevFreeBlock = blockIndex;
2723 /************************************************************************
2724 * Storage32Impl_GetNextBlockInChain
2726 * This method will retrieve the block index of the next big block in
2729 * Params: This - Pointer to the Storage object.
2730 * blockIndex - Index of the block to retrieve the chain
2732 * nextBlockIndex - receives the return value.
2734 * Returns: This method returns the index of the next block in the chain.
2735 * It will return the constants:
2736 * BLOCK_SPECIAL - If the block given was not part of a
2738 * BLOCK_END_OF_CHAIN - If the block given was the last in
2740 * BLOCK_UNUSED - If the block given was not past of a chain
2742 * BLOCK_EXTBBDEPOT - This block is part of the extended
2745 * See Windows documentation for more details on IStorage methods.
2747 HRESULT StorageImpl_GetNextBlockInChain(
2750 ULONG* nextBlockIndex)
2752 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2753 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2754 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2756 ULONG depotBlockIndexPos;
2759 *nextBlockIndex = BLOCK_SPECIAL;
2761 if(depotBlockCount >= This->bigBlockDepotCount)
2763 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2764 This->bigBlockDepotCount);
2765 return STG_E_READFAULT;
2769 * Cache the currently accessed depot block.
2771 if (depotBlockCount != This->indexBlockDepotCached)
2773 This->indexBlockDepotCached = depotBlockCount;
2775 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2777 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2782 * We have to look in the extended depot.
2784 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2787 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2790 return STG_E_READFAULT;
2792 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2794 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2795 This->blockDepotCached[index] = *nextBlockIndex;
2797 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2800 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2805 /******************************************************************************
2806 * Storage32Impl_GetNextExtendedBlock
2808 * Given an extended block this method will return the next extended block.
2811 * The last ULONG of an extended block is the block index of the next
2812 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2816 * - The index of the next extended block
2817 * - BLOCK_UNUSED: there is no next extended block.
2818 * - Any other return values denotes failure.
2820 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2822 ULONG nextBlockIndex = BLOCK_SPECIAL;
2823 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2826 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2830 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2832 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2835 return nextBlockIndex;
2838 /******************************************************************************
2839 * Storage32Impl_SetNextBlockInChain
2841 * This method will write the index of the specified block's next block
2842 * in the big block depot.
2844 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2847 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2848 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2849 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2852 void StorageImpl_SetNextBlockInChain(
2857 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2858 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2859 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2860 ULONG depotBlockIndexPos;
2863 assert(depotBlockCount < This->bigBlockDepotCount);
2864 assert(blockIndex != nextBlock);
2866 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2868 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2873 * We have to look in the extended depot.
2875 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2878 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2882 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2883 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2887 * Update the cached block depot, if necessary.
2889 if (depotBlockCount == This->indexBlockDepotCached)
2891 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2895 /******************************************************************************
2896 * Storage32Impl_LoadFileHeader
2898 * This method will read in the file header, i.e. big block index -1.
2900 HRESULT StorageImpl_LoadFileHeader(
2903 HRESULT hr = STG_E_FILENOTFOUND;
2904 void* headerBigBlock = NULL;
2908 * Get a pointer to the big block of data containing the header.
2910 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2913 * Extract the information from the header.
2915 if (headerBigBlock!=0)
2918 * Check for the "magic number" signature and return an error if it is not
2921 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2923 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2924 return STG_E_OLDFORMAT;
2927 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2929 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2930 return STG_E_INVALIDHEADER;
2933 StorageUtl_ReadWord(
2935 OFFSET_BIGBLOCKSIZEBITS,
2936 &This->bigBlockSizeBits);
2938 StorageUtl_ReadWord(
2940 OFFSET_SMALLBLOCKSIZEBITS,
2941 &This->smallBlockSizeBits);
2943 StorageUtl_ReadDWord(
2945 OFFSET_BBDEPOTCOUNT,
2946 &This->bigBlockDepotCount);
2948 StorageUtl_ReadDWord(
2950 OFFSET_ROOTSTARTBLOCK,
2951 &This->rootStartBlock);
2953 StorageUtl_ReadDWord(
2955 OFFSET_SBDEPOTSTART,
2956 &This->smallBlockDepotStart);
2958 StorageUtl_ReadDWord(
2960 OFFSET_EXTBBDEPOTSTART,
2961 &This->extBigBlockDepotStart);
2963 StorageUtl_ReadDWord(
2965 OFFSET_EXTBBDEPOTCOUNT,
2966 &This->extBigBlockDepotCount);
2968 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2970 StorageUtl_ReadDWord(
2972 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2973 &(This->bigBlockDepotStart[index]));
2977 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2981 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2982 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2986 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2987 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2991 * Right now, the code is making some assumptions about the size of the
2992 * blocks, just make sure they are what we're expecting.
2994 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2995 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2997 WARN("Broken OLE storage file\n");
2998 hr = STG_E_INVALIDHEADER;
3004 * Release the block.
3006 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3012 /******************************************************************************
3013 * Storage32Impl_SaveFileHeader
3015 * This method will save to the file the header, i.e. big block -1.
3017 void StorageImpl_SaveFileHeader(
3020 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3025 * Get a pointer to the big block of data containing the header.
3027 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3030 * If the block read failed, the file is probably new.
3035 * Initialize for all unknown fields.
3037 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3040 * Initialize the magic number.
3042 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3045 * And a bunch of things we don't know what they mean
3047 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3048 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3049 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3050 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3054 * Write the information to the header.
3056 StorageUtl_WriteWord(
3058 OFFSET_BIGBLOCKSIZEBITS,
3059 This->bigBlockSizeBits);
3061 StorageUtl_WriteWord(
3063 OFFSET_SMALLBLOCKSIZEBITS,
3064 This->smallBlockSizeBits);
3066 StorageUtl_WriteDWord(
3068 OFFSET_BBDEPOTCOUNT,
3069 This->bigBlockDepotCount);
3071 StorageUtl_WriteDWord(
3073 OFFSET_ROOTSTARTBLOCK,
3074 This->rootStartBlock);
3076 StorageUtl_WriteDWord(
3078 OFFSET_SBDEPOTSTART,
3079 This->smallBlockDepotStart);
3081 StorageUtl_WriteDWord(
3083 OFFSET_SBDEPOTCOUNT,
3084 This->smallBlockDepotChain ?
3085 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3087 StorageUtl_WriteDWord(
3089 OFFSET_EXTBBDEPOTSTART,
3090 This->extBigBlockDepotStart);
3092 StorageUtl_WriteDWord(
3094 OFFSET_EXTBBDEPOTCOUNT,
3095 This->extBigBlockDepotCount);
3097 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3099 StorageUtl_WriteDWord(
3101 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3102 (This->bigBlockDepotStart[index]));
3106 * Write the big block back to the file.
3108 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3111 /******************************************************************************
3112 * Storage32Impl_ReadProperty
3114 * This method will read the specified property from the property chain.
3116 BOOL StorageImpl_ReadProperty(
3119 StgProperty* buffer)
3121 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3122 ULARGE_INTEGER offsetInPropSet;
3123 BOOL readSuccessful;
3126 offsetInPropSet.s.HighPart = 0;
3127 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3129 readSuccessful = BlockChainStream_ReadAt(
3130 This->rootBlockChain,
3138 /* replace the name of root entry (often "Root Entry") by the file name */
3139 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3140 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3142 memset(buffer->name, 0, sizeof(buffer->name));
3146 PROPERTY_NAME_BUFFER_LEN );
3147 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3149 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3151 StorageUtl_ReadWord(
3153 OFFSET_PS_NAMELENGTH,
3154 &buffer->sizeOfNameString);
3156 StorageUtl_ReadDWord(
3158 OFFSET_PS_PREVIOUSPROP,
3159 &buffer->previousProperty);
3161 StorageUtl_ReadDWord(
3164 &buffer->nextProperty);
3166 StorageUtl_ReadDWord(
3169 &buffer->dirProperty);
3171 StorageUtl_ReadGUID(
3174 &buffer->propertyUniqueID);
3176 StorageUtl_ReadDWord(
3179 &buffer->timeStampS1);
3181 StorageUtl_ReadDWord(
3184 &buffer->timeStampD1);
3186 StorageUtl_ReadDWord(
3189 &buffer->timeStampS2);
3191 StorageUtl_ReadDWord(
3194 &buffer->timeStampD2);
3196 StorageUtl_ReadDWord(
3198 OFFSET_PS_STARTBLOCK,
3199 &buffer->startingBlock);
3201 StorageUtl_ReadDWord(
3204 &buffer->size.s.LowPart);
3206 buffer->size.s.HighPart = 0;
3209 return readSuccessful;
3212 /*********************************************************************
3213 * Write the specified property into the property chain
3215 BOOL StorageImpl_WriteProperty(
3218 StgProperty* buffer)
3220 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3221 ULARGE_INTEGER offsetInPropSet;
3222 BOOL writeSuccessful;
3225 offsetInPropSet.s.HighPart = 0;
3226 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3228 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3231 currentProperty + OFFSET_PS_NAME,
3233 PROPERTY_NAME_BUFFER_LEN );
3235 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3237 StorageUtl_WriteWord(
3239 OFFSET_PS_NAMELENGTH,
3240 buffer->sizeOfNameString);
3242 StorageUtl_WriteDWord(
3244 OFFSET_PS_PREVIOUSPROP,
3245 buffer->previousProperty);
3247 StorageUtl_WriteDWord(
3250 buffer->nextProperty);
3252 StorageUtl_WriteDWord(
3255 buffer->dirProperty);
3257 StorageUtl_WriteGUID(
3260 &buffer->propertyUniqueID);
3262 StorageUtl_WriteDWord(
3265 buffer->timeStampS1);
3267 StorageUtl_WriteDWord(
3270 buffer->timeStampD1);
3272 StorageUtl_WriteDWord(
3275 buffer->timeStampS2);
3277 StorageUtl_WriteDWord(
3280 buffer->timeStampD2);
3282 StorageUtl_WriteDWord(
3284 OFFSET_PS_STARTBLOCK,
3285 buffer->startingBlock);
3287 StorageUtl_WriteDWord(
3290 buffer->size.s.LowPart);
3292 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3297 return writeSuccessful;
3300 BOOL StorageImpl_ReadBigBlock(
3305 void* bigBlockBuffer;
3307 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3309 if (bigBlockBuffer!=0)
3311 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3313 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3321 BOOL StorageImpl_WriteBigBlock(
3326 void* bigBlockBuffer;
3328 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3330 if (bigBlockBuffer!=0)
3332 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3334 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3342 void* StorageImpl_GetROBigBlock(
3346 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3349 void* StorageImpl_GetBigBlock(
3353 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3356 void StorageImpl_ReleaseBigBlock(
3360 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3363 /******************************************************************************
3364 * Storage32Impl_SmallBlocksToBigBlocks
3366 * This method will convert a small block chain to a big block chain.
3367 * The small block chain will be destroyed.
3369 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3371 SmallBlockChainStream** ppsbChain)
3373 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3374 ULARGE_INTEGER size, offset;
3375 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3376 ULONG propertyIndex;
3377 BOOL successRead, successWrite;
3378 StgProperty chainProperty;
3380 BlockChainStream *bbTempChain = NULL;
3381 BlockChainStream *bigBlockChain = NULL;
3384 * Create a temporary big block chain that doesn't have
3385 * an associated property. This temporary chain will be
3386 * used to copy data from small blocks to big blocks.
3388 bbTempChain = BlockChainStream_Construct(This,
3391 if(!bbTempChain) return NULL;
3393 * Grow the big block chain.
3395 size = SmallBlockChainStream_GetSize(*ppsbChain);
3396 BlockChainStream_SetSize(bbTempChain, size);
3399 * Copy the contents of the small block chain to the big block chain
3400 * by small block size increments.
3402 offset.s.LowPart = 0;
3403 offset.s.HighPart = 0;
3407 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3410 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3412 DEF_SMALL_BLOCK_SIZE,
3415 cbTotalRead += cbRead;
3417 successWrite = BlockChainStream_WriteAt(bbTempChain,
3422 cbTotalWritten += cbWritten;
3424 offset.s.LowPart += This->smallBlockSize;
3426 } while (successRead && successWrite);
3427 HeapFree(GetProcessHeap(),0,buffer);
3429 assert(cbTotalRead == cbTotalWritten);
3432 * Destroy the small block chain.
3434 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3435 size.s.HighPart = 0;
3437 SmallBlockChainStream_SetSize(*ppsbChain, size);
3438 SmallBlockChainStream_Destroy(*ppsbChain);
3442 * Change the property information. This chain is now a big block chain
3443 * and it doesn't reside in the small blocks chain anymore.
3445 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3447 chainProperty.startingBlock = bbHeadOfChain;
3449 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3452 * Destroy the temporary propertyless big block chain.
3453 * Create a new big block chain associated with this property.
3455 BlockChainStream_Destroy(bbTempChain);
3456 bigBlockChain = BlockChainStream_Construct(This,
3460 return bigBlockChain;
3463 /******************************************************************************
3464 ** Storage32InternalImpl implementation
3467 StorageInternalImpl* StorageInternalImpl_Construct(
3468 StorageImpl* ancestorStorage,
3469 ULONG rootPropertyIndex)
3471 StorageInternalImpl* newStorage;
3474 * Allocate space for the new storage object
3476 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3480 memset(newStorage, 0, sizeof(StorageInternalImpl));
3483 * Initialize the virtual function table.
3485 newStorage->lpVtbl = &Storage32InternalImpl_Vtbl;
3486 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3489 * Keep the ancestor storage pointer and nail a reference to it.
3491 newStorage->ancestorStorage = ancestorStorage;
3492 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3495 * Keep the index of the root property set for this storage,
3497 newStorage->rootPropertySetIndex = rootPropertyIndex;
3505 void StorageInternalImpl_Destroy(
3506 StorageInternalImpl* This)
3508 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3509 HeapFree(GetProcessHeap(), 0, This);
3512 /******************************************************************************
3514 ** Storage32InternalImpl_Commit
3516 ** The non-root storages cannot be opened in transacted mode thus this function
3519 HRESULT WINAPI StorageInternalImpl_Commit(
3521 DWORD grfCommitFlags) /* [in] */
3526 /******************************************************************************
3528 ** Storage32InternalImpl_Revert
3530 ** The non-root storages cannot be opened in transacted mode thus this function
3533 HRESULT WINAPI StorageInternalImpl_Revert(
3539 /******************************************************************************
3540 ** IEnumSTATSTGImpl implementation
3543 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3544 StorageImpl* parentStorage,
3545 ULONG firstPropertyNode)
3547 IEnumSTATSTGImpl* newEnumeration;
3549 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3551 if (newEnumeration!=0)
3554 * Set-up the virtual function table and reference count.
3556 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3557 newEnumeration->ref = 0;
3560 * We want to nail-down the reference to the storage in case the
3561 * enumeration out-lives the storage in the client application.
3563 newEnumeration->parentStorage = parentStorage;
3564 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3566 newEnumeration->firstPropertyNode = firstPropertyNode;
3569 * Initialize the search stack
3571 newEnumeration->stackSize = 0;
3572 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3573 newEnumeration->stackToVisit =
3574 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3577 * Make sure the current node of the iterator is the first one.
3579 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3582 return newEnumeration;
3585 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3587 IStorage_Release((IStorage*)This->parentStorage);
3588 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3589 HeapFree(GetProcessHeap(), 0, This);
3592 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3593 IEnumSTATSTG* iface,
3597 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3600 * Perform a sanity check on the parameters.
3603 return E_INVALIDARG;
3606 * Initialize the return parameter.
3611 * Compare the riid with the interface IDs implemented by this object.
3613 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3615 *ppvObject = (IEnumSTATSTG*)This;
3617 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3619 *ppvObject = (IEnumSTATSTG*)This;
3623 * Check that we obtained an interface.
3625 if ((*ppvObject)==0)
3626 return E_NOINTERFACE;
3629 * Query Interface always increases the reference count by one when it is
3632 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3637 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3638 IEnumSTATSTG* iface)
3640 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3646 ULONG WINAPI IEnumSTATSTGImpl_Release(
3647 IEnumSTATSTG* iface)
3649 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3657 * If the reference count goes down to 0, perform suicide.
3661 IEnumSTATSTGImpl_Destroy(This);
3667 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3668 IEnumSTATSTG* iface,
3671 ULONG* pceltFetched)
3673 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3675 StgProperty currentProperty;
3676 STATSTG* currentReturnStruct = rgelt;
3677 ULONG objectFetched = 0;
3678 ULONG currentSearchNode;
3681 * Perform a sanity check on the parameters.
3683 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3684 return E_INVALIDARG;
3687 * To avoid the special case, get another pointer to a ULONG value if
3688 * the caller didn't supply one.
3690 if (pceltFetched==0)
3691 pceltFetched = &objectFetched;
3694 * Start the iteration, we will iterate until we hit the end of the
3695 * linked list or until we hit the number of items to iterate through
3700 * Start with the node at the top of the stack.
3702 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3704 while ( ( *pceltFetched < celt) &&
3705 ( currentSearchNode!=PROPERTY_NULL) )
3708 * Remove the top node from the stack
3710 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3713 * Read the property from the storage.
3715 StorageImpl_ReadProperty(This->parentStorage,
3720 * Copy the information to the return buffer.
3722 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3727 * Step to the next item in the iteration
3730 currentReturnStruct++;
3733 * Push the next search node in the search stack.
3735 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3738 * continue the iteration.
3740 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3743 if (*pceltFetched == celt)
3750 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3751 IEnumSTATSTG* iface,
3754 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3756 StgProperty currentProperty;
3757 ULONG objectFetched = 0;
3758 ULONG currentSearchNode;
3761 * Start with the node at the top of the stack.
3763 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3765 while ( (objectFetched < celt) &&
3766 (currentSearchNode!=PROPERTY_NULL) )
3769 * Remove the top node from the stack
3771 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3774 * Read the property from the storage.
3776 StorageImpl_ReadProperty(This->parentStorage,
3781 * Step to the next item in the iteration
3786 * Push the next search node in the search stack.
3788 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3791 * continue the iteration.
3793 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3796 if (objectFetched == celt)
3802 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3803 IEnumSTATSTG* iface)
3805 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3807 StgProperty rootProperty;
3808 BOOL readSuccessful;
3811 * Re-initialize the search stack to an empty stack
3813 This->stackSize = 0;
3816 * Read the root property from the storage.
3818 readSuccessful = StorageImpl_ReadProperty(
3819 This->parentStorage,
3820 This->firstPropertyNode,
3825 assert(rootProperty.sizeOfNameString!=0);
3828 * Push the search node in the search stack.
3830 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3836 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3837 IEnumSTATSTG* iface,
3838 IEnumSTATSTG** ppenum)
3840 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3842 IEnumSTATSTGImpl* newClone;
3845 * Perform a sanity check on the parameters.
3848 return E_INVALIDARG;
3850 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3851 This->firstPropertyNode);
3855 * The new clone enumeration must point to the same current node as
3858 newClone->stackSize = This->stackSize ;
3859 newClone->stackMaxSize = This->stackMaxSize ;
3860 newClone->stackToVisit =
3861 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3864 newClone->stackToVisit,
3866 sizeof(ULONG) * newClone->stackSize);
3868 *ppenum = (IEnumSTATSTG*)newClone;
3871 * Don't forget to nail down a reference to the clone before
3874 IEnumSTATSTGImpl_AddRef(*ppenum);
3879 INT IEnumSTATSTGImpl_FindParentProperty(
3880 IEnumSTATSTGImpl *This,
3881 ULONG childProperty,
3882 StgProperty *currentProperty,
3885 ULONG currentSearchNode;
3889 * To avoid the special case, get another pointer to a ULONG value if
3890 * the caller didn't supply one.
3893 thisNodeId = &foundNode;
3896 * Start with the node at the top of the stack.
3898 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3901 while (currentSearchNode!=PROPERTY_NULL)
3904 * Store the current node in the returned parameters
3906 *thisNodeId = currentSearchNode;
3909 * Remove the top node from the stack
3911 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3914 * Read the property from the storage.
3916 StorageImpl_ReadProperty(
3917 This->parentStorage,
3921 if (currentProperty->previousProperty == childProperty)
3922 return PROPERTY_RELATION_PREVIOUS;
3924 else if (currentProperty->nextProperty == childProperty)
3925 return PROPERTY_RELATION_NEXT;
3927 else if (currentProperty->dirProperty == childProperty)
3928 return PROPERTY_RELATION_DIR;
3931 * Push the next search node in the search stack.
3933 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3936 * continue the iteration.
3938 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3941 return PROPERTY_NULL;
3944 ULONG IEnumSTATSTGImpl_FindProperty(
3945 IEnumSTATSTGImpl* This,
3946 const OLECHAR* lpszPropName,
3947 StgProperty* currentProperty)
3949 ULONG currentSearchNode;
3952 * Start with the node at the top of the stack.
3954 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3956 while (currentSearchNode!=PROPERTY_NULL)
3959 * Remove the top node from the stack
3961 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3964 * Read the property from the storage.
3966 StorageImpl_ReadProperty(This->parentStorage,
3970 if ( propertyNameCmp(
3971 (OLECHAR*)currentProperty->name,
3972 (OLECHAR*)lpszPropName) == 0)
3973 return currentSearchNode;
3976 * Push the next search node in the search stack.
3978 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3981 * continue the iteration.
3983 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3986 return PROPERTY_NULL;
3989 void IEnumSTATSTGImpl_PushSearchNode(
3990 IEnumSTATSTGImpl* This,
3993 StgProperty rootProperty;
3994 BOOL readSuccessful;
3997 * First, make sure we're not trying to push an unexisting node.
3999 if (nodeToPush==PROPERTY_NULL)
4003 * First push the node to the stack
4005 if (This->stackSize == This->stackMaxSize)
4007 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4009 This->stackToVisit = HeapReAlloc(
4013 sizeof(ULONG) * This->stackMaxSize);
4016 This->stackToVisit[This->stackSize] = nodeToPush;
4020 * Read the root property from the storage.
4022 readSuccessful = StorageImpl_ReadProperty(
4023 This->parentStorage,
4029 assert(rootProperty.sizeOfNameString!=0);
4032 * Push the previous search node in the search stack.
4034 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4038 ULONG IEnumSTATSTGImpl_PopSearchNode(
4039 IEnumSTATSTGImpl* This,
4044 if (This->stackSize == 0)
4045 return PROPERTY_NULL;
4047 topNode = This->stackToVisit[This->stackSize-1];
4055 /******************************************************************************
4056 ** StorageUtl implementation
4059 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4061 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4064 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4066 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4069 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4071 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4074 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4076 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4079 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4081 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4082 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4083 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4085 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4088 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4090 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4091 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4092 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4094 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4097 void StorageUtl_CopyPropertyToSTATSTG(
4098 STATSTG* destination,
4099 StgProperty* source,
4103 * The copy of the string occurs only when the flag is not set
4105 if ((statFlags & STATFLAG_NONAME) != 0)
4107 destination->pwcsName = 0;
4111 destination->pwcsName =
4112 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4114 strcpyW((LPWSTR)destination->pwcsName, source->name);
4117 switch (source->propertyType)
4119 case PROPTYPE_STORAGE:
4121 destination->type = STGTY_STORAGE;
4123 case PROPTYPE_STREAM:
4124 destination->type = STGTY_STREAM;
4127 destination->type = STGTY_STREAM;
4131 destination->cbSize = source->size;
4133 currentReturnStruct->mtime = {0}; TODO
4134 currentReturnStruct->ctime = {0};
4135 currentReturnStruct->atime = {0};
4137 destination->grfMode = 0;
4138 destination->grfLocksSupported = 0;
4139 destination->clsid = source->propertyUniqueID;
4140 destination->grfStateBits = 0;
4141 destination->reserved = 0;
4144 /******************************************************************************
4145 ** BlockChainStream implementation
4148 BlockChainStream* BlockChainStream_Construct(
4149 StorageImpl* parentStorage,
4150 ULONG* headOfStreamPlaceHolder,
4151 ULONG propertyIndex)
4153 BlockChainStream* newStream;
4156 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4158 newStream->parentStorage = parentStorage;
4159 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4160 newStream->ownerPropertyIndex = propertyIndex;
4161 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4162 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4163 newStream->numBlocks = 0;
4165 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4167 while (blockIndex != BLOCK_END_OF_CHAIN)
4169 newStream->numBlocks++;
4170 newStream->tailIndex = blockIndex;
4172 if(FAILED(StorageImpl_GetNextBlockInChain(
4177 HeapFree(GetProcessHeap(), 0, newStream);
4185 void BlockChainStream_Destroy(BlockChainStream* This)
4187 HeapFree(GetProcessHeap(), 0, This);
4190 /******************************************************************************
4191 * BlockChainStream_GetHeadOfChain
4193 * Returns the head of this stream chain.
4194 * Some special chains don't have properties, their heads are kept in
4195 * This->headOfStreamPlaceHolder.
4198 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4200 StgProperty chainProperty;
4201 BOOL readSuccessful;
4203 if (This->headOfStreamPlaceHolder != 0)
4204 return *(This->headOfStreamPlaceHolder);
4206 if (This->ownerPropertyIndex != PROPERTY_NULL)
4208 readSuccessful = StorageImpl_ReadProperty(
4209 This->parentStorage,
4210 This->ownerPropertyIndex,
4215 return chainProperty.startingBlock;
4219 return BLOCK_END_OF_CHAIN;
4222 /******************************************************************************
4223 * BlockChainStream_GetCount
4225 * Returns the number of blocks that comprises this chain.
4226 * This is not the size of the stream as the last block may not be full!
4229 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4234 blockIndex = BlockChainStream_GetHeadOfChain(This);
4236 while (blockIndex != BLOCK_END_OF_CHAIN)
4240 if(FAILED(StorageImpl_GetNextBlockInChain(
4241 This->parentStorage,
4250 /******************************************************************************
4251 * BlockChainStream_ReadAt
4253 * Reads a specified number of bytes from this chain at the specified offset.
4254 * bytesRead may be NULL.
4255 * Failure will be returned if the specified number of bytes has not been read.
4257 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4258 ULARGE_INTEGER offset,
4263 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4264 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4265 ULONG bytesToReadInBuffer;
4268 BYTE* bigBlockBuffer;
4271 * Find the first block in the stream that contains part of the buffer.
4273 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4274 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4275 (blockNoInSequence < This->lastBlockNoInSequence) )
4277 blockIndex = BlockChainStream_GetHeadOfChain(This);
4278 This->lastBlockNoInSequence = blockNoInSequence;
4282 ULONG temp = blockNoInSequence;
4284 blockIndex = This->lastBlockNoInSequenceIndex;
4285 blockNoInSequence -= This->lastBlockNoInSequence;
4286 This->lastBlockNoInSequence = temp;
4289 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4291 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4293 blockNoInSequence--;
4296 This->lastBlockNoInSequenceIndex = blockIndex;
4299 * Start reading the buffer.
4302 bufferWalker = buffer;
4304 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4307 * Calculate how many bytes we can copy from this big block.
4309 bytesToReadInBuffer =
4310 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4313 * Copy those bytes to the buffer
4316 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4318 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4320 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4323 * Step to the next big block.
4325 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4328 bufferWalker += bytesToReadInBuffer;
4329 size -= bytesToReadInBuffer;
4330 *bytesRead += bytesToReadInBuffer;
4331 offsetInBlock = 0; /* There is no offset on the next block */
4338 /******************************************************************************
4339 * BlockChainStream_WriteAt
4341 * Writes the specified number of bytes to this chain at the specified offset.
4342 * bytesWritten may be NULL.
4343 * Will fail if not all specified number of bytes have been written.
4345 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4346 ULARGE_INTEGER offset,
4349 ULONG* bytesWritten)
4351 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4352 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4356 BYTE* bigBlockBuffer;
4359 * Find the first block in the stream that contains part of the buffer.
4361 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4362 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4363 (blockNoInSequence < This->lastBlockNoInSequence) )
4365 blockIndex = BlockChainStream_GetHeadOfChain(This);
4366 This->lastBlockNoInSequence = blockNoInSequence;
4370 ULONG temp = blockNoInSequence;
4372 blockIndex = This->lastBlockNoInSequenceIndex;
4373 blockNoInSequence -= This->lastBlockNoInSequence;
4374 This->lastBlockNoInSequence = temp;
4377 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4379 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4382 blockNoInSequence--;
4385 This->lastBlockNoInSequenceIndex = blockIndex;
4388 * Here, I'm casting away the constness on the buffer variable
4389 * This is OK since we don't intend to modify that buffer.
4392 bufferWalker = (BYTE*)buffer;
4394 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4397 * Calculate how many bytes we can copy from this big block.
4400 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4403 * Copy those bytes to the buffer
4405 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4407 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4409 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4412 * Step to the next big block.
4414 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4417 bufferWalker += bytesToWrite;
4418 size -= bytesToWrite;
4419 *bytesWritten += bytesToWrite;
4420 offsetInBlock = 0; /* There is no offset on the next block */
4426 /******************************************************************************
4427 * BlockChainStream_Shrink
4429 * Shrinks this chain in the big block depot.
4431 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4432 ULARGE_INTEGER newSize)
4434 ULONG blockIndex, extraBlock;
4439 * Reset the last accessed block cache.
4441 This->lastBlockNoInSequence = 0xFFFFFFFF;
4442 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4445 * Figure out how many blocks are needed to contain the new size
4447 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4449 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4452 blockIndex = BlockChainStream_GetHeadOfChain(This);
4455 * Go to the new end of chain
4457 while (count < numBlocks)
4459 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4465 /* Get the next block before marking the new end */
4466 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4470 /* Mark the new end of chain */
4471 StorageImpl_SetNextBlockInChain(
4472 This->parentStorage,
4474 BLOCK_END_OF_CHAIN);
4476 This->tailIndex = blockIndex;
4477 This->numBlocks = numBlocks;
4480 * Mark the extra blocks as free
4482 while (extraBlock != BLOCK_END_OF_CHAIN)
4484 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4487 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4488 extraBlock = blockIndex;
4494 /******************************************************************************
4495 * BlockChainStream_Enlarge
4497 * Grows this chain in the big block depot.
4499 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4500 ULARGE_INTEGER newSize)
4502 ULONG blockIndex, currentBlock;
4504 ULONG oldNumBlocks = 0;
4506 blockIndex = BlockChainStream_GetHeadOfChain(This);
4509 * Empty chain. Create the head.
4511 if (blockIndex == BLOCK_END_OF_CHAIN)
4513 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4514 StorageImpl_SetNextBlockInChain(This->parentStorage,
4516 BLOCK_END_OF_CHAIN);
4518 if (This->headOfStreamPlaceHolder != 0)
4520 *(This->headOfStreamPlaceHolder) = blockIndex;
4524 StgProperty chainProp;
4525 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4527 StorageImpl_ReadProperty(
4528 This->parentStorage,
4529 This->ownerPropertyIndex,
4532 chainProp.startingBlock = blockIndex;
4534 StorageImpl_WriteProperty(
4535 This->parentStorage,
4536 This->ownerPropertyIndex,
4540 This->tailIndex = blockIndex;
4541 This->numBlocks = 1;
4545 * Figure out how many blocks are needed to contain this stream
4547 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4549 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4553 * Go to the current end of chain
4555 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4557 currentBlock = blockIndex;
4559 while (blockIndex != BLOCK_END_OF_CHAIN)
4562 currentBlock = blockIndex;
4564 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4569 This->tailIndex = currentBlock;
4572 currentBlock = This->tailIndex;
4573 oldNumBlocks = This->numBlocks;
4576 * Add new blocks to the chain
4578 if (oldNumBlocks < newNumBlocks)
4580 while (oldNumBlocks < newNumBlocks)
4582 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4584 StorageImpl_SetNextBlockInChain(
4585 This->parentStorage,
4589 StorageImpl_SetNextBlockInChain(
4590 This->parentStorage,
4592 BLOCK_END_OF_CHAIN);
4594 currentBlock = blockIndex;
4598 This->tailIndex = blockIndex;
4599 This->numBlocks = newNumBlocks;
4605 /******************************************************************************
4606 * BlockChainStream_SetSize
4608 * Sets the size of this stream. The big block depot will be updated.
4609 * The file will grow if we grow the chain.
4611 * TODO: Free the actual blocks in the file when we shrink the chain.
4612 * Currently, the blocks are still in the file. So the file size
4613 * doesn't shrink even if we shrink streams.
4615 BOOL BlockChainStream_SetSize(
4616 BlockChainStream* This,
4617 ULARGE_INTEGER newSize)
4619 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4621 if (newSize.s.LowPart == size.s.LowPart)
4624 if (newSize.s.LowPart < size.s.LowPart)
4626 BlockChainStream_Shrink(This, newSize);
4630 ULARGE_INTEGER fileSize =
4631 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4633 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4636 * Make sure the file stays a multiple of blocksize
4638 if ((diff % This->parentStorage->bigBlockSize) != 0)
4639 diff += (This->parentStorage->bigBlockSize -
4640 (diff % This->parentStorage->bigBlockSize) );
4642 fileSize.s.LowPart += diff;
4643 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4645 BlockChainStream_Enlarge(This, newSize);
4651 /******************************************************************************
4652 * BlockChainStream_GetSize
4654 * Returns the size of this chain.
4655 * Will return the block count if this chain doesn't have a property.
4657 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4659 StgProperty chainProperty;
4661 if(This->headOfStreamPlaceHolder == NULL)
4664 * This chain is a data stream read the property and return
4665 * the appropriate size
4667 StorageImpl_ReadProperty(
4668 This->parentStorage,
4669 This->ownerPropertyIndex,
4672 return chainProperty.size;
4677 * this chain is a chain that does not have a property, figure out the
4678 * size by making the product number of used blocks times the
4681 ULARGE_INTEGER result;
4682 result.s.HighPart = 0;
4685 BlockChainStream_GetCount(This) *
4686 This->parentStorage->bigBlockSize;
4692 /******************************************************************************
4693 ** SmallBlockChainStream implementation
4696 SmallBlockChainStream* SmallBlockChainStream_Construct(
4697 StorageImpl* parentStorage,
4698 ULONG propertyIndex)
4700 SmallBlockChainStream* newStream;
4702 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4704 newStream->parentStorage = parentStorage;
4705 newStream->ownerPropertyIndex = propertyIndex;
4710 void SmallBlockChainStream_Destroy(
4711 SmallBlockChainStream* This)
4713 HeapFree(GetProcessHeap(), 0, This);
4716 /******************************************************************************
4717 * SmallBlockChainStream_GetHeadOfChain
4719 * Returns the head of this chain of small blocks.
4721 ULONG SmallBlockChainStream_GetHeadOfChain(
4722 SmallBlockChainStream* This)
4724 StgProperty chainProperty;
4725 BOOL readSuccessful;
4727 if (This->ownerPropertyIndex)
4729 readSuccessful = StorageImpl_ReadProperty(
4730 This->parentStorage,
4731 This->ownerPropertyIndex,
4736 return chainProperty.startingBlock;
4741 return BLOCK_END_OF_CHAIN;
4744 /******************************************************************************
4745 * SmallBlockChainStream_GetNextBlockInChain
4747 * Returns the index of the next small block in this chain.
4750 * - BLOCK_END_OF_CHAIN: end of this chain
4751 * - BLOCK_UNUSED: small block 'blockIndex' is free
4753 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4754 SmallBlockChainStream* This,
4756 ULONG* nextBlockInChain)
4758 ULARGE_INTEGER offsetOfBlockInDepot;
4763 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4765 offsetOfBlockInDepot.s.HighPart = 0;
4766 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4769 * Read those bytes in the buffer from the small block file.
4771 success = BlockChainStream_ReadAt(
4772 This->parentStorage->smallBlockDepotChain,
4773 offsetOfBlockInDepot,
4780 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4784 return STG_E_READFAULT;
4787 /******************************************************************************
4788 * SmallBlockChainStream_SetNextBlockInChain
4790 * Writes the index of the next block of the specified block in the small
4792 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4793 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4795 void SmallBlockChainStream_SetNextBlockInChain(
4796 SmallBlockChainStream* This,
4800 ULARGE_INTEGER offsetOfBlockInDepot;
4804 offsetOfBlockInDepot.s.HighPart = 0;
4805 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4807 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4810 * Read those bytes in the buffer from the small block file.
4812 BlockChainStream_WriteAt(
4813 This->parentStorage->smallBlockDepotChain,
4814 offsetOfBlockInDepot,
4820 /******************************************************************************
4821 * SmallBlockChainStream_FreeBlock
4823 * Flag small block 'blockIndex' as free in the small block depot.
4825 void SmallBlockChainStream_FreeBlock(
4826 SmallBlockChainStream* This,
4829 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4832 /******************************************************************************
4833 * SmallBlockChainStream_GetNextFreeBlock
4835 * Returns the index of a free small block. The small block depot will be
4836 * enlarged if necessary. The small block chain will also be enlarged if
4839 ULONG SmallBlockChainStream_GetNextFreeBlock(
4840 SmallBlockChainStream* This)
4842 ULARGE_INTEGER offsetOfBlockInDepot;
4845 ULONG blockIndex = 0;
4846 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4847 BOOL success = TRUE;
4848 ULONG smallBlocksPerBigBlock;
4850 offsetOfBlockInDepot.s.HighPart = 0;
4853 * Scan the small block depot for a free block
4855 while (nextBlockIndex != BLOCK_UNUSED)
4857 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4859 success = BlockChainStream_ReadAt(
4860 This->parentStorage->smallBlockDepotChain,
4861 offsetOfBlockInDepot,
4867 * If we run out of space for the small block depot, enlarge it
4871 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4873 if (nextBlockIndex != BLOCK_UNUSED)
4879 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4881 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4882 ULONG nextBlock, newsbdIndex;
4883 BYTE* smallBlockDepot;
4885 nextBlock = sbdIndex;
4886 while (nextBlock != BLOCK_END_OF_CHAIN)
4888 sbdIndex = nextBlock;
4889 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4892 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4893 if (sbdIndex != BLOCK_END_OF_CHAIN)
4894 StorageImpl_SetNextBlockInChain(
4895 This->parentStorage,
4899 StorageImpl_SetNextBlockInChain(
4900 This->parentStorage,
4902 BLOCK_END_OF_CHAIN);
4905 * Initialize all the small blocks to free
4908 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4910 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4911 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4916 * We have just created the small block depot.
4918 StgProperty rootProp;
4922 * Save it in the header
4924 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4925 StorageImpl_SaveFileHeader(This->parentStorage);
4928 * And allocate the first big block that will contain small blocks
4931 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4933 StorageImpl_SetNextBlockInChain(
4934 This->parentStorage,
4936 BLOCK_END_OF_CHAIN);
4938 StorageImpl_ReadProperty(
4939 This->parentStorage,
4940 This->parentStorage->rootPropertySetIndex,
4943 rootProp.startingBlock = sbStartIndex;
4944 rootProp.size.s.HighPart = 0;
4945 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4947 StorageImpl_WriteProperty(
4948 This->parentStorage,
4949 This->parentStorage->rootPropertySetIndex,
4955 smallBlocksPerBigBlock =
4956 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4959 * Verify if we have to allocate big blocks to contain small blocks
4961 if (blockIndex % smallBlocksPerBigBlock == 0)
4963 StgProperty rootProp;
4964 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4966 StorageImpl_ReadProperty(
4967 This->parentStorage,
4968 This->parentStorage->rootPropertySetIndex,
4971 if (rootProp.size.s.LowPart <
4972 (blocksRequired * This->parentStorage->bigBlockSize))
4974 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4976 BlockChainStream_SetSize(
4977 This->parentStorage->smallBlockRootChain,
4980 StorageImpl_WriteProperty(
4981 This->parentStorage,
4982 This->parentStorage->rootPropertySetIndex,
4990 /******************************************************************************
4991 * SmallBlockChainStream_ReadAt
4993 * Reads a specified number of bytes from this chain at the specified offset.
4994 * bytesRead may be NULL.
4995 * Failure will be returned if the specified number of bytes has not been read.
4997 BOOL SmallBlockChainStream_ReadAt(
4998 SmallBlockChainStream* This,
4999 ULARGE_INTEGER offset,
5004 ULARGE_INTEGER offsetInBigBlockFile;
5005 ULONG blockNoInSequence =
5006 offset.s.LowPart / This->parentStorage->smallBlockSize;
5008 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5009 ULONG bytesToReadInBuffer;
5011 ULONG bytesReadFromBigBlockFile;
5015 * This should never happen on a small block file.
5017 assert(offset.s.HighPart==0);
5020 * Find the first block in the stream that contains part of the buffer.
5022 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5024 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5026 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5029 blockNoInSequence--;
5033 * Start reading the buffer.
5036 bufferWalker = buffer;
5038 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5041 * Calculate how many bytes we can copy from this small block.
5043 bytesToReadInBuffer =
5044 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5047 * Calculate the offset of the small block in the small block file.
5049 offsetInBigBlockFile.s.HighPart = 0;
5050 offsetInBigBlockFile.s.LowPart =
5051 blockIndex * This->parentStorage->smallBlockSize;
5053 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5056 * Read those bytes in the buffer from the small block file.
5058 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5059 offsetInBigBlockFile,
5060 bytesToReadInBuffer,
5062 &bytesReadFromBigBlockFile);
5064 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5067 * Step to the next big block.
5069 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5071 bufferWalker += bytesToReadInBuffer;
5072 size -= bytesToReadInBuffer;
5073 *bytesRead += bytesToReadInBuffer;
5074 offsetInBlock = 0; /* There is no offset on the next block */
5080 /******************************************************************************
5081 * SmallBlockChainStream_WriteAt
5083 * Writes the specified number of bytes to this chain at the specified offset.
5084 * bytesWritten may be NULL.
5085 * Will fail if not all specified number of bytes have been written.
5087 BOOL SmallBlockChainStream_WriteAt(
5088 SmallBlockChainStream* This,
5089 ULARGE_INTEGER offset,
5092 ULONG* bytesWritten)
5094 ULARGE_INTEGER offsetInBigBlockFile;
5095 ULONG blockNoInSequence =
5096 offset.s.LowPart / This->parentStorage->smallBlockSize;
5098 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5099 ULONG bytesToWriteInBuffer;
5101 ULONG bytesWrittenFromBigBlockFile;
5105 * This should never happen on a small block file.
5107 assert(offset.s.HighPart==0);
5110 * Find the first block in the stream that contains part of the buffer.
5112 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5114 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5116 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5118 blockNoInSequence--;
5122 * Start writing the buffer.
5124 * Here, I'm casting away the constness on the buffer variable
5125 * This is OK since we don't intend to modify that buffer.
5128 bufferWalker = (BYTE*)buffer;
5129 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5132 * Calculate how many bytes we can copy to this small block.
5134 bytesToWriteInBuffer =
5135 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5138 * Calculate the offset of the small block in the small block file.
5140 offsetInBigBlockFile.s.HighPart = 0;
5141 offsetInBigBlockFile.s.LowPart =
5142 blockIndex * This->parentStorage->smallBlockSize;
5144 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5147 * Write those bytes in the buffer to the small block file.
5149 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5150 offsetInBigBlockFile,
5151 bytesToWriteInBuffer,
5153 &bytesWrittenFromBigBlockFile);
5155 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5158 * Step to the next big block.
5160 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5163 bufferWalker += bytesToWriteInBuffer;
5164 size -= bytesToWriteInBuffer;
5165 *bytesWritten += bytesToWriteInBuffer;
5166 offsetInBlock = 0; /* There is no offset on the next block */
5172 /******************************************************************************
5173 * SmallBlockChainStream_Shrink
5175 * Shrinks this chain in the small block depot.
5177 BOOL SmallBlockChainStream_Shrink(
5178 SmallBlockChainStream* This,
5179 ULARGE_INTEGER newSize)
5181 ULONG blockIndex, extraBlock;
5185 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5187 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5190 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5193 * Go to the new end of chain
5195 while (count < numBlocks)
5197 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5204 * If the count is 0, we have a special case, the head of the chain was
5209 StgProperty chainProp;
5211 StorageImpl_ReadProperty(This->parentStorage,
5212 This->ownerPropertyIndex,
5215 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5217 StorageImpl_WriteProperty(This->parentStorage,
5218 This->ownerPropertyIndex,
5222 * We start freeing the chain at the head block.
5224 extraBlock = blockIndex;
5228 /* Get the next block before marking the new end */
5229 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5233 /* Mark the new end of chain */
5234 SmallBlockChainStream_SetNextBlockInChain(
5237 BLOCK_END_OF_CHAIN);
5241 * Mark the extra blocks as free
5243 while (extraBlock != BLOCK_END_OF_CHAIN)
5245 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5248 SmallBlockChainStream_FreeBlock(This, extraBlock);
5249 extraBlock = blockIndex;
5255 /******************************************************************************
5256 * SmallBlockChainStream_Enlarge
5258 * Grows this chain in the small block depot.
5260 BOOL SmallBlockChainStream_Enlarge(
5261 SmallBlockChainStream* This,
5262 ULARGE_INTEGER newSize)
5264 ULONG blockIndex, currentBlock;
5266 ULONG oldNumBlocks = 0;
5268 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5273 if (blockIndex == BLOCK_END_OF_CHAIN)
5276 StgProperty chainProp;
5278 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5281 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5283 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5286 blockIndex = chainProp.startingBlock;
5287 SmallBlockChainStream_SetNextBlockInChain(
5290 BLOCK_END_OF_CHAIN);
5293 currentBlock = blockIndex;
5296 * Figure out how many blocks are needed to contain this stream
5298 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5300 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5304 * Go to the current end of chain
5306 while (blockIndex != BLOCK_END_OF_CHAIN)
5309 currentBlock = blockIndex;
5310 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5315 * Add new blocks to the chain
5317 while (oldNumBlocks < newNumBlocks)
5319 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5320 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5322 SmallBlockChainStream_SetNextBlockInChain(
5325 BLOCK_END_OF_CHAIN);
5327 currentBlock = blockIndex;
5334 /******************************************************************************
5335 * SmallBlockChainStream_GetCount
5337 * Returns the number of blocks that comprises this chain.
5338 * This is not the size of this chain as the last block may not be full!
5340 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5345 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5347 while (blockIndex != BLOCK_END_OF_CHAIN)
5351 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5358 /******************************************************************************
5359 * SmallBlockChainStream_SetSize
5361 * Sets the size of this stream.
5362 * The file will grow if we grow the chain.
5364 * TODO: Free the actual blocks in the file when we shrink the chain.
5365 * Currently, the blocks are still in the file. So the file size
5366 * doesn't shrink even if we shrink streams.
5368 BOOL SmallBlockChainStream_SetSize(
5369 SmallBlockChainStream* This,
5370 ULARGE_INTEGER newSize)
5372 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5374 if (newSize.s.LowPart == size.s.LowPart)
5377 if (newSize.s.LowPart < size.s.LowPart)
5379 SmallBlockChainStream_Shrink(This, newSize);
5383 SmallBlockChainStream_Enlarge(This, newSize);
5389 /******************************************************************************
5390 * SmallBlockChainStream_GetSize
5392 * Returns the size of this chain.
5394 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5396 StgProperty chainProperty;
5398 StorageImpl_ReadProperty(
5399 This->parentStorage,
5400 This->ownerPropertyIndex,
5403 return chainProperty.size;
5406 /******************************************************************************
5407 * StgCreateDocfile [OLE32.144]
5409 HRESULT WINAPI StgCreateDocfile(
5413 IStorage **ppstgOpen)
5415 StorageImpl* newStorage = 0;
5416 HANDLE hFile = INVALID_HANDLE_VALUE;
5421 DWORD fileAttributes;
5422 WCHAR tempFileName[MAX_PATH];
5424 TRACE("(%s, %lx, %ld, %p)\n",
5425 debugstr_w(pwcsName), grfMode,
5426 reserved, ppstgOpen);
5429 * Validate the parameters
5432 return STG_E_INVALIDPOINTER;
5435 * Validate the STGM flags
5437 if ( FAILED( validateSTGM(grfMode) ))
5438 return STG_E_INVALIDFLAG;
5441 * Generate a unique name.
5445 WCHAR tempPath[MAX_PATH];
5446 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5448 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5449 return STG_E_INVALIDFLAG;
5450 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5451 return STG_E_INVALIDFLAG;
5453 memset(tempPath, 0, sizeof(tempPath));
5454 memset(tempFileName, 0, sizeof(tempFileName));
5456 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5459 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5460 pwcsName = tempFileName;
5462 return STG_E_INSUFFICIENTMEMORY;
5464 creationMode = TRUNCATE_EXISTING;
5468 creationMode = GetCreationModeFromSTGM(grfMode);
5472 * Interpret the STGM value grfMode
5474 shareMode = GetShareModeFromSTGM(grfMode);
5475 accessMode = GetAccessModeFromSTGM(grfMode);
5477 if (grfMode & STGM_DELETEONRELEASE)
5478 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5480 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5482 if (grfMode & STGM_TRANSACTED)
5483 FIXME("Transacted mode not implemented.\n");
5486 * Initialize the "out" parameter.
5490 hFile = CreateFileW(pwcsName,
5498 if (hFile == INVALID_HANDLE_VALUE)
5504 * Allocate and initialize the new IStorage32object.
5506 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5508 if (newStorage == 0)
5509 return STG_E_INSUFFICIENTMEMORY;
5511 hr = StorageImpl_Construct(
5522 HeapFree(GetProcessHeap(), 0, newStorage);
5527 * Get an "out" pointer for the caller.
5529 hr = StorageBaseImpl_QueryInterface(
5530 (IStorage*)newStorage,
5531 (REFIID)&IID_IStorage,
5537 /******************************************************************************
5538 * StgOpenStorage [OLE32.148]
5540 HRESULT WINAPI StgOpenStorage(
5541 const OLECHAR *pwcsName,
5542 IStorage *pstgPriority,
5546 IStorage **ppstgOpen)
5548 StorageImpl* newStorage = 0;
5553 WCHAR fullname[MAX_PATH];
5556 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5557 debugstr_w(pwcsName), pstgPriority, grfMode,
5558 snbExclude, reserved, ppstgOpen);
5561 * Perform a sanity check
5563 if (( pwcsName == 0) || (ppstgOpen == 0) )
5565 hr = STG_E_INVALIDPOINTER;
5570 * Validate the STGM flags
5572 if ( FAILED( validateSTGM(grfMode) ))
5574 hr = STG_E_INVALIDFLAG;
5579 * Interpret the STGM value grfMode
5581 shareMode = GetShareModeFromSTGM(grfMode);
5582 accessMode = GetAccessModeFromSTGM(grfMode);
5585 * Initialize the "out" parameter.
5589 hFile = CreateFileW( pwcsName,
5594 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5597 length = GetFileSize(hFile, NULL);
5599 if (hFile==INVALID_HANDLE_VALUE)
5601 DWORD last_error = GetLastError();
5607 case ERROR_FILE_NOT_FOUND:
5608 hr = STG_E_FILENOTFOUND;
5611 case ERROR_PATH_NOT_FOUND:
5612 hr = STG_E_PATHNOTFOUND;
5615 case ERROR_ACCESS_DENIED:
5616 case ERROR_WRITE_PROTECT:
5617 hr = STG_E_ACCESSDENIED;
5620 case ERROR_SHARING_VIOLATION:
5621 hr = STG_E_SHAREVIOLATION;
5632 * Allocate and initialize the new IStorage32object.
5634 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5636 if (newStorage == 0)
5638 hr = STG_E_INSUFFICIENTMEMORY;
5642 /* if the file's length was zero, initialize the storage */
5643 hr = StorageImpl_Construct(
5654 HeapFree(GetProcessHeap(), 0, newStorage);
5656 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5658 if(hr == STG_E_INVALIDHEADER)
5659 hr = STG_E_FILEALREADYEXISTS;
5663 /* prepare the file name string given in lieu of the root property name */
5664 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5665 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5666 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5669 * Get an "out" pointer for the caller.
5671 hr = StorageBaseImpl_QueryInterface(
5672 (IStorage*)newStorage,
5673 (REFIID)&IID_IStorage,
5677 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5681 /******************************************************************************
5682 * StgCreateDocfileOnILockBytes [OLE32.145]
5684 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5688 IStorage** ppstgOpen)
5690 StorageImpl* newStorage = 0;
5694 * Validate the parameters
5696 if ((ppstgOpen == 0) || (plkbyt == 0))
5697 return STG_E_INVALIDPOINTER;
5700 * Allocate and initialize the new IStorage object.
5702 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5704 if (newStorage == 0)
5705 return STG_E_INSUFFICIENTMEMORY;
5707 hr = StorageImpl_Construct(
5718 HeapFree(GetProcessHeap(), 0, newStorage);
5723 * Get an "out" pointer for the caller.
5725 hr = StorageBaseImpl_QueryInterface(
5726 (IStorage*)newStorage,
5727 (REFIID)&IID_IStorage,
5733 /******************************************************************************
5734 * StgOpenStorageOnILockBytes [OLE32.149]
5736 HRESULT WINAPI StgOpenStorageOnILockBytes(
5738 IStorage *pstgPriority,
5742 IStorage **ppstgOpen)
5744 StorageImpl* newStorage = 0;
5748 * Perform a sanity check
5750 if ((plkbyt == 0) || (ppstgOpen == 0))
5751 return STG_E_INVALIDPOINTER;
5754 * Validate the STGM flags
5756 if ( FAILED( validateSTGM(grfMode) ))
5757 return STG_E_INVALIDFLAG;
5760 * Initialize the "out" parameter.
5765 * Allocate and initialize the new IStorage object.
5767 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5769 if (newStorage == 0)
5770 return STG_E_INSUFFICIENTMEMORY;
5772 hr = StorageImpl_Construct(
5783 HeapFree(GetProcessHeap(), 0, newStorage);
5788 * Get an "out" pointer for the caller.
5790 hr = StorageBaseImpl_QueryInterface(
5791 (IStorage*)newStorage,
5792 (REFIID)&IID_IStorage,
5798 /******************************************************************************
5799 * StgSetTimes [ole32.150]
5800 * StgSetTimes [OLE32.150]
5804 HRESULT WINAPI StgSetTimes(OLECHAR *str, FILETIME *a, FILETIME *b, FILETIME *c )
5806 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5810 /******************************************************************************
5811 * StgIsStorageILockBytes [OLE32.147]
5813 * Determines if the ILockBytes contains a storage object.
5815 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5818 ULARGE_INTEGER offset;
5820 offset.s.HighPart = 0;
5821 offset.s.LowPart = 0;
5823 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5825 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5831 /******************************************************************************
5832 * WriteClassStg [OLE32.158]
5834 * This method will store the specified CLSID in the specified storage object
5836 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5842 hRes = IStorage_SetClass(pStg, rclsid);
5847 /***********************************************************************
5848 * ReadClassStg (OLE32.134)
5850 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5852 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5862 * read a STATSTG structure (contains the clsid) from the storage
5864 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5867 *pclsid=pstatstg.clsid;
5872 /***********************************************************************
5873 * OleLoadFromStream (OLE32.113)
5875 * This function loads an object from stream
5877 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5881 LPPERSISTSTREAM xstm;
5883 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5885 res=ReadClassStm(pStm,&clsid);
5886 if (!SUCCEEDED(res))
5888 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5889 if (!SUCCEEDED(res))
5891 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5892 if (!SUCCEEDED(res)) {
5893 IUnknown_Release((IUnknown*)*ppvObj);
5896 res=IPersistStream_Load(xstm,pStm);
5897 IPersistStream_Release(xstm);
5898 /* FIXME: all refcounts ok at this point? I think they should be:
5901 * xstm : 0 (released)
5906 /***********************************************************************
5907 * OleSaveToStream (OLE32.125)
5909 * This function saves an object with the IPersistStream interface on it
5910 * to the specified stream.
5912 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5918 TRACE("(%p,%p)\n",pPStm,pStm);
5920 res=IPersistStream_GetClassID(pPStm,&clsid);
5922 if (SUCCEEDED(res)){
5924 res=WriteClassStm(pStm,&clsid);
5928 res=IPersistStream_Save(pPStm,pStm,TRUE);
5931 TRACE("Finished Save\n");
5935 /****************************************************************************
5936 * This method validate a STGM parameter that can contain the values below
5938 * STGM_DIRECT 0x00000000
5939 * STGM_TRANSACTED 0x00010000
5940 * STGM_SIMPLE 0x08000000
5942 * STGM_READ 0x00000000
5943 * STGM_WRITE 0x00000001
5944 * STGM_READWRITE 0x00000002
5946 * STGM_SHARE_DENY_NONE 0x00000040
5947 * STGM_SHARE_DENY_READ 0x00000030
5948 * STGM_SHARE_DENY_WRITE 0x00000020
5949 * STGM_SHARE_EXCLUSIVE 0x00000010
5951 * STGM_PRIORITY 0x00040000
5952 * STGM_DELETEONRELEASE 0x04000000
5954 * STGM_CREATE 0x00001000
5955 * STGM_CONVERT 0x00020000
5956 * STGM_FAILIFTHERE 0x00000000
5958 * STGM_NOSCRATCH 0x00100000
5959 * STGM_NOSNAPSHOT 0x00200000
5961 static HRESULT validateSTGM(DWORD stgm)
5963 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5964 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5965 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5967 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5968 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5969 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5971 BOOL bSTGM_SHARE_DENY_NONE =
5972 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5974 BOOL bSTGM_SHARE_DENY_READ =
5975 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5977 BOOL bSTGM_SHARE_DENY_WRITE =
5978 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5980 BOOL bSTGM_SHARE_EXCLUSIVE =
5981 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5983 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5984 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5986 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5987 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5990 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5992 if ( ! bSTGM_DIRECT )
5993 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5997 * STGM_WRITE | STGM_READWRITE | STGM_READ
6000 if( bSTGM_WRITE && bSTGM_READWRITE )
6004 * STGM_SHARE_DENY_NONE | others
6005 * (I assume here that DENY_READ implies DENY_WRITE)
6007 if ( bSTGM_SHARE_DENY_NONE )
6008 if ( bSTGM_SHARE_DENY_READ ||
6009 bSTGM_SHARE_DENY_WRITE ||
6010 bSTGM_SHARE_EXCLUSIVE)
6014 * STGM_CREATE | STGM_CONVERT
6015 * if both are false, STGM_FAILIFTHERE is set to TRUE
6017 if ( bSTGM_CREATE && bSTGM_CONVERT )
6021 * STGM_NOSCRATCH requires STGM_TRANSACTED
6023 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6027 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6028 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6030 if (bSTGM_NOSNAPSHOT)
6032 if ( ! ( bSTGM_TRANSACTED &&
6033 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6040 /****************************************************************************
6041 * GetShareModeFromSTGM
6043 * This method will return a share mode flag from a STGM value.
6044 * The STGM value is assumed valid.
6046 static DWORD GetShareModeFromSTGM(DWORD stgm)
6048 DWORD dwShareMode = 0;
6049 BOOL bSTGM_SHARE_DENY_NONE =
6050 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6052 BOOL bSTGM_SHARE_DENY_READ =
6053 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6055 BOOL bSTGM_SHARE_DENY_WRITE =
6056 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6058 BOOL bSTGM_SHARE_EXCLUSIVE =
6059 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6061 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6064 if (bSTGM_SHARE_DENY_NONE)
6065 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6067 if (bSTGM_SHARE_DENY_WRITE)
6068 dwShareMode = FILE_SHARE_READ;
6073 /****************************************************************************
6074 * GetAccessModeFromSTGM
6076 * This method will return an access mode flag from a STGM value.
6077 * The STGM value is assumed valid.
6079 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6081 DWORD dwDesiredAccess = GENERIC_READ;
6082 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6083 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6084 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6087 dwDesiredAccess = GENERIC_READ;
6090 dwDesiredAccess |= GENERIC_WRITE;
6092 if (bSTGM_READWRITE)
6093 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6095 return dwDesiredAccess;
6098 /****************************************************************************
6099 * GetCreationModeFromSTGM
6101 * This method will return a creation mode flag from a STGM value.
6102 * The STGM value is assumed valid.
6104 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6106 if ( stgm & STGM_CREATE)
6107 return CREATE_ALWAYS;
6108 if (stgm & STGM_CONVERT) {
6109 FIXME("STGM_CONVERT not implemented!\n");
6112 /* All other cases */
6113 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6114 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6119 /*************************************************************************
6120 * OLECONVERT_LoadOLE10 [Internal]
6122 * Loads the OLE10 STREAM to memory
6125 * pOleStream [I] The OLESTREAM
6126 * pData [I] Data Structure for the OLESTREAM Data
6130 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6131 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6134 * This function is used by OleConvertOLESTREAMToIStorage only.
6136 * Memory allocated for pData must be freed by the caller
6138 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6141 HRESULT hRes = S_OK;
6145 pData->pData = NULL;
6146 pData->pstrOleObjFileName = (CHAR *) NULL;
6148 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6151 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6152 if(dwSize != sizeof(pData->dwOleID))
6154 hRes = CONVERT10_E_OLESTREAM_GET;
6156 else if(pData->dwOleID != OLESTREAM_ID)
6158 hRes = CONVERT10_E_OLESTREAM_FMT;
6169 /* Get the TypeID...more info needed for this field */
6170 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6171 if(dwSize != sizeof(pData->dwTypeID))
6173 hRes = CONVERT10_E_OLESTREAM_GET;
6178 if(pData->dwTypeID != 0)
6180 /* Get the lenght of the OleTypeName */
6181 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6182 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6184 hRes = CONVERT10_E_OLESTREAM_GET;
6189 if(pData->dwOleTypeNameLength > 0)
6191 /* Get the OleTypeName */
6192 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6193 if(dwSize != pData->dwOleTypeNameLength)
6195 hRes = CONVERT10_E_OLESTREAM_GET;
6201 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6202 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6204 hRes = CONVERT10_E_OLESTREAM_GET;
6208 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6209 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6210 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6211 if(pData->pstrOleObjFileName)
6213 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6214 if(dwSize != pData->dwOleObjFileNameLength)
6216 hRes = CONVERT10_E_OLESTREAM_GET;
6220 hRes = CONVERT10_E_OLESTREAM_GET;
6225 /* Get the Width of the Metafile */
6226 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6227 if(dwSize != sizeof(pData->dwMetaFileWidth))
6229 hRes = CONVERT10_E_OLESTREAM_GET;
6233 /* Get the Height of the Metafile */
6234 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6235 if(dwSize != sizeof(pData->dwMetaFileHeight))
6237 hRes = CONVERT10_E_OLESTREAM_GET;
6243 /* Get the Lenght of the Data */
6244 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6245 if(dwSize != sizeof(pData->dwDataLength))
6247 hRes = CONVERT10_E_OLESTREAM_GET;
6251 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6253 if(!bStrem1) /* if it is a second OLE stream data */
6255 pData->dwDataLength -= 8;
6256 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6257 if(dwSize != sizeof(pData->strUnknown))
6259 hRes = CONVERT10_E_OLESTREAM_GET;
6265 if(pData->dwDataLength > 0)
6267 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6269 /* Get Data (ex. IStorage, Metafile, or BMP) */
6272 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6273 if(dwSize != pData->dwDataLength)
6275 hRes = CONVERT10_E_OLESTREAM_GET;
6280 hRes = CONVERT10_E_OLESTREAM_GET;
6289 /*************************************************************************
6290 * OLECONVERT_SaveOLE10 [Internal]
6292 * Saves the OLE10 STREAM From memory
6295 * pData [I] Data Structure for the OLESTREAM Data
6296 * pOleStream [I] The OLESTREAM to save
6300 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6303 * This function is used by OleConvertIStorageToOLESTREAM only.
6306 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6309 HRESULT hRes = S_OK;
6313 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6314 if(dwSize != sizeof(pData->dwOleID))
6316 hRes = CONVERT10_E_OLESTREAM_PUT;
6321 /* Set the TypeID */
6322 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6323 if(dwSize != sizeof(pData->dwTypeID))
6325 hRes = CONVERT10_E_OLESTREAM_PUT;
6329 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6331 /* Set the Lenght of the OleTypeName */
6332 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6333 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6335 hRes = CONVERT10_E_OLESTREAM_PUT;
6340 if(pData->dwOleTypeNameLength > 0)
6342 /* Set the OleTypeName */
6343 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6344 if(dwSize != pData->dwOleTypeNameLength)
6346 hRes = CONVERT10_E_OLESTREAM_PUT;
6353 /* Set the width of the Metafile */
6354 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6355 if(dwSize != sizeof(pData->dwMetaFileWidth))
6357 hRes = CONVERT10_E_OLESTREAM_PUT;
6363 /* Set the height of the Metafile */
6364 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6365 if(dwSize != sizeof(pData->dwMetaFileHeight))
6367 hRes = CONVERT10_E_OLESTREAM_PUT;
6373 /* Set the lenght of the Data */
6374 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6375 if(dwSize != sizeof(pData->dwDataLength))
6377 hRes = CONVERT10_E_OLESTREAM_PUT;
6383 if(pData->dwDataLength > 0)
6385 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6386 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6387 if(dwSize != pData->dwDataLength)
6389 hRes = CONVERT10_E_OLESTREAM_PUT;
6397 /*************************************************************************
6398 * OLECONVERT_GetOLE20FromOLE10[Internal]
6400 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6401 * opens it, and copies the content to the dest IStorage for
6402 * OleConvertOLESTREAMToIStorage
6406 * pDestStorage [I] The IStorage to copy the data to
6407 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6408 * nBufferLength [I] The size of the buffer
6417 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6421 IStorage *pTempStorage;
6422 DWORD dwNumOfBytesWritten;
6423 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6424 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6426 /* Create a temp File */
6427 GetTempPathW(MAX_PATH, wstrTempDir);
6428 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6429 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6431 if(hFile != INVALID_HANDLE_VALUE)
6433 /* Write IStorage Data to File */
6434 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6437 /* Open and copy temp storage to the Dest Storage */
6438 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6441 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6442 StorageBaseImpl_Release(pTempStorage);
6444 DeleteFileW(wstrTempFile);
6449 /*************************************************************************
6450 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6452 * Saves the OLE10 STREAM From memory
6455 * pStorage [I] The Src IStorage to copy
6456 * pData [I] The Dest Memory to write to.
6459 * The size in bytes allocated for pData
6462 * Memory allocated for pData must be freed by the caller
6464 * Used by OleConvertIStorageToOLESTREAM only.
6467 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6471 DWORD nDataLength = 0;
6472 IStorage *pTempStorage;
6473 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6474 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6478 /* Create temp Storage */
6479 GetTempPathW(MAX_PATH, wstrTempDir);
6480 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6481 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6485 /* Copy Src Storage to the Temp Storage */
6486 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6487 StorageBaseImpl_Release(pTempStorage);
6489 /* Open Temp Storage as a file and copy to memory */
6490 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6491 if(hFile != INVALID_HANDLE_VALUE)
6493 nDataLength = GetFileSize(hFile, NULL);
6494 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6495 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6498 DeleteFileW(wstrTempFile);
6503 /*************************************************************************
6504 * OLECONVERT_CreateOleStream [Internal]
6506 * Creates the "\001OLE" stream in the IStorage if necessary.
6509 * pStorage [I] Dest storage to create the stream in
6515 * This function is used by OleConvertOLESTREAMToIStorage only.
6517 * This stream is still unknown, MS Word seems to have extra data
6518 * but since the data is stored in the OLESTREAM there should be
6519 * no need to recreate the stream. If the stream is manually
6520 * deleted it will create it with this default data.
6523 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6527 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6528 BYTE pOleStreamHeader [] =
6530 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6531 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6532 0x00, 0x00, 0x00, 0x00
6535 /* Create stream if not present */
6536 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6537 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6541 /* Write default Data */
6542 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6543 IStream_Release(pStream);
6547 /* write a string to a stream, preceded by its length */
6548 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6555 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6556 r = IStream_Write( stm, &len, sizeof len, NULL);
6561 str = CoTaskMemAlloc( len );
6562 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6563 r = IStream_Write( stm, str, len, NULL);
6564 CoTaskMemFree( str );
6568 /* read a string preceded by its length from a stream */
6569 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6572 DWORD len, count = 0;
6576 r = IStream_Read( stm, &len, sizeof len, &count );
6579 if( count != sizeof len )
6580 return E_OUTOFMEMORY;
6582 TRACE("%ld bytes\n",len);
6584 str = CoTaskMemAlloc( len );
6586 return E_OUTOFMEMORY;
6588 r = IStream_Read( stm, str, len, &count );
6593 CoTaskMemFree( str );
6594 return E_OUTOFMEMORY;
6597 TRACE("Read string %s\n",debugstr_an(str,len));
6599 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6600 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6602 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6603 CoTaskMemFree( str );
6611 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6612 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6616 WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6618 static const BYTE unknown1[12] =
6619 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6620 0xFF, 0xFF, 0xFF, 0xFF};
6621 static const BYTE unknown2[16] =
6622 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6623 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6625 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6626 debugstr_w(lpszUserType), debugstr_w(szClipName),
6627 debugstr_w(szProgIDName));
6629 /* Create a CompObj stream if it doesn't exist */
6630 r = IStorage_CreateStream(pstg, szwStreamName,
6631 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6635 /* Write CompObj Structure to stream */
6636 r = IStream_Write(pstm, unknown1, sizeof unknown1, NULL);
6638 if( SUCCEEDED( r ) )
6639 r = WriteClassStm( pstm, clsid );
6641 if( SUCCEEDED( r ) )
6642 r = STREAM_WriteString( pstm, lpszUserType );
6643 if( SUCCEEDED( r ) )
6644 r = STREAM_WriteString( pstm, szClipName );
6645 if( SUCCEEDED( r ) )
6646 r = STREAM_WriteString( pstm, szProgIDName );
6647 if( SUCCEEDED( r ) )
6648 r = IStream_Write(pstm, unknown2, sizeof unknown2, NULL);
6650 IStream_Release( pstm );
6655 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6656 static HRESULT CLSIDFromUserType(LPCWSTR lpszUserType, CLSID *clsid)
6658 LONG r, count, i, len;
6660 HKEY hkey, hkeyclsid;
6661 LPWSTR buffer = NULL;
6663 const WCHAR szclsid[] = { 'C','L','S','I','D',0 };
6665 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType));
6667 r = RegOpenKeyW( HKEY_CLASSES_ROOT, szclsid, &hkeyclsid );
6669 return E_INVALIDARG;
6671 len = lstrlenW( lpszUserType ) + 1;
6672 buffer = CoTaskMemAlloc( len * sizeof (WCHAR) );
6676 for(i=0; !found; i++ )
6678 r = RegEnumKeyW( hkeyclsid, i, szKey, sizeof szKey/sizeof(WCHAR));
6679 if( r != ERROR_SUCCESS )
6682 r = RegOpenKeyW( hkeyclsid, szKey, &hkey );
6683 if( r != ERROR_SUCCESS )
6685 count = len * sizeof (WCHAR);
6686 r = RegQueryValueW( hkey, NULL, buffer, &count );
6687 found = ( r == ERROR_SUCCESS ) &&
6688 ( count == len*sizeof(WCHAR) ) &&
6689 !lstrcmpW( buffer, lpszUserType ) ;
6690 RegCloseKey( hkey );
6695 CoTaskMemFree( buffer );
6696 RegCloseKey( hkeyclsid );
6699 return E_INVALIDARG;
6701 TRACE("clsid is %s\n", debugstr_w( szKey ) );
6703 r = CLSIDFromString( szKey, clsid );
6709 /***********************************************************************
6710 * WriteFmtUserTypeStg (OLE32.160)
6712 HRESULT WINAPI WriteFmtUserTypeStg(
6713 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6716 WCHAR szwClipName[0x40];
6717 WCHAR szCLSIDName[OLESTREAM_MAX_STR_LEN];
6721 LPMALLOC allocator = NULL;
6723 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6725 r = CoGetMalloc(0, &allocator);
6727 return E_OUTOFMEMORY;
6729 /* get the clipboard format name */
6730 n = GetClipboardFormatNameW( cf, szwClipName, sizeof szwClipName );
6733 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6737 r = CLSIDFromUserType(lpszUserType, &clsid);
6741 TRACE("CLSID is %s\n",debugstr_guid(&clsid));
6743 /* get the real program ID */
6744 r = ProgIDFromCLSID( &clsid, &wstrProgID);
6748 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6750 /* if we have a good string, write the stream */
6752 r = STORAGE_WriteCompObj( pstg, &clsid,
6753 lpszUserType, szwClipName, wstrProgID );
6757 IMalloc_Free( allocator, wstrProgID);
6763 /******************************************************************************
6764 * ReadFmtUserTypeStg [OLE32.136]
6766 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6770 const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6771 unsigned char unknown1[12];
6772 unsigned char unknown2[16];
6774 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6777 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6779 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6780 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6783 ERR("Failed to open stream\n");
6787 /* read the various parts of the structure */
6788 r = IStream_Read( stm, unknown1, sizeof unknown1, &count );
6789 if( FAILED( r ) || ( count != sizeof unknown1 ) )
6791 r = ReadClassStm( stm, &clsid );
6795 r = STREAM_ReadString( stm, &szCLSIDName );
6799 r = STREAM_ReadString( stm, &szOleTypeName );
6803 r = STREAM_ReadString( stm, &szProgIDName );
6807 r = IStream_Read( stm, unknown2, sizeof unknown2, &count );
6808 if( FAILED( r ) || ( count != sizeof unknown2 ) )
6811 /* ok, success... now we just need to store what we found */
6813 *pcf = RegisterClipboardFormatW( szOleTypeName );
6814 CoTaskMemFree( szOleTypeName );
6816 if( lplpszUserType )
6817 *lplpszUserType = szCLSIDName;
6818 CoTaskMemFree( szProgIDName );
6821 IStream_Release( stm );
6827 /*************************************************************************
6828 * OLECONVERT_CreateCompObjStream [Internal]
6830 * Creates a "\001CompObj" is the destination IStorage if necessary.
6833 * pStorage [I] The dest IStorage to create the CompObj Stream
6835 * strOleTypeName [I] The ProgID
6839 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6842 * This function is used by OleConvertOLESTREAMToIStorage only.
6844 * The stream data is stored in the OLESTREAM and there should be
6845 * no need to recreate the stream. If the stream is manually
6846 * deleted it will attempt to create it by querying the registry.
6850 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6853 HRESULT hStorageRes, hRes = S_OK;
6854 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6855 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6857 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6858 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6860 /* Initialize the CompObj structure */
6861 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6862 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6863 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6866 /* Create a CompObj stream if it doesn't exist */
6867 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6868 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6869 if(hStorageRes == S_OK)
6871 /* copy the OleTypeName to the compobj struct */
6872 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6873 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6875 /* copy the OleTypeName to the compobj struct */
6876 /* Note: in the test made, these were Identical */
6877 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6878 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6881 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6887 /* Get the CLSID Default Name from the Registry */
6888 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6889 if(hErr == ERROR_SUCCESS)
6891 char strTemp[OLESTREAM_MAX_STR_LEN];
6892 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6893 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6894 if(hErr == ERROR_SUCCESS)
6896 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6902 /* Write CompObj Structure to stream */
6903 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6905 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6907 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6908 if(IStorageCompObj.dwCLSIDNameLength > 0)
6910 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6912 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6913 if(IStorageCompObj.dwOleTypeNameLength > 0)
6915 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6917 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6918 if(IStorageCompObj.dwProgIDNameLength > 0)
6920 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6922 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6923 IStream_Release(pStream);
6929 /*************************************************************************
6930 * OLECONVERT_CreateOlePresStream[Internal]
6932 * Creates the "\002OlePres000" Stream with the Metafile data
6935 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6936 * dwExtentX [I] Width of the Metafile
6937 * dwExtentY [I] Height of the Metafile
6938 * pData [I] Metafile data
6939 * dwDataLength [I] Size of the Metafile data
6943 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6946 * This function is used by OleConvertOLESTREAMToIStorage only.
6949 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6953 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6954 BYTE pOlePresStreamHeader [] =
6956 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6957 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6958 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6959 0x00, 0x00, 0x00, 0x00
6962 BYTE pOlePresStreamHeaderEmpty [] =
6964 0x00, 0x00, 0x00, 0x00,
6965 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6966 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6967 0x00, 0x00, 0x00, 0x00
6970 /* Create the OlePres000 Stream */
6971 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6972 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6977 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6979 memset(&OlePres, 0, sizeof(OlePres));
6980 /* Do we have any metafile data to save */
6981 if(dwDataLength > 0)
6983 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6984 nHeaderSize = sizeof(pOlePresStreamHeader);
6988 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6989 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6991 /* Set width and height of the metafile */
6992 OlePres.dwExtentX = dwExtentX;
6993 OlePres.dwExtentY = -dwExtentY;
6995 /* Set Data and Lenght */
6996 if(dwDataLength > sizeof(METAFILEPICT16))
6998 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6999 OlePres.pData = &(pData[8]);
7001 /* Save OlePres000 Data to Stream */
7002 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7003 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7004 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7005 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7006 if(OlePres.dwSize > 0)
7008 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7010 IStream_Release(pStream);
7014 /*************************************************************************
7015 * OLECONVERT_CreateOle10NativeStream [Internal]
7017 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7020 * pStorage [I] Dest storage to create the stream in
7021 * pData [I] Ole10 Native Data (ex. bmp)
7022 * dwDataLength [I] Size of the Ole10 Native Data
7028 * This function is used by OleConvertOLESTREAMToIStorage only.
7030 * Might need to verify the data and return appropriate error message
7033 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7037 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7039 /* Create the Ole10Native Stream */
7040 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7041 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7045 /* Write info to stream */
7046 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7047 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7048 IStream_Release(pStream);
7053 /*************************************************************************
7054 * OLECONVERT_GetOLE10ProgID [Internal]
7056 * Finds the ProgID (or OleTypeID) from the IStorage
7059 * pStorage [I] The Src IStorage to get the ProgID
7060 * strProgID [I] the ProgID string to get
7061 * dwSize [I] the size of the string
7065 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7068 * This function is used by OleConvertIStorageToOLESTREAM only.
7072 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7076 LARGE_INTEGER iSeekPos;
7077 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7078 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7080 /* Open the CompObj Stream */
7081 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7082 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7086 /*Get the OleType from the CompObj Stream */
7087 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7088 iSeekPos.s.HighPart = 0;
7090 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7091 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7092 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
7093 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7094 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7095 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
7096 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7098 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7101 IStream_Read(pStream, strProgID, *dwSize, NULL);
7103 IStream_Release(pStream);
7108 LPOLESTR wstrProgID;
7110 /* Get the OleType from the registry */
7111 REFCLSID clsid = &(stat.clsid);
7112 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7113 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7116 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7123 /*************************************************************************
7124 * OLECONVERT_GetOle10PresData [Internal]
7126 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7129 * pStorage [I] Src IStroage
7130 * pOleStream [I] Dest OleStream Mem Struct
7136 * This function is used by OleConvertIStorageToOLESTREAM only.
7138 * Memory allocated for pData must be freed by the caller
7142 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7147 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7149 /* Initialize Default data for OLESTREAM */
7150 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7151 pOleStreamData[0].dwTypeID = 2;
7152 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7153 pOleStreamData[1].dwTypeID = 0;
7154 pOleStreamData[0].dwMetaFileWidth = 0;
7155 pOleStreamData[0].dwMetaFileHeight = 0;
7156 pOleStreamData[0].pData = NULL;
7157 pOleStreamData[1].pData = NULL;
7159 /* Open Ole10Native Stream */
7160 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7161 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7165 /* Read Size and Data */
7166 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7167 if(pOleStreamData->dwDataLength > 0)
7169 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7170 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7172 IStream_Release(pStream);
7178 /*************************************************************************
7179 * OLECONVERT_GetOle20PresData[Internal]
7181 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7184 * pStorage [I] Src IStroage
7185 * pOleStreamData [I] Dest OleStream Mem Struct
7191 * This function is used by OleConvertIStorageToOLESTREAM only.
7193 * Memory allocated for pData must be freed by the caller
7195 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7199 OLECONVERT_ISTORAGE_OLEPRES olePress;
7200 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7202 /* Initialize Default data for OLESTREAM */
7203 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7204 pOleStreamData[0].dwTypeID = 2;
7205 pOleStreamData[0].dwMetaFileWidth = 0;
7206 pOleStreamData[0].dwMetaFileHeight = 0;
7207 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7208 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7209 pOleStreamData[1].dwTypeID = 0;
7210 pOleStreamData[1].dwOleTypeNameLength = 0;
7211 pOleStreamData[1].strOleTypeName[0] = 0;
7212 pOleStreamData[1].dwMetaFileWidth = 0;
7213 pOleStreamData[1].dwMetaFileHeight = 0;
7214 pOleStreamData[1].pData = NULL;
7215 pOleStreamData[1].dwDataLength = 0;
7218 /* Open OlePress000 stream */
7219 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7220 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7223 LARGE_INTEGER iSeekPos;
7224 METAFILEPICT16 MetaFilePict;
7225 char strMetafilePictName[] = "METAFILEPICT";
7227 /* Set the TypeID for a Metafile */
7228 pOleStreamData[1].dwTypeID = 5;
7230 /* Set the OleTypeName to Metafile */
7231 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7232 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7234 iSeekPos.s.HighPart = 0;
7235 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
7237 /* Get Presentation Data */
7238 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7239 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7240 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7241 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7243 /*Set width and Height */
7244 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7245 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7246 if(olePress.dwSize > 0)
7249 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7251 /* Set MetaFilePict struct */
7252 MetaFilePict.mm = 8;
7253 MetaFilePict.xExt = olePress.dwExtentX;
7254 MetaFilePict.yExt = olePress.dwExtentY;
7255 MetaFilePict.hMF = 0;
7257 /* Get Metafile Data */
7258 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7259 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7260 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7262 IStream_Release(pStream);
7266 /*************************************************************************
7267 * OleConvertOLESTREAMToIStorage [OLE32.87]
7272 * DVTARGETDEVICE paramenter is not handled
7273 * Still unsure of some mem fields for OLE 10 Stream
7274 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7275 * and "\001OLE" streams
7278 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7279 LPOLESTREAM pOleStream,
7281 const DVTARGETDEVICE* ptd)
7285 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7287 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7291 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7294 if(pstg == NULL || pOleStream == NULL)
7296 hRes = E_INVALIDARG;
7301 /* Load the OLESTREAM to Memory */
7302 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7307 /* Load the OLESTREAM to Memory (part 2)*/
7308 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7314 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7316 /* Do we have the IStorage Data in the OLESTREAM */
7317 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7319 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7320 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7324 /* It must be an original OLE 1.0 source */
7325 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7330 /* It must be an original OLE 1.0 source */
7331 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7334 /* Create CompObj Stream if necessary */
7335 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7338 /*Create the Ole Stream if necessary */
7339 OLECONVERT_CreateOleStream(pstg);
7344 /* Free allocated memory */
7345 for(i=0; i < 2; i++)
7347 if(pOleStreamData[i].pData != NULL)
7349 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7351 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7353 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7354 pOleStreamData[i].pstrOleObjFileName = NULL;
7360 /*************************************************************************
7361 * OleConvertIStorageToOLESTREAM [OLE32.85]
7368 * Still unsure of some mem fields for OLE 10 Stream
7369 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7370 * and "\001OLE" streams.
7373 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7375 LPOLESTREAM pOleStream)
7378 HRESULT hRes = S_OK;
7380 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7381 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7384 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7386 if(pstg == NULL || pOleStream == NULL)
7388 hRes = E_INVALIDARG;
7392 /* Get the ProgID */
7393 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7394 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7398 /* Was it originally Ole10 */
7399 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7402 IStream_Release(pStream);
7403 /* Get Presentation Data for Ole10Native */
7404 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7408 /* Get Presentation Data (OLE20) */
7409 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7412 /* Save OLESTREAM */
7413 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7416 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7421 /* Free allocated memory */
7422 for(i=0; i < 2; i++)
7424 if(pOleStreamData[i].pData != NULL)
7426 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7433 /***********************************************************************
7434 * GetConvertStg (OLE32.68)
7436 HRESULT WINAPI GetConvertStg(LPGUID guid) {
7437 FIXME("(%s), unimplemented stub!\n",debugstr_guid(guid));
7441 /******************************************************************************
7442 * StgIsStorageFile [OLE32.146]
7445 StgIsStorageFile(LPCOLESTR fn)
7451 TRACE("(\'%s\')\n", debugstr_w(fn));
7452 hf = CreateFileW(fn, GENERIC_READ,
7453 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7454 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7456 if (hf == INVALID_HANDLE_VALUE)
7457 return STG_E_FILENOTFOUND;
7459 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7461 WARN(" unable to read file\n");
7468 if (bytes_read != 8) {
7469 WARN(" too short\n");
7473 if (!memcmp(magic,STORAGE_magic,8)) {
7478 WARN(" -> Invalid header.\n");