2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
44 #include "storage32.h"
45 #include "ole2.h" /* For Write/ReadClassStm */
48 #include "wine/wingdi16.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(storage);
54 #define STGM_SHARE_MODE(stgm) ((stgm)&0xf0)
56 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
57 #define OLESTREAM_ID 0x501
58 #define OLESTREAM_MAX_STR_LEN 255
60 static const char rootPropertyName[] = "Root Entry";
63 /* OLESTREAM memory structure to use for Get and Put Routines */
64 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
69 DWORD dwOleTypeNameLength;
70 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
71 CHAR *pstrOleObjFileName;
72 DWORD dwOleObjFileNameLength;
73 DWORD dwMetaFileWidth;
74 DWORD dwMetaFileHeight;
75 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
78 }OLECONVERT_OLESTREAM_DATA;
80 /* CompObj Stream structure */
81 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
86 DWORD dwCLSIDNameLength;
87 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
88 DWORD dwOleTypeNameLength;
89 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
90 DWORD dwProgIDNameLength;
91 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
93 }OLECONVERT_ISTORAGE_COMPOBJ;
96 /* Ole Presention Stream structure */
97 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
105 }OLECONVERT_ISTORAGE_OLEPRES;
109 /***********************************************************************
110 * Forward declaration of internal functions used by the method DestroyElement
112 static HRESULT deleteStorageProperty(
113 StorageImpl *parentStorage,
114 ULONG foundPropertyIndexToDelete,
115 StgProperty propertyToDelete);
117 static HRESULT deleteStreamProperty(
118 StorageImpl *parentStorage,
119 ULONG foundPropertyIndexToDelete,
120 StgProperty propertyToDelete);
122 static HRESULT findPlaceholder(
123 StorageImpl *storage,
124 ULONG propertyIndexToStore,
125 ULONG storagePropertyIndex,
128 static HRESULT adjustPropertyChain(
130 StgProperty propertyToDelete,
131 StgProperty parentProperty,
132 ULONG parentPropertyId,
135 /***********************************************************************
136 * Declaration of the functions used to manipulate StgProperty
139 static ULONG getFreeProperty(
140 StorageImpl *storage);
142 static void updatePropertyChain(
143 StorageImpl *storage,
144 ULONG newPropertyIndex,
145 StgProperty newProperty);
147 static LONG propertyNameCmp(
148 const OLECHAR *newProperty,
149 const OLECHAR *currentProperty);
152 /***********************************************************************
153 * Declaration of miscellaneous functions...
155 static HRESULT validateSTGM(DWORD stgmValue);
157 static DWORD GetShareModeFromSTGM(DWORD stgm);
158 static DWORD GetAccessModeFromSTGM(DWORD stgm);
159 static DWORD GetCreationModeFromSTGM(DWORD stgm);
162 * Virtual function table for the IStorage32Impl class.
164 static IStorageVtbl Storage32Impl_Vtbl =
166 StorageBaseImpl_QueryInterface,
167 StorageBaseImpl_AddRef,
168 StorageBaseImpl_Release,
169 StorageBaseImpl_CreateStream,
170 StorageBaseImpl_OpenStream,
171 StorageImpl_CreateStorage,
172 StorageBaseImpl_OpenStorage,
174 StorageImpl_MoveElementTo,
177 StorageBaseImpl_EnumElements,
178 StorageImpl_DestroyElement,
179 StorageBaseImpl_RenameElement,
180 StorageImpl_SetElementTimes,
181 StorageBaseImpl_SetClass,
182 StorageImpl_SetStateBits,
187 * Virtual function table for the Storage32InternalImpl class.
189 static IStorageVtbl Storage32InternalImpl_Vtbl =
191 StorageBaseImpl_QueryInterface,
192 StorageBaseImpl_AddRef,
193 StorageBaseImpl_Release,
194 StorageBaseImpl_CreateStream,
195 StorageBaseImpl_OpenStream,
196 StorageImpl_CreateStorage,
197 StorageBaseImpl_OpenStorage,
199 StorageImpl_MoveElementTo,
200 StorageInternalImpl_Commit,
201 StorageInternalImpl_Revert,
202 StorageBaseImpl_EnumElements,
203 StorageImpl_DestroyElement,
204 StorageBaseImpl_RenameElement,
205 StorageImpl_SetElementTimes,
206 StorageBaseImpl_SetClass,
207 StorageImpl_SetStateBits,
212 * Virtual function table for the IEnumSTATSTGImpl class.
214 static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
216 IEnumSTATSTGImpl_QueryInterface,
217 IEnumSTATSTGImpl_AddRef,
218 IEnumSTATSTGImpl_Release,
219 IEnumSTATSTGImpl_Next,
220 IEnumSTATSTGImpl_Skip,
221 IEnumSTATSTGImpl_Reset,
222 IEnumSTATSTGImpl_Clone
229 /************************************************************************
230 ** Storage32BaseImpl implementatiion
233 /************************************************************************
234 * Storage32BaseImpl_QueryInterface (IUnknown)
236 * This method implements the common QueryInterface for all IStorage32
237 * implementations contained in this file.
239 * See Windows documentation for more details on IUnknown methods.
241 HRESULT WINAPI StorageBaseImpl_QueryInterface(
246 StorageBaseImpl *This = (StorageBaseImpl *)iface;
248 * Perform a sanity check on the parameters.
250 if ( (This==0) || (ppvObject==0) )
254 * Initialize the return parameter.
259 * Compare the riid with the interface IDs implemented by this object.
261 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
263 *ppvObject = (IStorage*)This;
265 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
267 *ppvObject = (IStorage*)This;
271 * Check that we obtained an interface.
274 return E_NOINTERFACE;
277 * Query Interface always increases the reference count by one when it is
280 StorageBaseImpl_AddRef(iface);
285 /************************************************************************
286 * Storage32BaseImpl_AddRef (IUnknown)
288 * This method implements the common AddRef for all IStorage32
289 * implementations contained in this file.
291 * See Windows documentation for more details on IUnknown methods.
293 ULONG WINAPI StorageBaseImpl_AddRef(
296 StorageBaseImpl *This = (StorageBaseImpl *)iface;
297 return InterlockedIncrement(&This->ref);
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 StorageBaseImpl *This = (StorageBaseImpl *)iface;
313 * Decrease the reference count on this object.
315 ULONG ref = InterlockedDecrement(&This->ref);
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);
333 /************************************************************************
334 * Storage32BaseImpl_OpenStream (IStorage)
336 * This method will open the specified stream object from the current storage.
338 * See Windows documentation for more details on IStorage methods.
340 HRESULT WINAPI StorageBaseImpl_OpenStream(
342 const OLECHAR* pwcsName, /* [string][in] */
343 void* reserved1, /* [unique][in] */
344 DWORD grfMode, /* [in] */
345 DWORD reserved2, /* [in] */
346 IStream** ppstm) /* [out] */
348 StorageBaseImpl *This = (StorageBaseImpl *)iface;
349 IEnumSTATSTGImpl* propertyEnumeration;
350 StgStreamImpl* newStream;
351 StgProperty currentProperty;
352 ULONG foundPropertyIndex;
353 HRESULT res = STG_E_UNKNOWN;
355 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
356 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
359 * Perform a sanity check on the parameters.
361 if ( (pwcsName==NULL) || (ppstm==0) )
368 * Initialize the out parameter
373 * Validate the STGM flags
375 if ( FAILED( validateSTGM(grfMode) ))
377 res = STG_E_INVALIDFLAG;
384 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
385 (grfMode & STGM_DELETEONRELEASE) ||
386 (grfMode & STGM_TRANSACTED) )
388 res = STG_E_INVALIDFUNCTION;
393 * Create a property enumeration to search the properties
395 propertyEnumeration = IEnumSTATSTGImpl_Construct(
396 This->ancestorStorage,
397 This->rootPropertySetIndex);
400 * Search the enumeration for the property with the given name
402 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
408 * Delete the property enumeration since we don't need it anymore
410 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
413 * If it was found, construct the stream object and return a pointer to it.
415 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
416 (currentProperty.propertyType==PROPTYPE_STREAM) )
418 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
422 newStream->grfMode = grfMode;
423 *ppstm = (IStream*)newStream;
426 * Since we are returning a pointer to the interface, we have to
427 * nail down the reference.
429 StgStreamImpl_AddRef(*ppstm);
439 res = STG_E_FILENOTFOUND;
443 TRACE("<-- IStream %p\n", *ppstm);
444 TRACE("<-- %08lx\n", res);
448 /************************************************************************
449 * Storage32BaseImpl_OpenStorage (IStorage)
451 * This method will open a new storage object from the current storage.
453 * See Windows documentation for more details on IStorage methods.
455 HRESULT WINAPI StorageBaseImpl_OpenStorage(
457 const OLECHAR* pwcsName, /* [string][unique][in] */
458 IStorage* pstgPriority, /* [unique][in] */
459 DWORD grfMode, /* [in] */
460 SNB snbExclude, /* [unique][in] */
461 DWORD reserved, /* [in] */
462 IStorage** ppstg) /* [out] */
464 StorageBaseImpl *This = (StorageBaseImpl *)iface;
465 StorageInternalImpl* newStorage;
466 IEnumSTATSTGImpl* propertyEnumeration;
467 StgProperty currentProperty;
468 ULONG foundPropertyIndex;
469 HRESULT res = STG_E_UNKNOWN;
471 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
472 iface, debugstr_w(pwcsName), pstgPriority,
473 grfMode, snbExclude, reserved, ppstg);
476 * Perform a sanity check on the parameters.
478 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
485 if (snbExclude != NULL)
487 res = STG_E_INVALIDPARAMETER;
492 * Validate the STGM flags
494 if ( FAILED( validateSTGM(grfMode) ))
496 res = STG_E_INVALIDFLAG;
503 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
504 (grfMode & STGM_DELETEONRELEASE) ||
505 (grfMode & STGM_PRIORITY) )
507 res = STG_E_INVALIDFUNCTION;
512 * Initialize the out parameter
517 * Create a property enumeration to search the properties
519 propertyEnumeration = IEnumSTATSTGImpl_Construct(
520 This->ancestorStorage,
521 This->rootPropertySetIndex);
524 * Search the enumeration for the property with the given name
526 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
532 * Delete the property enumeration since we don't need it anymore
534 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
537 * If it was found, construct the stream object and return a pointer to it.
539 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
540 (currentProperty.propertyType==PROPTYPE_STORAGE) )
543 * Construct a new Storage object
545 newStorage = StorageInternalImpl_Construct(
546 This->ancestorStorage,
551 *ppstg = (IStorage*)newStorage;
554 * Since we are returning a pointer to the interface,
555 * we have to nail down the reference.
557 StorageBaseImpl_AddRef(*ppstg);
563 res = STG_E_INSUFFICIENTMEMORY;
567 res = STG_E_FILENOTFOUND;
570 TRACE("<-- %08lx\n", res);
574 /************************************************************************
575 * Storage32BaseImpl_EnumElements (IStorage)
577 * This method will create an enumerator object that can be used to
578 * retrieve informatino about all the properties in the storage object.
580 * See Windows documentation for more details on IStorage methods.
582 HRESULT WINAPI StorageBaseImpl_EnumElements(
584 DWORD reserved1, /* [in] */
585 void* reserved2, /* [size_is][unique][in] */
586 DWORD reserved3, /* [in] */
587 IEnumSTATSTG** ppenum) /* [out] */
589 StorageBaseImpl *This = (StorageBaseImpl *)iface;
590 IEnumSTATSTGImpl* newEnum;
592 TRACE("(%p, %ld, %p, %ld, %p)\n",
593 iface, reserved1, reserved2, reserved3, ppenum);
596 * Perform a sanity check on the parameters.
598 if ( (This==0) || (ppenum==0))
602 * Construct the enumerator.
604 newEnum = IEnumSTATSTGImpl_Construct(
605 This->ancestorStorage,
606 This->rootPropertySetIndex);
610 *ppenum = (IEnumSTATSTG*)newEnum;
613 * Don't forget to nail down a reference to the new object before
616 IEnumSTATSTGImpl_AddRef(*ppenum);
621 return E_OUTOFMEMORY;
624 /************************************************************************
625 * Storage32BaseImpl_Stat (IStorage)
627 * This method will retrieve information about this storage object.
629 * See Windows documentation for more details on IStorage methods.
631 HRESULT WINAPI StorageBaseImpl_Stat(
633 STATSTG* pstatstg, /* [out] */
634 DWORD grfStatFlag) /* [in] */
636 StorageBaseImpl *This = (StorageBaseImpl *)iface;
637 StgProperty curProperty;
639 HRESULT res = STG_E_UNKNOWN;
641 TRACE("(%p, %p, %lx)\n",
642 iface, pstatstg, grfStatFlag);
645 * Perform a sanity check on the parameters.
647 if ( (This==0) || (pstatstg==0))
654 * Read the information from the property.
656 readSuccessful = StorageImpl_ReadProperty(
657 This->ancestorStorage,
658 This->rootPropertySetIndex,
663 StorageUtl_CopyPropertyToSTATSTG(
677 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
679 TRACE("<-- %08lx\n", res);
683 /************************************************************************
684 * Storage32BaseImpl_RenameElement (IStorage)
686 * This method will rename the specified element.
688 * See Windows documentation for more details on IStorage methods.
690 * Implementation notes: The method used to rename consists of creating a clone
691 * of the deleted StgProperty object setting it with the new name and to
692 * perform a DestroyElement of the old StgProperty.
694 HRESULT WINAPI StorageBaseImpl_RenameElement(
696 const OLECHAR* pwcsOldName, /* [in] */
697 const OLECHAR* pwcsNewName) /* [in] */
699 StorageBaseImpl *This = (StorageBaseImpl *)iface;
700 IEnumSTATSTGImpl* propertyEnumeration;
701 StgProperty currentProperty;
702 ULONG foundPropertyIndex;
704 TRACE("(%p, %s, %s)\n",
705 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
708 * Create a property enumeration to search the properties
710 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
711 This->rootPropertySetIndex);
714 * Search the enumeration for the new property name
716 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
720 if (foundPropertyIndex != PROPERTY_NULL)
723 * There is already a property with the new name
725 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
726 return STG_E_FILEALREADYEXISTS;
729 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
732 * Search the enumeration for the old property name
734 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
739 * Delete the property enumeration since we don't need it anymore
741 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
743 if (foundPropertyIndex != PROPERTY_NULL)
745 StgProperty renamedProperty;
746 ULONG renamedPropertyIndex;
749 * Setup a new property for the renamed property
751 renamedProperty.sizeOfNameString =
752 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
754 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
755 return STG_E_INVALIDNAME;
757 strcpyW(renamedProperty.name, pwcsNewName);
759 renamedProperty.propertyType = currentProperty.propertyType;
760 renamedProperty.startingBlock = currentProperty.startingBlock;
761 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
762 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
764 renamedProperty.previousProperty = PROPERTY_NULL;
765 renamedProperty.nextProperty = PROPERTY_NULL;
768 * Bring the dirProperty link in case it is a storage and in which
769 * case the renamed storage elements don't require to be reorganized.
771 renamedProperty.dirProperty = currentProperty.dirProperty;
773 /* call CoFileTime to get the current time
774 renamedProperty.timeStampS1
775 renamedProperty.timeStampD1
776 renamedProperty.timeStampS2
777 renamedProperty.timeStampD2
778 renamedProperty.propertyUniqueID
782 * Obtain a free property in the property chain
784 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
787 * Save the new property into the new property spot
789 StorageImpl_WriteProperty(
790 This->ancestorStorage,
791 renamedPropertyIndex,
795 * Find a spot in the property chain for our newly created property.
799 renamedPropertyIndex,
803 * At this point the renamed property has been inserted in the tree,
804 * now, before to Destroy the old property we must zeroed it's dirProperty
805 * otherwise the DestroyProperty below will zap it all and we do not want
807 * Also, we fake that the old property is a storage so the DestroyProperty
808 * will not do a SetSize(0) on the stream data.
810 * This means that we need to tweek the StgProperty if it is a stream or a
813 StorageImpl_ReadProperty(This->ancestorStorage,
817 currentProperty.dirProperty = PROPERTY_NULL;
818 currentProperty.propertyType = PROPTYPE_STORAGE;
819 StorageImpl_WriteProperty(
820 This->ancestorStorage,
825 * Invoke Destroy to get rid of the ole property and automatically redo
826 * the linking of it's previous and next members...
828 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
834 * There is no property with the old name
836 return STG_E_FILENOTFOUND;
842 /************************************************************************
843 * Storage32BaseImpl_CreateStream (IStorage)
845 * This method will create a stream object within this storage
847 * See Windows documentation for more details on IStorage methods.
849 HRESULT WINAPI StorageBaseImpl_CreateStream(
851 const OLECHAR* pwcsName, /* [string][in] */
852 DWORD grfMode, /* [in] */
853 DWORD reserved1, /* [in] */
854 DWORD reserved2, /* [in] */
855 IStream** ppstm) /* [out] */
857 StorageBaseImpl *This = (StorageBaseImpl *)iface;
858 IEnumSTATSTGImpl* propertyEnumeration;
859 StgStreamImpl* newStream;
860 StgProperty currentProperty, newStreamProperty;
861 ULONG foundPropertyIndex, newPropertyIndex;
863 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
864 iface, debugstr_w(pwcsName), grfMode,
865 reserved1, reserved2, ppstm);
868 * Validate parameters
871 return STG_E_INVALIDPOINTER;
874 return STG_E_INVALIDNAME;
876 if (reserved1 || reserved2)
877 return STG_E_INVALIDPARAMETER;
880 * Validate the STGM flags
882 if ( FAILED( validateSTGM(grfMode) ))
883 return STG_E_INVALIDFLAG;
885 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) )
886 return STG_E_INVALIDFLAG;
891 if ((grfMode & STGM_DELETEONRELEASE) ||
892 (grfMode & STGM_TRANSACTED))
893 return STG_E_INVALIDFUNCTION;
896 * Initialize the out parameter
901 * Create a property enumeration to search the properties
903 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
904 This->rootPropertySetIndex);
906 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
910 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
912 if (foundPropertyIndex != PROPERTY_NULL)
915 * An element with this name already exists
917 if (grfMode & STGM_CREATE)
919 IStorage_DestroyElement(iface, pwcsName);
922 return STG_E_FILEALREADYEXISTS;
926 * memset the empty property
928 memset(&newStreamProperty, 0, sizeof(StgProperty));
930 newStreamProperty.sizeOfNameString =
931 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
933 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
934 return STG_E_INVALIDNAME;
936 strcpyW(newStreamProperty.name, pwcsName);
938 newStreamProperty.propertyType = PROPTYPE_STREAM;
939 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
940 newStreamProperty.size.u.LowPart = 0;
941 newStreamProperty.size.u.HighPart = 0;
943 newStreamProperty.previousProperty = PROPERTY_NULL;
944 newStreamProperty.nextProperty = PROPERTY_NULL;
945 newStreamProperty.dirProperty = PROPERTY_NULL;
947 /* call CoFileTime to get the current time
948 newStreamProperty.timeStampS1
949 newStreamProperty.timeStampD1
950 newStreamProperty.timeStampS2
951 newStreamProperty.timeStampD2
954 /* newStreamProperty.propertyUniqueID */
957 * Get a free property or create a new one
959 newPropertyIndex = getFreeProperty(This->ancestorStorage);
962 * Save the new property into the new property spot
964 StorageImpl_WriteProperty(
965 This->ancestorStorage,
970 * Find a spot in the property chain for our newly created property.
978 * Open the stream to return it.
980 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
984 *ppstm = (IStream*)newStream;
987 * Since we are returning a pointer to the interface, we have to nail down
990 StgStreamImpl_AddRef(*ppstm);
994 return STG_E_INSUFFICIENTMEMORY;
1000 /************************************************************************
1001 * Storage32BaseImpl_SetClass (IStorage)
1003 * This method will write the specified CLSID in the property of this
1006 * See Windows documentation for more details on IStorage methods.
1008 HRESULT WINAPI StorageBaseImpl_SetClass(
1010 REFCLSID clsid) /* [in] */
1012 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1013 HRESULT hRes = E_FAIL;
1014 StgProperty curProperty;
1017 TRACE("(%p, %p)\n", iface, clsid);
1019 success = StorageImpl_ReadProperty(This->ancestorStorage,
1020 This->rootPropertySetIndex,
1024 curProperty.propertyUniqueID = *clsid;
1026 success = StorageImpl_WriteProperty(This->ancestorStorage,
1027 This->rootPropertySetIndex,
1036 /************************************************************************
1037 ** Storage32Impl implementation
1040 /************************************************************************
1041 * Storage32Impl_CreateStorage (IStorage)
1043 * This method will create the storage object within the provided storage.
1045 * See Windows documentation for more details on IStorage methods.
1047 HRESULT WINAPI StorageImpl_CreateStorage(
1049 const OLECHAR *pwcsName, /* [string][in] */
1050 DWORD grfMode, /* [in] */
1051 DWORD reserved1, /* [in] */
1052 DWORD reserved2, /* [in] */
1053 IStorage **ppstg) /* [out] */
1055 StorageImpl* const This=(StorageImpl*)iface;
1057 IEnumSTATSTGImpl *propertyEnumeration;
1058 StgProperty currentProperty;
1059 StgProperty newProperty;
1060 ULONG foundPropertyIndex;
1061 ULONG newPropertyIndex;
1064 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1065 iface, debugstr_w(pwcsName), grfMode,
1066 reserved1, reserved2, ppstg);
1069 * Validate parameters
1072 return STG_E_INVALIDPOINTER;
1075 return STG_E_INVALIDNAME;
1078 * Validate the STGM flags
1080 if ( FAILED( validateSTGM(grfMode) ) ||
1081 (grfMode & STGM_DELETEONRELEASE) )
1082 return STG_E_INVALIDFLAG;
1085 * Initialize the out parameter
1090 * Create a property enumeration and search the properties
1092 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1093 This->rootPropertySetIndex);
1095 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1098 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1100 if (foundPropertyIndex != PROPERTY_NULL)
1103 * An element with this name already exists
1105 if (grfMode & STGM_CREATE)
1106 IStorage_DestroyElement(iface, pwcsName);
1108 return STG_E_FILEALREADYEXISTS;
1112 * memset the empty property
1114 memset(&newProperty, 0, sizeof(StgProperty));
1116 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1118 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1119 return STG_E_INVALIDNAME;
1121 strcpyW(newProperty.name, pwcsName);
1123 newProperty.propertyType = PROPTYPE_STORAGE;
1124 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1125 newProperty.size.u.LowPart = 0;
1126 newProperty.size.u.HighPart = 0;
1128 newProperty.previousProperty = PROPERTY_NULL;
1129 newProperty.nextProperty = PROPERTY_NULL;
1130 newProperty.dirProperty = PROPERTY_NULL;
1132 /* call CoFileTime to get the current time
1133 newProperty.timeStampS1
1134 newProperty.timeStampD1
1135 newProperty.timeStampS2
1136 newProperty.timeStampD2
1139 /* newStorageProperty.propertyUniqueID */
1142 * Obtain a free property in the property chain
1144 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1147 * Save the new property into the new property spot
1149 StorageImpl_WriteProperty(
1150 This->ancestorStorage,
1155 * Find a spot in the property chain for our newly created property.
1157 updatePropertyChain(
1163 * Open it to get a pointer to return.
1165 hr = IStorage_OpenStorage(
1167 (const OLECHAR*)pwcsName,
1174 if( (hr != S_OK) || (*ppstg == NULL))
1184 /***************************************************************************
1188 * Get a free property or create a new one.
1190 static ULONG getFreeProperty(
1191 StorageImpl *storage)
1193 ULONG currentPropertyIndex = 0;
1194 ULONG newPropertyIndex = PROPERTY_NULL;
1195 BOOL readSuccessful = TRUE;
1196 StgProperty currentProperty;
1201 * Start by reading the root property
1203 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1204 currentPropertyIndex,
1208 if (currentProperty.sizeOfNameString == 0)
1211 * The property existis and is available, we found it.
1213 newPropertyIndex = currentPropertyIndex;
1219 * We exhausted the property list, we will create more space below
1221 newPropertyIndex = currentPropertyIndex;
1223 currentPropertyIndex++;
1225 } while (newPropertyIndex == PROPERTY_NULL);
1228 * grow the property chain
1230 if (! readSuccessful)
1232 StgProperty emptyProperty;
1233 ULARGE_INTEGER newSize;
1234 ULONG propertyIndex;
1235 ULONG lastProperty = 0;
1236 ULONG blockCount = 0;
1239 * obtain the new count of property blocks
1241 blockCount = BlockChainStream_GetCount(
1242 storage->ancestorStorage->rootBlockChain)+1;
1245 * initialize the size used by the property stream
1247 newSize.u.HighPart = 0;
1248 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1251 * add a property block to the property chain
1253 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1256 * memset the empty property in order to initialize the unused newly
1259 memset(&emptyProperty, 0, sizeof(StgProperty));
1264 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1267 propertyIndex = newPropertyIndex;
1268 propertyIndex < lastProperty;
1271 StorageImpl_WriteProperty(
1272 storage->ancestorStorage,
1278 return newPropertyIndex;
1281 /****************************************************************************
1285 * Case insensitive comparaison of StgProperty.name by first considering
1288 * Returns <0 when newPrpoerty < currentProperty
1289 * >0 when newPrpoerty > currentProperty
1290 * 0 when newPrpoerty == currentProperty
1292 static LONG propertyNameCmp(
1293 const OLECHAR *newProperty,
1294 const OLECHAR *currentProperty)
1296 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1301 * We compare the string themselves only when they are of the same length
1303 diff = lstrcmpiW( newProperty, currentProperty);
1309 /****************************************************************************
1313 * Properly link this new element in the property chain.
1315 static void updatePropertyChain(
1316 StorageImpl *storage,
1317 ULONG newPropertyIndex,
1318 StgProperty newProperty)
1320 StgProperty currentProperty;
1323 * Read the root property
1325 StorageImpl_ReadProperty(storage->ancestorStorage,
1326 storage->rootPropertySetIndex,
1329 if (currentProperty.dirProperty != PROPERTY_NULL)
1332 * The root storage contains some element, therefore, start the research
1333 * for the appropriate location.
1336 ULONG current, next, previous, currentPropertyId;
1339 * Keep the StgProperty sequence number of the storage first property
1341 currentPropertyId = currentProperty.dirProperty;
1346 StorageImpl_ReadProperty(storage->ancestorStorage,
1347 currentProperty.dirProperty,
1350 previous = currentProperty.previousProperty;
1351 next = currentProperty.nextProperty;
1352 current = currentPropertyId;
1356 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1360 if (previous != PROPERTY_NULL)
1362 StorageImpl_ReadProperty(storage->ancestorStorage,
1369 currentProperty.previousProperty = newPropertyIndex;
1370 StorageImpl_WriteProperty(storage->ancestorStorage,
1378 if (next != PROPERTY_NULL)
1380 StorageImpl_ReadProperty(storage->ancestorStorage,
1387 currentProperty.nextProperty = newPropertyIndex;
1388 StorageImpl_WriteProperty(storage->ancestorStorage,
1397 * Trying to insert an item with the same name in the
1398 * subtree structure.
1403 previous = currentProperty.previousProperty;
1404 next = currentProperty.nextProperty;
1410 * The root storage is empty, link the new property to it's dir property
1412 currentProperty.dirProperty = newPropertyIndex;
1413 StorageImpl_WriteProperty(storage->ancestorStorage,
1414 storage->rootPropertySetIndex,
1420 /*************************************************************************
1423 HRESULT WINAPI StorageImpl_CopyTo(
1425 DWORD ciidExclude, /* [in] */
1426 const IID* rgiidExclude, /* [size_is][unique][in] */
1427 SNB snbExclude, /* [unique][in] */
1428 IStorage* pstgDest) /* [unique][in] */
1430 IEnumSTATSTG *elements = 0;
1431 STATSTG curElement, strStat;
1433 IStorage *pstgTmp, *pstgChild;
1434 IStream *pstrTmp, *pstrChild;
1436 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1437 FIXME("Exclude option not implemented\n");
1439 TRACE("(%p, %ld, %p, %p, %p)\n",
1440 iface, ciidExclude, rgiidExclude,
1441 snbExclude, pstgDest);
1444 * Perform a sanity check
1446 if ( pstgDest == 0 )
1447 return STG_E_INVALIDPOINTER;
1450 * Enumerate the elements
1452 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1460 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1461 IStorage_SetClass( pstgDest, &curElement.clsid );
1466 * Obtain the next element
1468 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1470 if ( hr == S_FALSE )
1472 hr = S_OK; /* done, every element has been copied */
1476 if (curElement.type == STGTY_STORAGE)
1479 * open child source storage
1481 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1482 STGM_READ|STGM_SHARE_EXCLUSIVE,
1483 NULL, 0, &pstgChild );
1489 * Check if destination storage is not a child of the source
1490 * storage, which will cause an infinite loop
1492 if (pstgChild == pstgDest)
1494 IEnumSTATSTG_Release(elements);
1496 return STG_E_ACCESSDENIED;
1500 * create a new storage in destination storage
1502 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1503 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1507 * if it already exist, don't create a new one use this one
1509 if (hr == STG_E_FILEALREADYEXISTS)
1511 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1512 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1513 NULL, 0, &pstgTmp );
1521 * do the copy recursively
1523 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1524 snbExclude, pstgTmp );
1526 IStorage_Release( pstgTmp );
1527 IStorage_Release( pstgChild );
1529 else if (curElement.type == STGTY_STREAM)
1532 * create a new stream in destination storage. If the stream already
1533 * exist, it will be deleted and a new one will be created.
1535 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1536 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1543 * open child stream storage
1545 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1546 STGM_READ|STGM_SHARE_EXCLUSIVE,
1553 * Get the size of the source stream
1555 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1558 * Set the size of the destination stream.
1560 IStream_SetSize(pstrTmp, strStat.cbSize);
1565 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1568 IStream_Release( pstrTmp );
1569 IStream_Release( pstrChild );
1573 WARN("unknown element type: %ld\n", curElement.type);
1576 } while (hr == S_OK);
1581 IEnumSTATSTG_Release(elements);
1586 /*************************************************************************
1587 * MoveElementTo (IStorage)
1589 HRESULT WINAPI StorageImpl_MoveElementTo(
1591 const OLECHAR *pwcsName, /* [string][in] */
1592 IStorage *pstgDest, /* [unique][in] */
1593 const OLECHAR *pwcsNewName,/* [string][in] */
1594 DWORD grfFlags) /* [in] */
1596 FIXME("not implemented!\n");
1600 /*************************************************************************
1603 HRESULT WINAPI StorageImpl_Commit(
1605 DWORD grfCommitFlags)/* [in] */
1607 FIXME("(%ld): stub!\n", grfCommitFlags);
1611 /*************************************************************************
1614 HRESULT WINAPI StorageImpl_Revert(
1617 FIXME("not implemented!\n");
1621 /*************************************************************************
1622 * DestroyElement (IStorage)
1624 * Stategy: This implementation is build this way for simplicity not for speed.
1625 * I always delete the top most element of the enumeration and adjust
1626 * the deleted element pointer all the time. This takes longer to
1627 * do but allow to reinvoke DestroyElement whenever we encounter a
1628 * storage object. The optimisation reside in the usage of another
1629 * enumeration stategy that would give all the leaves of a storage
1630 * first. (postfix order)
1632 HRESULT WINAPI StorageImpl_DestroyElement(
1634 const OLECHAR *pwcsName)/* [string][in] */
1636 StorageImpl* const This=(StorageImpl*)iface;
1638 IEnumSTATSTGImpl* propertyEnumeration;
1641 StgProperty propertyToDelete;
1642 StgProperty parentProperty;
1643 ULONG foundPropertyIndexToDelete;
1644 ULONG typeOfRelation;
1645 ULONG parentPropertyId;
1648 iface, debugstr_w(pwcsName));
1651 * Perform a sanity check on the parameters.
1654 return STG_E_INVALIDPOINTER;
1657 * Create a property enumeration to search the property with the given name
1659 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1660 This->ancestorStorage,
1661 This->rootPropertySetIndex);
1663 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1664 propertyEnumeration,
1668 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1670 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1672 return STG_E_FILENOTFOUND;
1676 * Find the parent property of the property to delete (the one that
1677 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1678 * the parent is This. Otherwise, the parent is one of it's sibling...
1682 * First, read This's StgProperty..
1684 res = StorageImpl_ReadProperty(
1685 This->ancestorStorage,
1686 This->rootPropertySetIndex,
1692 * Second, check to see if by any chance the actual storage (This) is not
1693 * the parent of the property to delete... We never know...
1695 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1698 * Set data as it would have been done in the else part...
1700 typeOfRelation = PROPERTY_RELATION_DIR;
1701 parentPropertyId = This->rootPropertySetIndex;
1706 * Create a property enumeration to search the parent properties, and
1707 * delete it once done.
1709 IEnumSTATSTGImpl* propertyEnumeration2;
1711 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1712 This->ancestorStorage,
1713 This->rootPropertySetIndex);
1715 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1716 propertyEnumeration2,
1717 foundPropertyIndexToDelete,
1721 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1724 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1726 hr = deleteStorageProperty(
1728 foundPropertyIndexToDelete,
1731 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1733 hr = deleteStreamProperty(
1735 foundPropertyIndexToDelete,
1743 * Adjust the property chain
1745 hr = adjustPropertyChain(
1756 /************************************************************************
1757 * StorageImpl_Stat (IStorage)
1759 * This method will retrieve information about this storage object.
1761 * See Windows documentation for more details on IStorage methods.
1763 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1764 STATSTG* pstatstg, /* [out] */
1765 DWORD grfStatFlag) /* [in] */
1767 StorageImpl* const This = (StorageImpl*)iface;
1768 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1770 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1772 CoTaskMemFree(pstatstg->pwcsName);
1773 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1774 strcpyW(pstatstg->pwcsName, This->pwcsName);
1782 /*********************************************************************
1786 * Perform the deletion of a complete storage node
1789 static HRESULT deleteStorageProperty(
1790 StorageImpl *parentStorage,
1791 ULONG indexOfPropertyToDelete,
1792 StgProperty propertyToDelete)
1794 IEnumSTATSTG *elements = 0;
1795 IStorage *childStorage = 0;
1796 STATSTG currentElement;
1798 HRESULT destroyHr = S_OK;
1801 * Open the storage and enumerate it
1803 hr = StorageBaseImpl_OpenStorage(
1804 (IStorage*)parentStorage,
1805 propertyToDelete.name,
1807 STGM_SHARE_EXCLUSIVE,
1818 * Enumerate the elements
1820 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1825 * Obtain the next element
1827 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1830 destroyHr = StorageImpl_DestroyElement(
1831 (IStorage*)childStorage,
1832 (OLECHAR*)currentElement.pwcsName);
1834 CoTaskMemFree(currentElement.pwcsName);
1838 * We need to Reset the enumeration every time because we delete elements
1839 * and the enumeration could be invalid
1841 IEnumSTATSTG_Reset(elements);
1843 } while ((hr == S_OK) && (destroyHr == S_OK));
1846 * Invalidate the property by zeroing it's name member.
1848 propertyToDelete.sizeOfNameString = 0;
1850 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1851 indexOfPropertyToDelete,
1854 IStorage_Release(childStorage);
1855 IEnumSTATSTG_Release(elements);
1860 /*********************************************************************
1864 * Perform the deletion of a stream node
1867 static HRESULT deleteStreamProperty(
1868 StorageImpl *parentStorage,
1869 ULONG indexOfPropertyToDelete,
1870 StgProperty propertyToDelete)
1874 ULARGE_INTEGER size;
1876 size.u.HighPart = 0;
1879 hr = StorageBaseImpl_OpenStream(
1880 (IStorage*)parentStorage,
1881 (OLECHAR*)propertyToDelete.name,
1883 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1895 hr = IStream_SetSize(pis, size);
1903 * Release the stream object.
1905 IStream_Release(pis);
1908 * Invalidate the property by zeroing it's name member.
1910 propertyToDelete.sizeOfNameString = 0;
1913 * Here we should re-read the property so we get the updated pointer
1914 * but since we are here to zap it, I don't do it...
1916 StorageImpl_WriteProperty(
1917 parentStorage->ancestorStorage,
1918 indexOfPropertyToDelete,
1924 /*********************************************************************
1928 * Finds a placeholder for the StgProperty within the Storage
1931 static HRESULT findPlaceholder(
1932 StorageImpl *storage,
1933 ULONG propertyIndexToStore,
1934 ULONG storePropertyIndex,
1937 StgProperty storeProperty;
1942 * Read the storage property
1944 res = StorageImpl_ReadProperty(
1945 storage->ancestorStorage,
1954 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1956 if (storeProperty.previousProperty != PROPERTY_NULL)
1958 return findPlaceholder(
1960 propertyIndexToStore,
1961 storeProperty.previousProperty,
1966 storeProperty.previousProperty = propertyIndexToStore;
1969 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1971 if (storeProperty.nextProperty != PROPERTY_NULL)
1973 return findPlaceholder(
1975 propertyIndexToStore,
1976 storeProperty.nextProperty,
1981 storeProperty.nextProperty = propertyIndexToStore;
1984 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1986 if (storeProperty.dirProperty != PROPERTY_NULL)
1988 return findPlaceholder(
1990 propertyIndexToStore,
1991 storeProperty.dirProperty,
1996 storeProperty.dirProperty = propertyIndexToStore;
2000 hr = StorageImpl_WriteProperty(
2001 storage->ancestorStorage,
2013 /*************************************************************************
2017 * This method takes the previous and the next property link of a property
2018 * to be deleted and find them a place in the Storage.
2020 static HRESULT adjustPropertyChain(
2022 StgProperty propertyToDelete,
2023 StgProperty parentProperty,
2024 ULONG parentPropertyId,
2027 ULONG newLinkProperty = PROPERTY_NULL;
2028 BOOL needToFindAPlaceholder = FALSE;
2029 ULONG storeNode = PROPERTY_NULL;
2030 ULONG toStoreNode = PROPERTY_NULL;
2031 INT relationType = 0;
2035 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2037 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2040 * Set the parent previous to the property to delete previous
2042 newLinkProperty = propertyToDelete.previousProperty;
2044 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2047 * We also need to find a storage for the other link, setup variables
2048 * to do this at the end...
2050 needToFindAPlaceholder = TRUE;
2051 storeNode = propertyToDelete.previousProperty;
2052 toStoreNode = propertyToDelete.nextProperty;
2053 relationType = PROPERTY_RELATION_NEXT;
2056 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2059 * Set the parent previous to the property to delete next
2061 newLinkProperty = propertyToDelete.nextProperty;
2065 * Link it for real...
2067 parentProperty.previousProperty = newLinkProperty;
2070 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2072 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2075 * Set the parent next to the property to delete next previous
2077 newLinkProperty = propertyToDelete.previousProperty;
2079 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2082 * We also need to find a storage for the other link, setup variables
2083 * to do this at the end...
2085 needToFindAPlaceholder = TRUE;
2086 storeNode = propertyToDelete.previousProperty;
2087 toStoreNode = propertyToDelete.nextProperty;
2088 relationType = PROPERTY_RELATION_NEXT;
2091 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2094 * Set the parent next to the property to delete next
2096 newLinkProperty = propertyToDelete.nextProperty;
2100 * Link it for real...
2102 parentProperty.nextProperty = newLinkProperty;
2104 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2106 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2109 * Set the parent dir to the property to delete previous
2111 newLinkProperty = propertyToDelete.previousProperty;
2113 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2116 * We also need to find a storage for the other link, setup variables
2117 * to do this at the end...
2119 needToFindAPlaceholder = TRUE;
2120 storeNode = propertyToDelete.previousProperty;
2121 toStoreNode = propertyToDelete.nextProperty;
2122 relationType = PROPERTY_RELATION_NEXT;
2125 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2128 * Set the parent dir to the property to delete next
2130 newLinkProperty = propertyToDelete.nextProperty;
2134 * Link it for real...
2136 parentProperty.dirProperty = newLinkProperty;
2140 * Write back the parent property
2142 res = StorageImpl_WriteProperty(
2143 This->ancestorStorage,
2152 * If a placeholder is required for the other link, then, find one and
2153 * get out of here...
2155 if (needToFindAPlaceholder)
2157 hr = findPlaceholder(
2168 /******************************************************************************
2169 * SetElementTimes (IStorage)
2171 HRESULT WINAPI StorageImpl_SetElementTimes(
2173 const OLECHAR *pwcsName,/* [string][in] */
2174 const FILETIME *pctime, /* [in] */
2175 const FILETIME *patime, /* [in] */
2176 const FILETIME *pmtime) /* [in] */
2178 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2182 /******************************************************************************
2183 * SetStateBits (IStorage)
2185 HRESULT WINAPI StorageImpl_SetStateBits(
2187 DWORD grfStateBits,/* [in] */
2188 DWORD grfMask) /* [in] */
2190 FIXME("not implemented!\n");
2194 HRESULT StorageImpl_Construct(
2204 StgProperty currentProperty;
2205 BOOL readSuccessful;
2206 ULONG currentPropertyIndex;
2208 if ( FAILED( validateSTGM(openFlags) ))
2209 return STG_E_INVALIDFLAG;
2211 memset(This, 0, sizeof(StorageImpl));
2214 * Initialize the virtual function table.
2216 This->lpVtbl = &Storage32Impl_Vtbl;
2217 This->v_destructor = &StorageImpl_Destroy;
2220 * This is the top-level storage so initialize the ancestor pointer
2223 This->ancestorStorage = This;
2226 * Initialize the physical support of the storage.
2228 This->hFile = hFile;
2231 * Store copy of file path.
2234 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2235 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2236 if (!This->pwcsName)
2237 return STG_E_INSUFFICIENTMEMORY;
2238 strcpyW(This->pwcsName, pwcsName);
2242 * Initialize the big block cache.
2244 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2245 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2246 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2252 if (This->bigBlockFile == 0)
2257 ULARGE_INTEGER size;
2258 BYTE* bigBlockBuffer;
2261 * Initialize all header variables:
2262 * - The big block depot consists of one block and it is at block 0
2263 * - The properties start at block 1
2264 * - There is no small block depot
2266 memset( This->bigBlockDepotStart,
2268 sizeof(This->bigBlockDepotStart));
2270 This->bigBlockDepotCount = 1;
2271 This->bigBlockDepotStart[0] = 0;
2272 This->rootStartBlock = 1;
2273 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2274 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2275 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2276 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2277 This->extBigBlockDepotCount = 0;
2279 StorageImpl_SaveFileHeader(This);
2282 * Add one block for the big block depot and one block for the properties
2284 size.u.HighPart = 0;
2285 size.u.LowPart = This->bigBlockSize * 3;
2286 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2289 * Initialize the big block depot
2291 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2292 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2293 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2294 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2295 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2300 * Load the header for the file.
2302 hr = StorageImpl_LoadFileHeader(This);
2306 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2313 * There is no block depot cached yet.
2315 This->indexBlockDepotCached = 0xFFFFFFFF;
2318 * Start searching for free blocks with block 0.
2320 This->prevFreeBlock = 0;
2323 * Create the block chain abstractions.
2325 if(!(This->rootBlockChain =
2326 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2327 return STG_E_READFAULT;
2329 if(!(This->smallBlockDepotChain =
2330 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2332 return STG_E_READFAULT;
2335 * Write the root property
2339 StgProperty rootProp;
2341 * Initialize the property chain
2343 memset(&rootProp, 0, sizeof(rootProp));
2344 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2345 sizeof(rootProp.name)/sizeof(WCHAR) );
2346 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2347 rootProp.propertyType = PROPTYPE_ROOT;
2348 rootProp.previousProperty = PROPERTY_NULL;
2349 rootProp.nextProperty = PROPERTY_NULL;
2350 rootProp.dirProperty = PROPERTY_NULL;
2351 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2352 rootProp.size.u.HighPart = 0;
2353 rootProp.size.u.LowPart = 0;
2355 StorageImpl_WriteProperty(This, 0, &rootProp);
2359 * Find the ID of the root in the property sets.
2361 currentPropertyIndex = 0;
2365 readSuccessful = StorageImpl_ReadProperty(
2367 currentPropertyIndex,
2372 if ( (currentProperty.sizeOfNameString != 0 ) &&
2373 (currentProperty.propertyType == PROPTYPE_ROOT) )
2375 This->rootPropertySetIndex = currentPropertyIndex;
2379 currentPropertyIndex++;
2381 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2383 if (!readSuccessful)
2386 return STG_E_READFAULT;
2390 * Create the block chain abstraction for the small block root chain.
2392 if(!(This->smallBlockRootChain =
2393 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2394 return STG_E_READFAULT;
2399 void StorageImpl_Destroy(
2402 TRACE("(%p)\n", This);
2404 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2406 BlockChainStream_Destroy(This->smallBlockRootChain);
2407 BlockChainStream_Destroy(This->rootBlockChain);
2408 BlockChainStream_Destroy(This->smallBlockDepotChain);
2410 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2414 /******************************************************************************
2415 * Storage32Impl_GetNextFreeBigBlock
2417 * Returns the index of the next free big block.
2418 * If the big block depot is filled, this method will enlarge it.
2421 ULONG StorageImpl_GetNextFreeBigBlock(
2424 ULONG depotBlockIndexPos;
2426 ULONG depotBlockOffset;
2427 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2428 ULONG nextBlockIndex = BLOCK_SPECIAL;
2430 ULONG freeBlock = BLOCK_UNUSED;
2432 depotIndex = This->prevFreeBlock / blocksPerDepot;
2433 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2436 * Scan the entire big block depot until we find a block marked free
2438 while (nextBlockIndex != BLOCK_UNUSED)
2440 if (depotIndex < COUNT_BBDEPOTINHEADER)
2442 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2445 * Grow the primary depot.
2447 if (depotBlockIndexPos == BLOCK_UNUSED)
2449 depotBlockIndexPos = depotIndex*blocksPerDepot;
2452 * Add a block depot.
2454 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2455 This->bigBlockDepotCount++;
2456 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2459 * Flag it as a block depot.
2461 StorageImpl_SetNextBlockInChain(This,
2465 /* Save new header information.
2467 StorageImpl_SaveFileHeader(This);
2472 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2474 if (depotBlockIndexPos == BLOCK_UNUSED)
2477 * Grow the extended depot.
2479 ULONG extIndex = BLOCK_UNUSED;
2480 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2481 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2483 if (extBlockOffset == 0)
2485 /* We need an extended block.
2487 extIndex = Storage32Impl_AddExtBlockDepot(This);
2488 This->extBigBlockDepotCount++;
2489 depotBlockIndexPos = extIndex + 1;
2492 depotBlockIndexPos = depotIndex * blocksPerDepot;
2495 * Add a block depot and mark it in the extended block.
2497 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2498 This->bigBlockDepotCount++;
2499 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2501 /* Flag the block depot.
2503 StorageImpl_SetNextBlockInChain(This,
2507 /* If necessary, flag the extended depot block.
2509 if (extIndex != BLOCK_UNUSED)
2510 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2512 /* Save header information.
2514 StorageImpl_SaveFileHeader(This);
2518 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2520 if (depotBuffer != 0)
2522 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2523 ( nextBlockIndex != BLOCK_UNUSED))
2525 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2527 if (nextBlockIndex == BLOCK_UNUSED)
2529 freeBlock = (depotIndex * blocksPerDepot) +
2530 (depotBlockOffset/sizeof(ULONG));
2533 depotBlockOffset += sizeof(ULONG);
2536 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2540 depotBlockOffset = 0;
2543 This->prevFreeBlock = freeBlock;
2548 /******************************************************************************
2549 * Storage32Impl_AddBlockDepot
2551 * This will create a depot block, essentially it is a block initialized
2554 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2558 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2561 * Initialize blocks as free
2563 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2565 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2568 /******************************************************************************
2569 * Storage32Impl_GetExtDepotBlock
2571 * Returns the index of the block that corresponds to the specified depot
2572 * index. This method is only for depot indexes equal or greater than
2573 * COUNT_BBDEPOTINHEADER.
2575 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2577 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2578 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2579 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2580 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2581 ULONG blockIndex = BLOCK_UNUSED;
2582 ULONG extBlockIndex = This->extBigBlockDepotStart;
2584 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2586 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2587 return BLOCK_UNUSED;
2589 while (extBlockCount > 0)
2591 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2595 if (extBlockIndex != BLOCK_UNUSED)
2599 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2601 if (depotBuffer != 0)
2603 StorageUtl_ReadDWord(depotBuffer,
2604 extBlockOffset * sizeof(ULONG),
2607 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2614 /******************************************************************************
2615 * Storage32Impl_SetExtDepotBlock
2617 * Associates the specified block index to the specified depot index.
2618 * This method is only for depot indexes equal or greater than
2619 * COUNT_BBDEPOTINHEADER.
2621 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2625 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2626 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2627 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2628 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2629 ULONG extBlockIndex = This->extBigBlockDepotStart;
2631 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2633 while (extBlockCount > 0)
2635 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2639 if (extBlockIndex != BLOCK_UNUSED)
2643 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2645 if (depotBuffer != 0)
2647 StorageUtl_WriteDWord(depotBuffer,
2648 extBlockOffset * sizeof(ULONG),
2651 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2656 /******************************************************************************
2657 * Storage32Impl_AddExtBlockDepot
2659 * Creates an extended depot block.
2661 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2663 ULONG numExtBlocks = This->extBigBlockDepotCount;
2664 ULONG nextExtBlock = This->extBigBlockDepotStart;
2665 BYTE* depotBuffer = NULL;
2666 ULONG index = BLOCK_UNUSED;
2667 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2668 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2669 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2671 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2672 blocksPerDepotBlock;
2674 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2677 * The first extended block.
2679 This->extBigBlockDepotStart = index;
2685 * Follow the chain to the last one.
2687 for (i = 0; i < (numExtBlocks - 1); i++)
2689 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2693 * Add the new extended block to the chain.
2695 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2696 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2697 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2701 * Initialize this block.
2703 depotBuffer = StorageImpl_GetBigBlock(This, index);
2704 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2705 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2710 /******************************************************************************
2711 * Storage32Impl_FreeBigBlock
2713 * This method will flag the specified block as free in the big block depot.
2715 void StorageImpl_FreeBigBlock(
2719 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2721 if (blockIndex < This->prevFreeBlock)
2722 This->prevFreeBlock = blockIndex;
2725 /************************************************************************
2726 * Storage32Impl_GetNextBlockInChain
2728 * This method will retrieve the block index of the next big block in
2731 * Params: This - Pointer to the Storage object.
2732 * blockIndex - Index of the block to retrieve the chain
2734 * nextBlockIndex - receives the return value.
2736 * Returns: This method returns the index of the next block in the chain.
2737 * It will return the constants:
2738 * BLOCK_SPECIAL - If the block given was not part of a
2740 * BLOCK_END_OF_CHAIN - If the block given was the last in
2742 * BLOCK_UNUSED - If the block given was not past of a chain
2744 * BLOCK_EXTBBDEPOT - This block is part of the extended
2747 * See Windows documentation for more details on IStorage methods.
2749 HRESULT StorageImpl_GetNextBlockInChain(
2752 ULONG* nextBlockIndex)
2754 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2755 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2756 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2758 ULONG depotBlockIndexPos;
2761 *nextBlockIndex = BLOCK_SPECIAL;
2763 if(depotBlockCount >= This->bigBlockDepotCount)
2765 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2766 This->bigBlockDepotCount);
2767 return STG_E_READFAULT;
2771 * Cache the currently accessed depot block.
2773 if (depotBlockCount != This->indexBlockDepotCached)
2775 This->indexBlockDepotCached = depotBlockCount;
2777 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2779 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2784 * We have to look in the extended depot.
2786 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2789 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2792 return STG_E_READFAULT;
2794 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2796 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2797 This->blockDepotCached[index] = *nextBlockIndex;
2799 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2802 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2807 /******************************************************************************
2808 * Storage32Impl_GetNextExtendedBlock
2810 * Given an extended block this method will return the next extended block.
2813 * The last ULONG of an extended block is the block index of the next
2814 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2818 * - The index of the next extended block
2819 * - BLOCK_UNUSED: there is no next extended block.
2820 * - Any other return values denotes failure.
2822 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2824 ULONG nextBlockIndex = BLOCK_SPECIAL;
2825 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2828 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2832 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2834 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2837 return nextBlockIndex;
2840 /******************************************************************************
2841 * Storage32Impl_SetNextBlockInChain
2843 * This method will write the index of the specified block's next block
2844 * in the big block depot.
2846 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2849 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2850 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2851 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2854 void StorageImpl_SetNextBlockInChain(
2859 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2860 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2861 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2862 ULONG depotBlockIndexPos;
2865 assert(depotBlockCount < This->bigBlockDepotCount);
2866 assert(blockIndex != nextBlock);
2868 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2870 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2875 * We have to look in the extended depot.
2877 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2880 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2884 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2885 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2889 * Update the cached block depot, if necessary.
2891 if (depotBlockCount == This->indexBlockDepotCached)
2893 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2897 /******************************************************************************
2898 * Storage32Impl_LoadFileHeader
2900 * This method will read in the file header, i.e. big block index -1.
2902 HRESULT StorageImpl_LoadFileHeader(
2905 HRESULT hr = STG_E_FILENOTFOUND;
2906 void* headerBigBlock = NULL;
2910 * Get a pointer to the big block of data containing the header.
2912 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2915 * Extract the information from the header.
2917 if (headerBigBlock!=0)
2920 * Check for the "magic number" signature and return an error if it is not
2923 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2925 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2926 return STG_E_OLDFORMAT;
2929 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2931 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2932 return STG_E_INVALIDHEADER;
2935 StorageUtl_ReadWord(
2937 OFFSET_BIGBLOCKSIZEBITS,
2938 &This->bigBlockSizeBits);
2940 StorageUtl_ReadWord(
2942 OFFSET_SMALLBLOCKSIZEBITS,
2943 &This->smallBlockSizeBits);
2945 StorageUtl_ReadDWord(
2947 OFFSET_BBDEPOTCOUNT,
2948 &This->bigBlockDepotCount);
2950 StorageUtl_ReadDWord(
2952 OFFSET_ROOTSTARTBLOCK,
2953 &This->rootStartBlock);
2955 StorageUtl_ReadDWord(
2957 OFFSET_SBDEPOTSTART,
2958 &This->smallBlockDepotStart);
2960 StorageUtl_ReadDWord(
2962 OFFSET_EXTBBDEPOTSTART,
2963 &This->extBigBlockDepotStart);
2965 StorageUtl_ReadDWord(
2967 OFFSET_EXTBBDEPOTCOUNT,
2968 &This->extBigBlockDepotCount);
2970 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2972 StorageUtl_ReadDWord(
2974 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2975 &(This->bigBlockDepotStart[index]));
2979 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2983 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2984 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2988 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2989 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2993 * Right now, the code is making some assumptions about the size of the
2994 * blocks, just make sure they are what we're expecting.
2996 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2997 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2999 WARN("Broken OLE storage file\n");
3000 hr = STG_E_INVALIDHEADER;
3006 * Release the block.
3008 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3014 /******************************************************************************
3015 * Storage32Impl_SaveFileHeader
3017 * This method will save to the file the header, i.e. big block -1.
3019 void StorageImpl_SaveFileHeader(
3022 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3027 * Get a pointer to the big block of data containing the header.
3029 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3032 * If the block read failed, the file is probably new.
3037 * Initialize for all unknown fields.
3039 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3042 * Initialize the magic number.
3044 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3047 * And a bunch of things we don't know what they mean
3049 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3050 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3051 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3052 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3056 * Write the information to the header.
3058 StorageUtl_WriteWord(
3060 OFFSET_BIGBLOCKSIZEBITS,
3061 This->bigBlockSizeBits);
3063 StorageUtl_WriteWord(
3065 OFFSET_SMALLBLOCKSIZEBITS,
3066 This->smallBlockSizeBits);
3068 StorageUtl_WriteDWord(
3070 OFFSET_BBDEPOTCOUNT,
3071 This->bigBlockDepotCount);
3073 StorageUtl_WriteDWord(
3075 OFFSET_ROOTSTARTBLOCK,
3076 This->rootStartBlock);
3078 StorageUtl_WriteDWord(
3080 OFFSET_SBDEPOTSTART,
3081 This->smallBlockDepotStart);
3083 StorageUtl_WriteDWord(
3085 OFFSET_SBDEPOTCOUNT,
3086 This->smallBlockDepotChain ?
3087 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3089 StorageUtl_WriteDWord(
3091 OFFSET_EXTBBDEPOTSTART,
3092 This->extBigBlockDepotStart);
3094 StorageUtl_WriteDWord(
3096 OFFSET_EXTBBDEPOTCOUNT,
3097 This->extBigBlockDepotCount);
3099 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3101 StorageUtl_WriteDWord(
3103 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3104 (This->bigBlockDepotStart[index]));
3108 * Write the big block back to the file.
3110 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3113 /******************************************************************************
3114 * Storage32Impl_ReadProperty
3116 * This method will read the specified property from the property chain.
3118 BOOL StorageImpl_ReadProperty(
3121 StgProperty* buffer)
3123 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3124 ULARGE_INTEGER offsetInPropSet;
3125 BOOL readSuccessful;
3128 offsetInPropSet.u.HighPart = 0;
3129 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3131 readSuccessful = BlockChainStream_ReadAt(
3132 This->rootBlockChain,
3140 /* replace the name of root entry (often "Root Entry") by the file name */
3141 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3142 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3144 memset(buffer->name, 0, sizeof(buffer->name));
3148 PROPERTY_NAME_BUFFER_LEN );
3149 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3151 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3153 StorageUtl_ReadWord(
3155 OFFSET_PS_NAMELENGTH,
3156 &buffer->sizeOfNameString);
3158 StorageUtl_ReadDWord(
3160 OFFSET_PS_PREVIOUSPROP,
3161 &buffer->previousProperty);
3163 StorageUtl_ReadDWord(
3166 &buffer->nextProperty);
3168 StorageUtl_ReadDWord(
3171 &buffer->dirProperty);
3173 StorageUtl_ReadGUID(
3176 &buffer->propertyUniqueID);
3178 StorageUtl_ReadDWord(
3181 &buffer->timeStampS1);
3183 StorageUtl_ReadDWord(
3186 &buffer->timeStampD1);
3188 StorageUtl_ReadDWord(
3191 &buffer->timeStampS2);
3193 StorageUtl_ReadDWord(
3196 &buffer->timeStampD2);
3198 StorageUtl_ReadDWord(
3200 OFFSET_PS_STARTBLOCK,
3201 &buffer->startingBlock);
3203 StorageUtl_ReadDWord(
3206 &buffer->size.u.LowPart);
3208 buffer->size.u.HighPart = 0;
3211 return readSuccessful;
3214 /*********************************************************************
3215 * Write the specified property into the property chain
3217 BOOL StorageImpl_WriteProperty(
3220 StgProperty* buffer)
3222 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3223 ULARGE_INTEGER offsetInPropSet;
3224 BOOL writeSuccessful;
3227 offsetInPropSet.u.HighPart = 0;
3228 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3230 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3233 currentProperty + OFFSET_PS_NAME,
3235 PROPERTY_NAME_BUFFER_LEN );
3237 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3239 StorageUtl_WriteWord(
3241 OFFSET_PS_NAMELENGTH,
3242 buffer->sizeOfNameString);
3244 StorageUtl_WriteDWord(
3246 OFFSET_PS_PREVIOUSPROP,
3247 buffer->previousProperty);
3249 StorageUtl_WriteDWord(
3252 buffer->nextProperty);
3254 StorageUtl_WriteDWord(
3257 buffer->dirProperty);
3259 StorageUtl_WriteGUID(
3262 &buffer->propertyUniqueID);
3264 StorageUtl_WriteDWord(
3267 buffer->timeStampS1);
3269 StorageUtl_WriteDWord(
3272 buffer->timeStampD1);
3274 StorageUtl_WriteDWord(
3277 buffer->timeStampS2);
3279 StorageUtl_WriteDWord(
3282 buffer->timeStampD2);
3284 StorageUtl_WriteDWord(
3286 OFFSET_PS_STARTBLOCK,
3287 buffer->startingBlock);
3289 StorageUtl_WriteDWord(
3292 buffer->size.u.LowPart);
3294 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3299 return writeSuccessful;
3302 BOOL StorageImpl_ReadBigBlock(
3307 void* bigBlockBuffer;
3309 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3311 if (bigBlockBuffer!=0)
3313 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3315 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3323 BOOL StorageImpl_WriteBigBlock(
3328 void* bigBlockBuffer;
3330 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3332 if (bigBlockBuffer!=0)
3334 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3336 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3344 void* StorageImpl_GetROBigBlock(
3348 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3351 void* StorageImpl_GetBigBlock(
3355 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3358 void StorageImpl_ReleaseBigBlock(
3362 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3365 /******************************************************************************
3366 * Storage32Impl_SmallBlocksToBigBlocks
3368 * This method will convert a small block chain to a big block chain.
3369 * The small block chain will be destroyed.
3371 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3373 SmallBlockChainStream** ppsbChain)
3375 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3376 ULARGE_INTEGER size, offset;
3377 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3378 ULONG propertyIndex;
3379 BOOL successRead, successWrite;
3380 StgProperty chainProperty;
3382 BlockChainStream *bbTempChain = NULL;
3383 BlockChainStream *bigBlockChain = NULL;
3386 * Create a temporary big block chain that doesn't have
3387 * an associated property. This temporary chain will be
3388 * used to copy data from small blocks to big blocks.
3390 bbTempChain = BlockChainStream_Construct(This,
3393 if(!bbTempChain) return NULL;
3395 * Grow the big block chain.
3397 size = SmallBlockChainStream_GetSize(*ppsbChain);
3398 BlockChainStream_SetSize(bbTempChain, size);
3401 * Copy the contents of the small block chain to the big block chain
3402 * by small block size increments.
3404 offset.u.LowPart = 0;
3405 offset.u.HighPart = 0;
3409 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3412 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3414 DEF_SMALL_BLOCK_SIZE,
3417 cbTotalRead += cbRead;
3419 successWrite = BlockChainStream_WriteAt(bbTempChain,
3424 cbTotalWritten += cbWritten;
3426 offset.u.LowPart += This->smallBlockSize;
3428 } while (successRead && successWrite);
3429 HeapFree(GetProcessHeap(),0,buffer);
3431 assert(cbTotalRead == cbTotalWritten);
3434 * Destroy the small block chain.
3436 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3437 size.u.HighPart = 0;
3439 SmallBlockChainStream_SetSize(*ppsbChain, size);
3440 SmallBlockChainStream_Destroy(*ppsbChain);
3444 * Change the property information. This chain is now a big block chain
3445 * and it doesn't reside in the small blocks chain anymore.
3447 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3449 chainProperty.startingBlock = bbHeadOfChain;
3451 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3454 * Destroy the temporary propertyless big block chain.
3455 * Create a new big block chain associated with this property.
3457 BlockChainStream_Destroy(bbTempChain);
3458 bigBlockChain = BlockChainStream_Construct(This,
3462 return bigBlockChain;
3465 /******************************************************************************
3466 ** Storage32InternalImpl implementation
3469 StorageInternalImpl* StorageInternalImpl_Construct(
3470 StorageImpl* ancestorStorage,
3471 ULONG rootPropertyIndex)
3473 StorageInternalImpl* newStorage;
3476 * Allocate space for the new storage object
3478 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3482 memset(newStorage, 0, sizeof(StorageInternalImpl));
3485 * Initialize the virtual function table.
3487 newStorage->lpVtbl = &Storage32InternalImpl_Vtbl;
3488 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3491 * Keep the ancestor storage pointer and nail a reference to it.
3493 newStorage->ancestorStorage = ancestorStorage;
3494 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3497 * Keep the index of the root property set for this storage,
3499 newStorage->rootPropertySetIndex = rootPropertyIndex;
3507 void StorageInternalImpl_Destroy(
3508 StorageInternalImpl* This)
3510 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3511 HeapFree(GetProcessHeap(), 0, This);
3514 /******************************************************************************
3516 ** Storage32InternalImpl_Commit
3518 ** The non-root storages cannot be opened in transacted mode thus this function
3521 HRESULT WINAPI StorageInternalImpl_Commit(
3523 DWORD grfCommitFlags) /* [in] */
3528 /******************************************************************************
3530 ** Storage32InternalImpl_Revert
3532 ** The non-root storages cannot be opened in transacted mode thus this function
3535 HRESULT WINAPI StorageInternalImpl_Revert(
3541 /******************************************************************************
3542 ** IEnumSTATSTGImpl implementation
3545 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3546 StorageImpl* parentStorage,
3547 ULONG firstPropertyNode)
3549 IEnumSTATSTGImpl* newEnumeration;
3551 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3553 if (newEnumeration!=0)
3556 * Set-up the virtual function table and reference count.
3558 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3559 newEnumeration->ref = 0;
3562 * We want to nail-down the reference to the storage in case the
3563 * enumeration out-lives the storage in the client application.
3565 newEnumeration->parentStorage = parentStorage;
3566 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3568 newEnumeration->firstPropertyNode = firstPropertyNode;
3571 * Initialize the search stack
3573 newEnumeration->stackSize = 0;
3574 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3575 newEnumeration->stackToVisit =
3576 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3579 * Make sure the current node of the iterator is the first one.
3581 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3584 return newEnumeration;
3587 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3589 IStorage_Release((IStorage*)This->parentStorage);
3590 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3591 HeapFree(GetProcessHeap(), 0, This);
3594 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3595 IEnumSTATSTG* iface,
3599 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3602 * Perform a sanity check on the parameters.
3605 return E_INVALIDARG;
3608 * Initialize the return parameter.
3613 * Compare the riid with the interface IDs implemented by this object.
3615 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3617 *ppvObject = (IEnumSTATSTG*)This;
3619 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3621 *ppvObject = (IEnumSTATSTG*)This;
3625 * Check that we obtained an interface.
3627 if ((*ppvObject)==0)
3628 return E_NOINTERFACE;
3631 * Query Interface always increases the reference count by one when it is
3634 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3639 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3640 IEnumSTATSTG* iface)
3642 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3643 return InterlockedIncrement(&This->ref);
3646 ULONG WINAPI IEnumSTATSTGImpl_Release(
3647 IEnumSTATSTG* iface)
3649 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3653 newRef = InterlockedDecrement(&This->ref);
3656 * If the reference count goes down to 0, perform suicide.
3660 IEnumSTATSTGImpl_Destroy(This);
3666 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3667 IEnumSTATSTG* iface,
3670 ULONG* pceltFetched)
3672 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3674 StgProperty currentProperty;
3675 STATSTG* currentReturnStruct = rgelt;
3676 ULONG objectFetched = 0;
3677 ULONG currentSearchNode;
3680 * Perform a sanity check on the parameters.
3682 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3683 return E_INVALIDARG;
3686 * To avoid the special case, get another pointer to a ULONG value if
3687 * the caller didn't supply one.
3689 if (pceltFetched==0)
3690 pceltFetched = &objectFetched;
3693 * Start the iteration, we will iterate until we hit the end of the
3694 * linked list or until we hit the number of items to iterate through
3699 * Start with the node at the top of the stack.
3701 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3703 while ( ( *pceltFetched < celt) &&
3704 ( currentSearchNode!=PROPERTY_NULL) )
3707 * Remove the top node from the stack
3709 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3712 * Read the property from the storage.
3714 StorageImpl_ReadProperty(This->parentStorage,
3719 * Copy the information to the return buffer.
3721 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3726 * Step to the next item in the iteration
3729 currentReturnStruct++;
3732 * Push the next search node in the search stack.
3734 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3737 * continue the iteration.
3739 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3742 if (*pceltFetched == celt)
3749 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3750 IEnumSTATSTG* iface,
3753 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3755 StgProperty currentProperty;
3756 ULONG objectFetched = 0;
3757 ULONG currentSearchNode;
3760 * Start with the node at the top of the stack.
3762 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3764 while ( (objectFetched < celt) &&
3765 (currentSearchNode!=PROPERTY_NULL) )
3768 * Remove the top node from the stack
3770 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3773 * Read the property from the storage.
3775 StorageImpl_ReadProperty(This->parentStorage,
3780 * Step to the next item in the iteration
3785 * Push the next search node in the search stack.
3787 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3790 * continue the iteration.
3792 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3795 if (objectFetched == celt)
3801 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3802 IEnumSTATSTG* iface)
3804 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3806 StgProperty rootProperty;
3807 BOOL readSuccessful;
3810 * Re-initialize the search stack to an empty stack
3812 This->stackSize = 0;
3815 * Read the root property from the storage.
3817 readSuccessful = StorageImpl_ReadProperty(
3818 This->parentStorage,
3819 This->firstPropertyNode,
3824 assert(rootProperty.sizeOfNameString!=0);
3827 * Push the search node in the search stack.
3829 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3835 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3836 IEnumSTATSTG* iface,
3837 IEnumSTATSTG** ppenum)
3839 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3841 IEnumSTATSTGImpl* newClone;
3844 * Perform a sanity check on the parameters.
3847 return E_INVALIDARG;
3849 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3850 This->firstPropertyNode);
3854 * The new clone enumeration must point to the same current node as
3857 newClone->stackSize = This->stackSize ;
3858 newClone->stackMaxSize = This->stackMaxSize ;
3859 newClone->stackToVisit =
3860 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3863 newClone->stackToVisit,
3865 sizeof(ULONG) * newClone->stackSize);
3867 *ppenum = (IEnumSTATSTG*)newClone;
3870 * Don't forget to nail down a reference to the clone before
3873 IEnumSTATSTGImpl_AddRef(*ppenum);
3878 INT IEnumSTATSTGImpl_FindParentProperty(
3879 IEnumSTATSTGImpl *This,
3880 ULONG childProperty,
3881 StgProperty *currentProperty,
3884 ULONG currentSearchNode;
3888 * To avoid the special case, get another pointer to a ULONG value if
3889 * the caller didn't supply one.
3892 thisNodeId = &foundNode;
3895 * Start with the node at the top of the stack.
3897 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3900 while (currentSearchNode!=PROPERTY_NULL)
3903 * Store the current node in the returned parameters
3905 *thisNodeId = currentSearchNode;
3908 * Remove the top node from the stack
3910 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3913 * Read the property from the storage.
3915 StorageImpl_ReadProperty(
3916 This->parentStorage,
3920 if (currentProperty->previousProperty == childProperty)
3921 return PROPERTY_RELATION_PREVIOUS;
3923 else if (currentProperty->nextProperty == childProperty)
3924 return PROPERTY_RELATION_NEXT;
3926 else if (currentProperty->dirProperty == childProperty)
3927 return PROPERTY_RELATION_DIR;
3930 * Push the next search node in the search stack.
3932 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3935 * continue the iteration.
3937 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3940 return PROPERTY_NULL;
3943 ULONG IEnumSTATSTGImpl_FindProperty(
3944 IEnumSTATSTGImpl* This,
3945 const OLECHAR* lpszPropName,
3946 StgProperty* currentProperty)
3948 ULONG currentSearchNode;
3951 * Start with the node at the top of the stack.
3953 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3955 while (currentSearchNode!=PROPERTY_NULL)
3958 * Remove the top node from the stack
3960 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3963 * Read the property from the storage.
3965 StorageImpl_ReadProperty(This->parentStorage,
3969 if ( propertyNameCmp(
3970 (const OLECHAR*)currentProperty->name,
3971 (const OLECHAR*)lpszPropName) == 0)
3972 return currentSearchNode;
3975 * Push the next search node in the search stack.
3977 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3980 * continue the iteration.
3982 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3985 return PROPERTY_NULL;
3988 void IEnumSTATSTGImpl_PushSearchNode(
3989 IEnumSTATSTGImpl* This,
3992 StgProperty rootProperty;
3993 BOOL readSuccessful;
3996 * First, make sure we're not trying to push an unexisting node.
3998 if (nodeToPush==PROPERTY_NULL)
4002 * First push the node to the stack
4004 if (This->stackSize == This->stackMaxSize)
4006 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4008 This->stackToVisit = HeapReAlloc(
4012 sizeof(ULONG) * This->stackMaxSize);
4015 This->stackToVisit[This->stackSize] = nodeToPush;
4019 * Read the root property from the storage.
4021 readSuccessful = StorageImpl_ReadProperty(
4022 This->parentStorage,
4028 assert(rootProperty.sizeOfNameString!=0);
4031 * Push the previous search node in the search stack.
4033 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4037 ULONG IEnumSTATSTGImpl_PopSearchNode(
4038 IEnumSTATSTGImpl* This,
4043 if (This->stackSize == 0)
4044 return PROPERTY_NULL;
4046 topNode = This->stackToVisit[This->stackSize-1];
4054 /******************************************************************************
4055 ** StorageUtl implementation
4058 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4060 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4063 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4065 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4068 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4070 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4073 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4075 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4078 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4080 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4081 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4082 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4084 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4087 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4089 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4090 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4091 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4093 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4096 void StorageUtl_CopyPropertyToSTATSTG(
4097 STATSTG* destination,
4098 StgProperty* source,
4102 * The copy of the string occurs only when the flag is not set
4104 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4105 (source->name == NULL) ||
4106 (source->name[0] == 0) )
4108 destination->pwcsName = 0;
4112 destination->pwcsName =
4113 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4115 strcpyW((LPWSTR)destination->pwcsName, source->name);
4118 switch (source->propertyType)
4120 case PROPTYPE_STORAGE:
4122 destination->type = STGTY_STORAGE;
4124 case PROPTYPE_STREAM:
4125 destination->type = STGTY_STREAM;
4128 destination->type = STGTY_STREAM;
4132 destination->cbSize = source->size;
4134 currentReturnStruct->mtime = {0}; TODO
4135 currentReturnStruct->ctime = {0};
4136 currentReturnStruct->atime = {0};
4138 destination->grfMode = 0;
4139 destination->grfLocksSupported = 0;
4140 destination->clsid = source->propertyUniqueID;
4141 destination->grfStateBits = 0;
4142 destination->reserved = 0;
4145 /******************************************************************************
4146 ** BlockChainStream implementation
4149 BlockChainStream* BlockChainStream_Construct(
4150 StorageImpl* parentStorage,
4151 ULONG* headOfStreamPlaceHolder,
4152 ULONG propertyIndex)
4154 BlockChainStream* newStream;
4157 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4159 newStream->parentStorage = parentStorage;
4160 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4161 newStream->ownerPropertyIndex = propertyIndex;
4162 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4163 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4164 newStream->numBlocks = 0;
4166 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4168 while (blockIndex != BLOCK_END_OF_CHAIN)
4170 newStream->numBlocks++;
4171 newStream->tailIndex = blockIndex;
4173 if(FAILED(StorageImpl_GetNextBlockInChain(
4178 HeapFree(GetProcessHeap(), 0, newStream);
4186 void BlockChainStream_Destroy(BlockChainStream* This)
4188 HeapFree(GetProcessHeap(), 0, This);
4191 /******************************************************************************
4192 * BlockChainStream_GetHeadOfChain
4194 * Returns the head of this stream chain.
4195 * Some special chains don't have properties, their heads are kept in
4196 * This->headOfStreamPlaceHolder.
4199 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4201 StgProperty chainProperty;
4202 BOOL readSuccessful;
4204 if (This->headOfStreamPlaceHolder != 0)
4205 return *(This->headOfStreamPlaceHolder);
4207 if (This->ownerPropertyIndex != PROPERTY_NULL)
4209 readSuccessful = StorageImpl_ReadProperty(
4210 This->parentStorage,
4211 This->ownerPropertyIndex,
4216 return chainProperty.startingBlock;
4220 return BLOCK_END_OF_CHAIN;
4223 /******************************************************************************
4224 * BlockChainStream_GetCount
4226 * Returns the number of blocks that comprises this chain.
4227 * This is not the size of the stream as the last block may not be full!
4230 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4235 blockIndex = BlockChainStream_GetHeadOfChain(This);
4237 while (blockIndex != BLOCK_END_OF_CHAIN)
4241 if(FAILED(StorageImpl_GetNextBlockInChain(
4242 This->parentStorage,
4251 /******************************************************************************
4252 * BlockChainStream_ReadAt
4254 * Reads a specified number of bytes from this chain at the specified offset.
4255 * bytesRead may be NULL.
4256 * Failure will be returned if the specified number of bytes has not been read.
4258 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4259 ULARGE_INTEGER offset,
4264 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4265 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4266 ULONG bytesToReadInBuffer;
4269 BYTE* bigBlockBuffer;
4272 * Find the first block in the stream that contains part of the buffer.
4274 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4275 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4276 (blockNoInSequence < This->lastBlockNoInSequence) )
4278 blockIndex = BlockChainStream_GetHeadOfChain(This);
4279 This->lastBlockNoInSequence = blockNoInSequence;
4283 ULONG temp = blockNoInSequence;
4285 blockIndex = This->lastBlockNoInSequenceIndex;
4286 blockNoInSequence -= This->lastBlockNoInSequence;
4287 This->lastBlockNoInSequence = temp;
4290 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4292 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4294 blockNoInSequence--;
4297 This->lastBlockNoInSequenceIndex = blockIndex;
4300 * Start reading the buffer.
4303 bufferWalker = buffer;
4305 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4308 * Calculate how many bytes we can copy from this big block.
4310 bytesToReadInBuffer =
4311 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4314 * Copy those bytes to the buffer
4317 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4319 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4321 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4324 * Step to the next big block.
4326 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4329 bufferWalker += bytesToReadInBuffer;
4330 size -= bytesToReadInBuffer;
4331 *bytesRead += bytesToReadInBuffer;
4332 offsetInBlock = 0; /* There is no offset on the next block */
4339 /******************************************************************************
4340 * BlockChainStream_WriteAt
4342 * Writes the specified number of bytes to this chain at the specified offset.
4343 * bytesWritten may be NULL.
4344 * Will fail if not all specified number of bytes have been written.
4346 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4347 ULARGE_INTEGER offset,
4350 ULONG* bytesWritten)
4352 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4353 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4356 const BYTE* bufferWalker;
4357 BYTE* bigBlockBuffer;
4360 * Find the first block in the stream that contains part of the buffer.
4362 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4363 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4364 (blockNoInSequence < This->lastBlockNoInSequence) )
4366 blockIndex = BlockChainStream_GetHeadOfChain(This);
4367 This->lastBlockNoInSequence = blockNoInSequence;
4371 ULONG temp = blockNoInSequence;
4373 blockIndex = This->lastBlockNoInSequenceIndex;
4374 blockNoInSequence -= This->lastBlockNoInSequence;
4375 This->lastBlockNoInSequence = temp;
4378 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4380 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4383 blockNoInSequence--;
4386 This->lastBlockNoInSequenceIndex = blockIndex;
4389 * Here, I'm casting away the constness on the buffer variable
4390 * This is OK since we don't intend to modify that buffer.
4393 bufferWalker = (const BYTE*)buffer;
4395 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4398 * Calculate how many bytes we can copy from this big block.
4401 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4404 * Copy those bytes to the buffer
4406 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4408 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4410 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4413 * Step to the next big block.
4415 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4418 bufferWalker += bytesToWrite;
4419 size -= bytesToWrite;
4420 *bytesWritten += bytesToWrite;
4421 offsetInBlock = 0; /* There is no offset on the next block */
4427 /******************************************************************************
4428 * BlockChainStream_Shrink
4430 * Shrinks this chain in the big block depot.
4432 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4433 ULARGE_INTEGER newSize)
4435 ULONG blockIndex, extraBlock;
4440 * Reset the last accessed block cache.
4442 This->lastBlockNoInSequence = 0xFFFFFFFF;
4443 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4446 * Figure out how many blocks are needed to contain the new size
4448 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4450 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4453 blockIndex = BlockChainStream_GetHeadOfChain(This);
4456 * Go to the new end of chain
4458 while (count < numBlocks)
4460 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4466 /* Get the next block before marking the new end */
4467 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4471 /* Mark the new end of chain */
4472 StorageImpl_SetNextBlockInChain(
4473 This->parentStorage,
4475 BLOCK_END_OF_CHAIN);
4477 This->tailIndex = blockIndex;
4478 This->numBlocks = numBlocks;
4481 * Mark the extra blocks as free
4483 while (extraBlock != BLOCK_END_OF_CHAIN)
4485 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4488 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4489 extraBlock = blockIndex;
4495 /******************************************************************************
4496 * BlockChainStream_Enlarge
4498 * Grows this chain in the big block depot.
4500 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4501 ULARGE_INTEGER newSize)
4503 ULONG blockIndex, currentBlock;
4505 ULONG oldNumBlocks = 0;
4507 blockIndex = BlockChainStream_GetHeadOfChain(This);
4510 * Empty chain. Create the head.
4512 if (blockIndex == BLOCK_END_OF_CHAIN)
4514 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4515 StorageImpl_SetNextBlockInChain(This->parentStorage,
4517 BLOCK_END_OF_CHAIN);
4519 if (This->headOfStreamPlaceHolder != 0)
4521 *(This->headOfStreamPlaceHolder) = blockIndex;
4525 StgProperty chainProp;
4526 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4528 StorageImpl_ReadProperty(
4529 This->parentStorage,
4530 This->ownerPropertyIndex,
4533 chainProp.startingBlock = blockIndex;
4535 StorageImpl_WriteProperty(
4536 This->parentStorage,
4537 This->ownerPropertyIndex,
4541 This->tailIndex = blockIndex;
4542 This->numBlocks = 1;
4546 * Figure out how many blocks are needed to contain this stream
4548 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4550 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4554 * Go to the current end of chain
4556 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4558 currentBlock = blockIndex;
4560 while (blockIndex != BLOCK_END_OF_CHAIN)
4563 currentBlock = blockIndex;
4565 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4570 This->tailIndex = currentBlock;
4573 currentBlock = This->tailIndex;
4574 oldNumBlocks = This->numBlocks;
4577 * Add new blocks to the chain
4579 if (oldNumBlocks < newNumBlocks)
4581 while (oldNumBlocks < newNumBlocks)
4583 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4585 StorageImpl_SetNextBlockInChain(
4586 This->parentStorage,
4590 StorageImpl_SetNextBlockInChain(
4591 This->parentStorage,
4593 BLOCK_END_OF_CHAIN);
4595 currentBlock = blockIndex;
4599 This->tailIndex = blockIndex;
4600 This->numBlocks = newNumBlocks;
4606 /******************************************************************************
4607 * BlockChainStream_SetSize
4609 * Sets the size of this stream. The big block depot will be updated.
4610 * The file will grow if we grow the chain.
4612 * TODO: Free the actual blocks in the file when we shrink the chain.
4613 * Currently, the blocks are still in the file. So the file size
4614 * doesn't shrink even if we shrink streams.
4616 BOOL BlockChainStream_SetSize(
4617 BlockChainStream* This,
4618 ULARGE_INTEGER newSize)
4620 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4622 if (newSize.u.LowPart == size.u.LowPart)
4625 if (newSize.u.LowPart < size.u.LowPart)
4627 BlockChainStream_Shrink(This, newSize);
4631 ULARGE_INTEGER fileSize =
4632 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4634 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4637 * Make sure the file stays a multiple of blocksize
4639 if ((diff % This->parentStorage->bigBlockSize) != 0)
4640 diff += (This->parentStorage->bigBlockSize -
4641 (diff % This->parentStorage->bigBlockSize) );
4643 fileSize.u.LowPart += diff;
4644 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4646 BlockChainStream_Enlarge(This, newSize);
4652 /******************************************************************************
4653 * BlockChainStream_GetSize
4655 * Returns the size of this chain.
4656 * Will return the block count if this chain doesn't have a property.
4658 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4660 StgProperty chainProperty;
4662 if(This->headOfStreamPlaceHolder == NULL)
4665 * This chain is a data stream read the property and return
4666 * the appropriate size
4668 StorageImpl_ReadProperty(
4669 This->parentStorage,
4670 This->ownerPropertyIndex,
4673 return chainProperty.size;
4678 * this chain is a chain that does not have a property, figure out the
4679 * size by making the product number of used blocks times the
4682 ULARGE_INTEGER result;
4683 result.u.HighPart = 0;
4686 BlockChainStream_GetCount(This) *
4687 This->parentStorage->bigBlockSize;
4693 /******************************************************************************
4694 ** SmallBlockChainStream implementation
4697 SmallBlockChainStream* SmallBlockChainStream_Construct(
4698 StorageImpl* parentStorage,
4699 ULONG propertyIndex)
4701 SmallBlockChainStream* newStream;
4703 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4705 newStream->parentStorage = parentStorage;
4706 newStream->ownerPropertyIndex = propertyIndex;
4711 void SmallBlockChainStream_Destroy(
4712 SmallBlockChainStream* This)
4714 HeapFree(GetProcessHeap(), 0, This);
4717 /******************************************************************************
4718 * SmallBlockChainStream_GetHeadOfChain
4720 * Returns the head of this chain of small blocks.
4722 ULONG SmallBlockChainStream_GetHeadOfChain(
4723 SmallBlockChainStream* This)
4725 StgProperty chainProperty;
4726 BOOL readSuccessful;
4728 if (This->ownerPropertyIndex)
4730 readSuccessful = StorageImpl_ReadProperty(
4731 This->parentStorage,
4732 This->ownerPropertyIndex,
4737 return chainProperty.startingBlock;
4742 return BLOCK_END_OF_CHAIN;
4745 /******************************************************************************
4746 * SmallBlockChainStream_GetNextBlockInChain
4748 * Returns the index of the next small block in this chain.
4751 * - BLOCK_END_OF_CHAIN: end of this chain
4752 * - BLOCK_UNUSED: small block 'blockIndex' is free
4754 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4755 SmallBlockChainStream* This,
4757 ULONG* nextBlockInChain)
4759 ULARGE_INTEGER offsetOfBlockInDepot;
4764 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4766 offsetOfBlockInDepot.u.HighPart = 0;
4767 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4770 * Read those bytes in the buffer from the small block file.
4772 success = BlockChainStream_ReadAt(
4773 This->parentStorage->smallBlockDepotChain,
4774 offsetOfBlockInDepot,
4781 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4785 return STG_E_READFAULT;
4788 /******************************************************************************
4789 * SmallBlockChainStream_SetNextBlockInChain
4791 * Writes the index of the next block of the specified block in the small
4793 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4794 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4796 void SmallBlockChainStream_SetNextBlockInChain(
4797 SmallBlockChainStream* This,
4801 ULARGE_INTEGER offsetOfBlockInDepot;
4805 offsetOfBlockInDepot.u.HighPart = 0;
4806 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4808 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4811 * Read those bytes in the buffer from the small block file.
4813 BlockChainStream_WriteAt(
4814 This->parentStorage->smallBlockDepotChain,
4815 offsetOfBlockInDepot,
4821 /******************************************************************************
4822 * SmallBlockChainStream_FreeBlock
4824 * Flag small block 'blockIndex' as free in the small block depot.
4826 void SmallBlockChainStream_FreeBlock(
4827 SmallBlockChainStream* This,
4830 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4833 /******************************************************************************
4834 * SmallBlockChainStream_GetNextFreeBlock
4836 * Returns the index of a free small block. The small block depot will be
4837 * enlarged if necessary. The small block chain will also be enlarged if
4840 ULONG SmallBlockChainStream_GetNextFreeBlock(
4841 SmallBlockChainStream* This)
4843 ULARGE_INTEGER offsetOfBlockInDepot;
4846 ULONG blockIndex = 0;
4847 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4848 BOOL success = TRUE;
4849 ULONG smallBlocksPerBigBlock;
4851 offsetOfBlockInDepot.u.HighPart = 0;
4854 * Scan the small block depot for a free block
4856 while (nextBlockIndex != BLOCK_UNUSED)
4858 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4860 success = BlockChainStream_ReadAt(
4861 This->parentStorage->smallBlockDepotChain,
4862 offsetOfBlockInDepot,
4868 * If we run out of space for the small block depot, enlarge it
4872 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4874 if (nextBlockIndex != BLOCK_UNUSED)
4880 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4882 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4883 ULONG nextBlock, newsbdIndex;
4884 BYTE* smallBlockDepot;
4886 nextBlock = sbdIndex;
4887 while (nextBlock != BLOCK_END_OF_CHAIN)
4889 sbdIndex = nextBlock;
4890 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4893 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4894 if (sbdIndex != BLOCK_END_OF_CHAIN)
4895 StorageImpl_SetNextBlockInChain(
4896 This->parentStorage,
4900 StorageImpl_SetNextBlockInChain(
4901 This->parentStorage,
4903 BLOCK_END_OF_CHAIN);
4906 * Initialize all the small blocks to free
4909 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4911 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4912 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4917 * We have just created the small block depot.
4919 StgProperty rootProp;
4923 * Save it in the header
4925 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4926 StorageImpl_SaveFileHeader(This->parentStorage);
4929 * And allocate the first big block that will contain small blocks
4932 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4934 StorageImpl_SetNextBlockInChain(
4935 This->parentStorage,
4937 BLOCK_END_OF_CHAIN);
4939 StorageImpl_ReadProperty(
4940 This->parentStorage,
4941 This->parentStorage->rootPropertySetIndex,
4944 rootProp.startingBlock = sbStartIndex;
4945 rootProp.size.u.HighPart = 0;
4946 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
4948 StorageImpl_WriteProperty(
4949 This->parentStorage,
4950 This->parentStorage->rootPropertySetIndex,
4956 smallBlocksPerBigBlock =
4957 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4960 * Verify if we have to allocate big blocks to contain small blocks
4962 if (blockIndex % smallBlocksPerBigBlock == 0)
4964 StgProperty rootProp;
4965 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4967 StorageImpl_ReadProperty(
4968 This->parentStorage,
4969 This->parentStorage->rootPropertySetIndex,
4972 if (rootProp.size.u.LowPart <
4973 (blocksRequired * This->parentStorage->bigBlockSize))
4975 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
4977 BlockChainStream_SetSize(
4978 This->parentStorage->smallBlockRootChain,
4981 StorageImpl_WriteProperty(
4982 This->parentStorage,
4983 This->parentStorage->rootPropertySetIndex,
4991 /******************************************************************************
4992 * SmallBlockChainStream_ReadAt
4994 * Reads a specified number of bytes from this chain at the specified offset.
4995 * bytesRead may be NULL.
4996 * Failure will be returned if the specified number of bytes has not been read.
4998 BOOL SmallBlockChainStream_ReadAt(
4999 SmallBlockChainStream* This,
5000 ULARGE_INTEGER offset,
5005 ULARGE_INTEGER offsetInBigBlockFile;
5006 ULONG blockNoInSequence =
5007 offset.u.LowPart / This->parentStorage->smallBlockSize;
5009 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5010 ULONG bytesToReadInBuffer;
5012 ULONG bytesReadFromBigBlockFile;
5016 * This should never happen on a small block file.
5018 assert(offset.u.HighPart==0);
5021 * Find the first block in the stream that contains part of the buffer.
5023 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5025 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5027 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5030 blockNoInSequence--;
5034 * Start reading the buffer.
5037 bufferWalker = buffer;
5039 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5042 * Calculate how many bytes we can copy from this small block.
5044 bytesToReadInBuffer =
5045 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5048 * Calculate the offset of the small block in the small block file.
5050 offsetInBigBlockFile.u.HighPart = 0;
5051 offsetInBigBlockFile.u.LowPart =
5052 blockIndex * This->parentStorage->smallBlockSize;
5054 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5057 * Read those bytes in the buffer from the small block file.
5059 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5060 offsetInBigBlockFile,
5061 bytesToReadInBuffer,
5063 &bytesReadFromBigBlockFile);
5065 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5068 * Step to the next big block.
5070 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5072 bufferWalker += bytesToReadInBuffer;
5073 size -= bytesToReadInBuffer;
5074 *bytesRead += bytesToReadInBuffer;
5075 offsetInBlock = 0; /* There is no offset on the next block */
5081 /******************************************************************************
5082 * SmallBlockChainStream_WriteAt
5084 * Writes the specified number of bytes to this chain at the specified offset.
5085 * bytesWritten may be NULL.
5086 * Will fail if not all specified number of bytes have been written.
5088 BOOL SmallBlockChainStream_WriteAt(
5089 SmallBlockChainStream* This,
5090 ULARGE_INTEGER offset,
5093 ULONG* bytesWritten)
5095 ULARGE_INTEGER offsetInBigBlockFile;
5096 ULONG blockNoInSequence =
5097 offset.u.LowPart / This->parentStorage->smallBlockSize;
5099 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5100 ULONG bytesToWriteInBuffer;
5102 ULONG bytesWrittenFromBigBlockFile;
5103 const BYTE* bufferWalker;
5106 * This should never happen on a small block file.
5108 assert(offset.u.HighPart==0);
5111 * Find the first block in the stream that contains part of the buffer.
5113 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5115 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5117 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5119 blockNoInSequence--;
5123 * Start writing the buffer.
5125 * Here, I'm casting away the constness on the buffer variable
5126 * This is OK since we don't intend to modify that buffer.
5129 bufferWalker = (const BYTE*)buffer;
5130 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5133 * Calculate how many bytes we can copy to this small block.
5135 bytesToWriteInBuffer =
5136 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5139 * Calculate the offset of the small block in the small block file.
5141 offsetInBigBlockFile.u.HighPart = 0;
5142 offsetInBigBlockFile.u.LowPart =
5143 blockIndex * This->parentStorage->smallBlockSize;
5145 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5148 * Write those bytes in the buffer to the small block file.
5150 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5151 offsetInBigBlockFile,
5152 bytesToWriteInBuffer,
5154 &bytesWrittenFromBigBlockFile);
5156 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5159 * Step to the next big block.
5161 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5164 bufferWalker += bytesToWriteInBuffer;
5165 size -= bytesToWriteInBuffer;
5166 *bytesWritten += bytesToWriteInBuffer;
5167 offsetInBlock = 0; /* There is no offset on the next block */
5173 /******************************************************************************
5174 * SmallBlockChainStream_Shrink
5176 * Shrinks this chain in the small block depot.
5178 BOOL SmallBlockChainStream_Shrink(
5179 SmallBlockChainStream* This,
5180 ULARGE_INTEGER newSize)
5182 ULONG blockIndex, extraBlock;
5186 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5188 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5191 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5194 * Go to the new end of chain
5196 while (count < numBlocks)
5198 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5205 * If the count is 0, we have a special case, the head of the chain was
5210 StgProperty chainProp;
5212 StorageImpl_ReadProperty(This->parentStorage,
5213 This->ownerPropertyIndex,
5216 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5218 StorageImpl_WriteProperty(This->parentStorage,
5219 This->ownerPropertyIndex,
5223 * We start freeing the chain at the head block.
5225 extraBlock = blockIndex;
5229 /* Get the next block before marking the new end */
5230 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5234 /* Mark the new end of chain */
5235 SmallBlockChainStream_SetNextBlockInChain(
5238 BLOCK_END_OF_CHAIN);
5242 * Mark the extra blocks as free
5244 while (extraBlock != BLOCK_END_OF_CHAIN)
5246 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5249 SmallBlockChainStream_FreeBlock(This, extraBlock);
5250 extraBlock = blockIndex;
5256 /******************************************************************************
5257 * SmallBlockChainStream_Enlarge
5259 * Grows this chain in the small block depot.
5261 BOOL SmallBlockChainStream_Enlarge(
5262 SmallBlockChainStream* This,
5263 ULARGE_INTEGER newSize)
5265 ULONG blockIndex, currentBlock;
5267 ULONG oldNumBlocks = 0;
5269 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5274 if (blockIndex == BLOCK_END_OF_CHAIN)
5277 StgProperty chainProp;
5279 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5282 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5284 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5287 blockIndex = chainProp.startingBlock;
5288 SmallBlockChainStream_SetNextBlockInChain(
5291 BLOCK_END_OF_CHAIN);
5294 currentBlock = blockIndex;
5297 * Figure out how many blocks are needed to contain this stream
5299 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5301 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5305 * Go to the current end of chain
5307 while (blockIndex != BLOCK_END_OF_CHAIN)
5310 currentBlock = blockIndex;
5311 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5316 * Add new blocks to the chain
5318 while (oldNumBlocks < newNumBlocks)
5320 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5321 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5323 SmallBlockChainStream_SetNextBlockInChain(
5326 BLOCK_END_OF_CHAIN);
5328 currentBlock = blockIndex;
5335 /******************************************************************************
5336 * SmallBlockChainStream_GetCount
5338 * Returns the number of blocks that comprises this chain.
5339 * This is not the size of this chain as the last block may not be full!
5341 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5346 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5348 while (blockIndex != BLOCK_END_OF_CHAIN)
5352 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5359 /******************************************************************************
5360 * SmallBlockChainStream_SetSize
5362 * Sets the size of this stream.
5363 * The file will grow if we grow the chain.
5365 * TODO: Free the actual blocks in the file when we shrink the chain.
5366 * Currently, the blocks are still in the file. So the file size
5367 * doesn't shrink even if we shrink streams.
5369 BOOL SmallBlockChainStream_SetSize(
5370 SmallBlockChainStream* This,
5371 ULARGE_INTEGER newSize)
5373 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5375 if (newSize.u.LowPart == size.u.LowPart)
5378 if (newSize.u.LowPart < size.u.LowPart)
5380 SmallBlockChainStream_Shrink(This, newSize);
5384 SmallBlockChainStream_Enlarge(This, newSize);
5390 /******************************************************************************
5391 * SmallBlockChainStream_GetSize
5393 * Returns the size of this chain.
5395 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5397 StgProperty chainProperty;
5399 StorageImpl_ReadProperty(
5400 This->parentStorage,
5401 This->ownerPropertyIndex,
5404 return chainProperty.size;
5407 /******************************************************************************
5408 * StgCreateDocfile [OLE32.@]
5410 HRESULT WINAPI StgCreateDocfile(
5414 IStorage **ppstgOpen)
5416 StorageImpl* newStorage = 0;
5417 HANDLE hFile = INVALID_HANDLE_VALUE;
5422 DWORD fileAttributes;
5423 WCHAR tempFileName[MAX_PATH];
5425 TRACE("(%s, %lx, %ld, %p)\n",
5426 debugstr_w(pwcsName), grfMode,
5427 reserved, ppstgOpen);
5430 * Validate the parameters
5433 return STG_E_INVALIDPOINTER;
5435 return STG_E_INVALIDPARAMETER;
5438 * Validate the STGM flags
5440 if ( FAILED( validateSTGM(grfMode) ))
5441 return STG_E_INVALIDFLAG;
5443 /* StgCreateDocFile always opens for write */
5444 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5445 return STG_E_INVALIDFLAG;
5447 /* always opens non-shared */
5448 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5449 return STG_E_INVALIDFLAG;
5452 * Generate a unique name.
5456 WCHAR tempPath[MAX_PATH];
5457 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5459 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5460 return STG_E_INVALIDFLAG;
5461 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5462 return STG_E_INVALIDFLAG;
5464 memset(tempPath, 0, sizeof(tempPath));
5465 memset(tempFileName, 0, sizeof(tempFileName));
5467 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5470 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5471 pwcsName = tempFileName;
5473 return STG_E_INSUFFICIENTMEMORY;
5475 creationMode = TRUNCATE_EXISTING;
5479 creationMode = GetCreationModeFromSTGM(grfMode);
5483 * Interpret the STGM value grfMode
5485 shareMode = GetShareModeFromSTGM(grfMode);
5486 accessMode = GetAccessModeFromSTGM(grfMode);
5488 if (grfMode & STGM_DELETEONRELEASE)
5489 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5491 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5493 if (grfMode & STGM_TRANSACTED)
5494 FIXME("Transacted mode not implemented.\n");
5497 * Initialize the "out" parameter.
5501 hFile = CreateFileW(pwcsName,
5509 if (hFile == INVALID_HANDLE_VALUE)
5511 if(GetLastError() == ERROR_FILE_EXISTS)
5512 return STG_E_FILEALREADYEXISTS;
5517 * Allocate and initialize the new IStorage32object.
5519 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5521 if (newStorage == 0)
5522 return STG_E_INSUFFICIENTMEMORY;
5524 hr = StorageImpl_Construct(
5535 HeapFree(GetProcessHeap(), 0, newStorage);
5540 * Get an "out" pointer for the caller.
5542 hr = StorageBaseImpl_QueryInterface(
5543 (IStorage*)newStorage,
5544 (REFIID)&IID_IStorage,
5550 /******************************************************************************
5551 * StgCreateStorageEx [OLE32.@]
5553 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5555 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5556 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5557 return STG_E_UNIMPLEMENTEDFUNCTION;
5560 /******************************************************************************
5561 * StgOpenStorage [OLE32.@]
5563 HRESULT WINAPI StgOpenStorage(
5564 const OLECHAR *pwcsName,
5565 IStorage *pstgPriority,
5569 IStorage **ppstgOpen)
5571 StorageImpl* newStorage = 0;
5576 WCHAR fullname[MAX_PATH];
5579 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5580 debugstr_w(pwcsName), pstgPriority, grfMode,
5581 snbExclude, reserved, ppstgOpen);
5584 * Perform sanity checks
5588 hr = STG_E_INVALIDNAME;
5594 hr = STG_E_INVALIDPOINTER;
5600 hr = STG_E_INVALIDPARAMETER;
5605 * Validate the sharing mode
5607 switch(STGM_SHARE_MODE(grfMode))
5609 case STGM_SHARE_EXCLUSIVE:
5610 case STGM_SHARE_DENY_WRITE:
5613 hr = STG_E_INVALIDFLAG;
5618 * Validate the STGM flags
5620 if ( FAILED( validateSTGM(grfMode) ) ||
5621 (grfMode&STGM_CREATE))
5623 hr = STG_E_INVALIDFLAG;
5628 * Interpret the STGM value grfMode
5630 shareMode = GetShareModeFromSTGM(grfMode);
5631 accessMode = GetAccessModeFromSTGM(grfMode);
5634 * Initialize the "out" parameter.
5638 hFile = CreateFileW( pwcsName,
5643 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5646 if (hFile==INVALID_HANDLE_VALUE)
5648 DWORD last_error = GetLastError();
5654 case ERROR_FILE_NOT_FOUND:
5655 hr = STG_E_FILENOTFOUND;
5658 case ERROR_PATH_NOT_FOUND:
5659 hr = STG_E_PATHNOTFOUND;
5662 case ERROR_ACCESS_DENIED:
5663 case ERROR_WRITE_PROTECT:
5664 hr = STG_E_ACCESSDENIED;
5667 case ERROR_SHARING_VIOLATION:
5668 hr = STG_E_SHAREVIOLATION;
5678 length = GetFileSize(hFile, NULL);
5681 * Allocate and initialize the new IStorage32object.
5683 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5685 if (newStorage == 0)
5687 hr = STG_E_INSUFFICIENTMEMORY;
5691 /* if the file's length was zero, initialize the storage */
5692 hr = StorageImpl_Construct(
5703 HeapFree(GetProcessHeap(), 0, newStorage);
5705 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5707 if(hr == STG_E_INVALIDHEADER)
5708 hr = STG_E_FILEALREADYEXISTS;
5712 /* prepare the file name string given in lieu of the root property name */
5713 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5714 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5715 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5718 * Get an "out" pointer for the caller.
5720 hr = StorageBaseImpl_QueryInterface(
5721 (IStorage*)newStorage,
5722 (REFIID)&IID_IStorage,
5726 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5730 /******************************************************************************
5731 * StgCreateDocfileOnILockBytes [OLE32.@]
5733 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5737 IStorage** ppstgOpen)
5739 StorageImpl* newStorage = 0;
5743 * Validate the parameters
5745 if ((ppstgOpen == 0) || (plkbyt == 0))
5746 return STG_E_INVALIDPOINTER;
5749 * Allocate and initialize the new IStorage object.
5751 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5753 if (newStorage == 0)
5754 return STG_E_INSUFFICIENTMEMORY;
5756 hr = StorageImpl_Construct(
5767 HeapFree(GetProcessHeap(), 0, newStorage);
5772 * Get an "out" pointer for the caller.
5774 hr = StorageBaseImpl_QueryInterface(
5775 (IStorage*)newStorage,
5776 (REFIID)&IID_IStorage,
5782 /******************************************************************************
5783 * StgOpenStorageOnILockBytes [OLE32.@]
5785 HRESULT WINAPI StgOpenStorageOnILockBytes(
5787 IStorage *pstgPriority,
5791 IStorage **ppstgOpen)
5793 StorageImpl* newStorage = 0;
5797 * Perform a sanity check
5799 if ((plkbyt == 0) || (ppstgOpen == 0))
5800 return STG_E_INVALIDPOINTER;
5803 * Validate the STGM flags
5805 if ( FAILED( validateSTGM(grfMode) ))
5806 return STG_E_INVALIDFLAG;
5809 * Initialize the "out" parameter.
5814 * Allocate and initialize the new IStorage object.
5816 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5818 if (newStorage == 0)
5819 return STG_E_INSUFFICIENTMEMORY;
5821 hr = StorageImpl_Construct(
5832 HeapFree(GetProcessHeap(), 0, newStorage);
5837 * Get an "out" pointer for the caller.
5839 hr = StorageBaseImpl_QueryInterface(
5840 (IStorage*)newStorage,
5841 (REFIID)&IID_IStorage,
5847 /******************************************************************************
5848 * StgSetTimes [ole32.@]
5849 * StgSetTimes [OLE32.@]
5853 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *a,
5854 FILETIME const *b, FILETIME const *c )
5856 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5860 /******************************************************************************
5861 * StgIsStorageILockBytes [OLE32.@]
5863 * Determines if the ILockBytes contains a storage object.
5865 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5868 ULARGE_INTEGER offset;
5870 offset.u.HighPart = 0;
5871 offset.u.LowPart = 0;
5873 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5875 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5881 /******************************************************************************
5882 * WriteClassStg [OLE32.@]
5884 * This method will store the specified CLSID in the specified storage object
5886 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5892 hRes = IStorage_SetClass(pStg, rclsid);
5897 /***********************************************************************
5898 * ReadClassStg (OLE32.@)
5900 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5902 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5912 * read a STATSTG structure (contains the clsid) from the storage
5914 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5917 *pclsid=pstatstg.clsid;
5922 /***********************************************************************
5923 * OleLoadFromStream (OLE32.@)
5925 * This function loads an object from stream
5927 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5931 LPPERSISTSTREAM xstm;
5933 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5935 res=ReadClassStm(pStm,&clsid);
5936 if (!SUCCEEDED(res))
5938 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5939 if (!SUCCEEDED(res))
5941 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5942 if (!SUCCEEDED(res)) {
5943 IUnknown_Release((IUnknown*)*ppvObj);
5946 res=IPersistStream_Load(xstm,pStm);
5947 IPersistStream_Release(xstm);
5948 /* FIXME: all refcounts ok at this point? I think they should be:
5951 * xstm : 0 (released)
5956 /***********************************************************************
5957 * OleSaveToStream (OLE32.@)
5959 * This function saves an object with the IPersistStream interface on it
5960 * to the specified stream.
5962 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5968 TRACE("(%p,%p)\n",pPStm,pStm);
5970 res=IPersistStream_GetClassID(pPStm,&clsid);
5972 if (SUCCEEDED(res)){
5974 res=WriteClassStm(pStm,&clsid);
5978 res=IPersistStream_Save(pPStm,pStm,TRUE);
5981 TRACE("Finished Save\n");
5985 /****************************************************************************
5986 * This method validate a STGM parameter that can contain the values below
5988 * STGM_DIRECT 0x00000000
5989 * STGM_TRANSACTED 0x00010000
5990 * STGM_SIMPLE 0x08000000
5992 * STGM_READ 0x00000000
5993 * STGM_WRITE 0x00000001
5994 * STGM_READWRITE 0x00000002
5996 * STGM_SHARE_DENY_NONE 0x00000040
5997 * STGM_SHARE_DENY_READ 0x00000030
5998 * STGM_SHARE_DENY_WRITE 0x00000020
5999 * STGM_SHARE_EXCLUSIVE 0x00000010
6001 * STGM_PRIORITY 0x00040000
6002 * STGM_DELETEONRELEASE 0x04000000
6004 * STGM_CREATE 0x00001000
6005 * STGM_CONVERT 0x00020000
6006 * STGM_FAILIFTHERE 0x00000000
6008 * STGM_NOSCRATCH 0x00100000
6009 * STGM_NOSNAPSHOT 0x00200000
6011 static HRESULT validateSTGM(DWORD stgm)
6013 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
6014 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
6015 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
6017 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6018 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6019 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6021 BOOL bSTGM_SHARE_DENY_NONE =
6022 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6024 BOOL bSTGM_SHARE_DENY_READ =
6025 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6027 BOOL bSTGM_SHARE_DENY_WRITE =
6028 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6030 BOOL bSTGM_SHARE_EXCLUSIVE =
6031 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6033 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
6034 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
6036 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
6037 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
6040 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6042 if ( ! bSTGM_DIRECT )
6043 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
6047 * STGM_WRITE | STGM_READWRITE | STGM_READ
6050 if( bSTGM_WRITE && bSTGM_READWRITE )
6054 * STGM_SHARE_DENY_NONE | others
6055 * (I assume here that DENY_READ implies DENY_WRITE)
6057 if ( bSTGM_SHARE_DENY_NONE )
6058 if ( bSTGM_SHARE_DENY_READ ||
6059 bSTGM_SHARE_DENY_WRITE ||
6060 bSTGM_SHARE_EXCLUSIVE)
6064 * STGM_CREATE | STGM_CONVERT
6065 * if both are false, STGM_FAILIFTHERE is set to TRUE
6067 if ( bSTGM_CREATE && bSTGM_CONVERT )
6071 * STGM_NOSCRATCH requires STGM_TRANSACTED
6073 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6077 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6078 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6080 if (bSTGM_NOSNAPSHOT)
6082 if ( ! ( bSTGM_TRANSACTED &&
6083 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6090 /****************************************************************************
6091 * GetShareModeFromSTGM
6093 * This method will return a share mode flag from a STGM value.
6094 * The STGM value is assumed valid.
6096 static DWORD GetShareModeFromSTGM(DWORD stgm)
6098 DWORD dwShareMode = 0;
6099 BOOL bSTGM_SHARE_DENY_NONE =
6100 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6102 BOOL bSTGM_SHARE_DENY_READ =
6103 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6105 BOOL bSTGM_SHARE_DENY_WRITE =
6106 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6108 BOOL bSTGM_SHARE_EXCLUSIVE =
6109 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6111 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6114 if (bSTGM_SHARE_DENY_NONE)
6115 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6117 if (bSTGM_SHARE_DENY_WRITE)
6118 dwShareMode = FILE_SHARE_READ;
6123 /****************************************************************************
6124 * GetAccessModeFromSTGM
6126 * This method will return an access mode flag from a STGM value.
6127 * The STGM value is assumed valid.
6129 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6131 DWORD dwDesiredAccess = GENERIC_READ;
6132 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6133 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6134 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6137 dwDesiredAccess = GENERIC_READ;
6140 dwDesiredAccess |= GENERIC_WRITE;
6142 if (bSTGM_READWRITE)
6143 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6145 return dwDesiredAccess;
6148 /****************************************************************************
6149 * GetCreationModeFromSTGM
6151 * This method will return a creation mode flag from a STGM value.
6152 * The STGM value is assumed valid.
6154 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6156 if ( stgm & STGM_CREATE)
6157 return CREATE_ALWAYS;
6158 if (stgm & STGM_CONVERT) {
6159 FIXME("STGM_CONVERT not implemented!\n");
6162 /* All other cases */
6163 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6164 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6169 /*************************************************************************
6170 * OLECONVERT_LoadOLE10 [Internal]
6172 * Loads the OLE10 STREAM to memory
6175 * pOleStream [I] The OLESTREAM
6176 * pData [I] Data Structure for the OLESTREAM Data
6180 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6181 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6184 * This function is used by OleConvertOLESTREAMToIStorage only.
6186 * Memory allocated for pData must be freed by the caller
6188 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6191 HRESULT hRes = S_OK;
6195 pData->pData = NULL;
6196 pData->pstrOleObjFileName = (CHAR *) NULL;
6198 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6201 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6202 if(dwSize != sizeof(pData->dwOleID))
6204 hRes = CONVERT10_E_OLESTREAM_GET;
6206 else if(pData->dwOleID != OLESTREAM_ID)
6208 hRes = CONVERT10_E_OLESTREAM_FMT;
6219 /* Get the TypeID...more info needed for this field */
6220 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6221 if(dwSize != sizeof(pData->dwTypeID))
6223 hRes = CONVERT10_E_OLESTREAM_GET;
6228 if(pData->dwTypeID != 0)
6230 /* Get the length of the OleTypeName */
6231 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6232 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6234 hRes = CONVERT10_E_OLESTREAM_GET;
6239 if(pData->dwOleTypeNameLength > 0)
6241 /* Get the OleTypeName */
6242 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6243 if(dwSize != pData->dwOleTypeNameLength)
6245 hRes = CONVERT10_E_OLESTREAM_GET;
6251 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6252 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6254 hRes = CONVERT10_E_OLESTREAM_GET;
6258 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6259 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6260 pData->pstrOleObjFileName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6261 if(pData->pstrOleObjFileName)
6263 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6264 if(dwSize != pData->dwOleObjFileNameLength)
6266 hRes = CONVERT10_E_OLESTREAM_GET;
6270 hRes = CONVERT10_E_OLESTREAM_GET;
6275 /* Get the Width of the Metafile */
6276 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6277 if(dwSize != sizeof(pData->dwMetaFileWidth))
6279 hRes = CONVERT10_E_OLESTREAM_GET;
6283 /* Get the Height of the Metafile */
6284 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6285 if(dwSize != sizeof(pData->dwMetaFileHeight))
6287 hRes = CONVERT10_E_OLESTREAM_GET;
6293 /* Get the Length of the Data */
6294 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6295 if(dwSize != sizeof(pData->dwDataLength))
6297 hRes = CONVERT10_E_OLESTREAM_GET;
6301 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6303 if(!bStrem1) /* if it is a second OLE stream data */
6305 pData->dwDataLength -= 8;
6306 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6307 if(dwSize != sizeof(pData->strUnknown))
6309 hRes = CONVERT10_E_OLESTREAM_GET;
6315 if(pData->dwDataLength > 0)
6317 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6319 /* Get Data (ex. IStorage, Metafile, or BMP) */
6322 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6323 if(dwSize != pData->dwDataLength)
6325 hRes = CONVERT10_E_OLESTREAM_GET;
6330 hRes = CONVERT10_E_OLESTREAM_GET;
6339 /*************************************************************************
6340 * OLECONVERT_SaveOLE10 [Internal]
6342 * Saves the OLE10 STREAM From memory
6345 * pData [I] Data Structure for the OLESTREAM Data
6346 * pOleStream [I] The OLESTREAM to save
6350 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6353 * This function is used by OleConvertIStorageToOLESTREAM only.
6356 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6359 HRESULT hRes = S_OK;
6363 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6364 if(dwSize != sizeof(pData->dwOleID))
6366 hRes = CONVERT10_E_OLESTREAM_PUT;
6371 /* Set the TypeID */
6372 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6373 if(dwSize != sizeof(pData->dwTypeID))
6375 hRes = CONVERT10_E_OLESTREAM_PUT;
6379 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6381 /* Set the Length of the OleTypeName */
6382 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6383 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6385 hRes = CONVERT10_E_OLESTREAM_PUT;
6390 if(pData->dwOleTypeNameLength > 0)
6392 /* Set the OleTypeName */
6393 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6394 if(dwSize != pData->dwOleTypeNameLength)
6396 hRes = CONVERT10_E_OLESTREAM_PUT;
6403 /* Set the width of the Metafile */
6404 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6405 if(dwSize != sizeof(pData->dwMetaFileWidth))
6407 hRes = CONVERT10_E_OLESTREAM_PUT;
6413 /* Set the height of the Metafile */
6414 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6415 if(dwSize != sizeof(pData->dwMetaFileHeight))
6417 hRes = CONVERT10_E_OLESTREAM_PUT;
6423 /* Set the length of the Data */
6424 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6425 if(dwSize != sizeof(pData->dwDataLength))
6427 hRes = CONVERT10_E_OLESTREAM_PUT;
6433 if(pData->dwDataLength > 0)
6435 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6436 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6437 if(dwSize != pData->dwDataLength)
6439 hRes = CONVERT10_E_OLESTREAM_PUT;
6447 /*************************************************************************
6448 * OLECONVERT_GetOLE20FromOLE10[Internal]
6450 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6451 * opens it, and copies the content to the dest IStorage for
6452 * OleConvertOLESTREAMToIStorage
6456 * pDestStorage [I] The IStorage to copy the data to
6457 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6458 * nBufferLength [I] The size of the buffer
6467 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6471 IStorage *pTempStorage;
6472 DWORD dwNumOfBytesWritten;
6473 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6474 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6476 /* Create a temp File */
6477 GetTempPathW(MAX_PATH, wstrTempDir);
6478 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6479 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6481 if(hFile != INVALID_HANDLE_VALUE)
6483 /* Write IStorage Data to File */
6484 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6487 /* Open and copy temp storage to the Dest Storage */
6488 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6491 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6492 StorageBaseImpl_Release(pTempStorage);
6494 DeleteFileW(wstrTempFile);
6499 /*************************************************************************
6500 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6502 * Saves the OLE10 STREAM From memory
6505 * pStorage [I] The Src IStorage to copy
6506 * pData [I] The Dest Memory to write to.
6509 * The size in bytes allocated for pData
6512 * Memory allocated for pData must be freed by the caller
6514 * Used by OleConvertIStorageToOLESTREAM only.
6517 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6521 DWORD nDataLength = 0;
6522 IStorage *pTempStorage;
6523 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6524 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6528 /* Create temp Storage */
6529 GetTempPathW(MAX_PATH, wstrTempDir);
6530 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6531 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6535 /* Copy Src Storage to the Temp Storage */
6536 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6537 StorageBaseImpl_Release(pTempStorage);
6539 /* Open Temp Storage as a file and copy to memory */
6540 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6541 if(hFile != INVALID_HANDLE_VALUE)
6543 nDataLength = GetFileSize(hFile, NULL);
6544 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6545 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6548 DeleteFileW(wstrTempFile);
6553 /*************************************************************************
6554 * OLECONVERT_CreateOleStream [Internal]
6556 * Creates the "\001OLE" stream in the IStorage if necessary.
6559 * pStorage [I] Dest storage to create the stream in
6565 * This function is used by OleConvertOLESTREAMToIStorage only.
6567 * This stream is still unknown, MS Word seems to have extra data
6568 * but since the data is stored in the OLESTREAM there should be
6569 * no need to recreate the stream. If the stream is manually
6570 * deleted it will create it with this default data.
6573 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6577 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6578 BYTE pOleStreamHeader [] =
6580 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6581 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6582 0x00, 0x00, 0x00, 0x00
6585 /* Create stream if not present */
6586 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6587 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6591 /* Write default Data */
6592 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6593 IStream_Release(pStream);
6597 /* write a string to a stream, preceded by its length */
6598 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6605 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6606 r = IStream_Write( stm, &len, sizeof(len), NULL);
6611 str = CoTaskMemAlloc( len );
6612 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6613 r = IStream_Write( stm, str, len, NULL);
6614 CoTaskMemFree( str );
6618 /* read a string preceded by its length from a stream */
6619 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6622 DWORD len, count = 0;
6626 r = IStream_Read( stm, &len, sizeof(len), &count );
6629 if( count != sizeof(len) )
6630 return E_OUTOFMEMORY;
6632 TRACE("%ld bytes\n",len);
6634 str = CoTaskMemAlloc( len );
6636 return E_OUTOFMEMORY;
6638 r = IStream_Read( stm, str, len, &count );
6643 CoTaskMemFree( str );
6644 return E_OUTOFMEMORY;
6647 TRACE("Read string %s\n",debugstr_an(str,len));
6649 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6650 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6652 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6653 CoTaskMemFree( str );
6661 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6662 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6666 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6668 static const BYTE unknown1[12] =
6669 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6670 0xFF, 0xFF, 0xFF, 0xFF};
6671 static const BYTE unknown2[16] =
6672 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6673 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6675 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6676 debugstr_w(lpszUserType), debugstr_w(szClipName),
6677 debugstr_w(szProgIDName));
6679 /* Create a CompObj stream if it doesn't exist */
6680 r = IStorage_CreateStream(pstg, szwStreamName,
6681 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6685 /* Write CompObj Structure to stream */
6686 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6688 if( SUCCEEDED( r ) )
6689 r = WriteClassStm( pstm, clsid );
6691 if( SUCCEEDED( r ) )
6692 r = STREAM_WriteString( pstm, lpszUserType );
6693 if( SUCCEEDED( r ) )
6694 r = STREAM_WriteString( pstm, szClipName );
6695 if( SUCCEEDED( r ) )
6696 r = STREAM_WriteString( pstm, szProgIDName );
6697 if( SUCCEEDED( r ) )
6698 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6700 IStream_Release( pstm );
6705 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6706 static HRESULT CLSIDFromUserType(LPCWSTR lpszUserType, CLSID *clsid)
6711 HKEY hkey, hkeyclsid;
6712 LPWSTR buffer = NULL;
6714 static const WCHAR szclsid[] = { 'C','L','S','I','D',0 };
6716 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType));
6718 r = RegOpenKeyW( HKEY_CLASSES_ROOT, szclsid, &hkeyclsid );
6720 return E_INVALIDARG;
6722 len = lstrlenW( lpszUserType ) + 1;
6723 buffer = CoTaskMemAlloc( len * sizeof (WCHAR) );
6727 for(i=0; !found; i++ )
6729 r = RegEnumKeyW( hkeyclsid, i, szKey, sizeof(szKey)/sizeof(WCHAR));
6730 if( r != ERROR_SUCCESS )
6733 r = RegOpenKeyW( hkeyclsid, szKey, &hkey );
6734 if( r != ERROR_SUCCESS )
6736 count = len * sizeof (WCHAR);
6737 r = RegQueryValueW( hkey, NULL, buffer, &count );
6738 found = ( r == ERROR_SUCCESS ) &&
6739 ( count == len*sizeof(WCHAR) ) &&
6740 !lstrcmpW( buffer, lpszUserType ) ;
6741 RegCloseKey( hkey );
6746 CoTaskMemFree( buffer );
6747 RegCloseKey( hkeyclsid );
6750 return E_INVALIDARG;
6752 TRACE("clsid is %s\n", debugstr_w( szKey ) );
6754 r = CLSIDFromString( szKey, clsid );
6760 /***********************************************************************
6761 * WriteFmtUserTypeStg (OLE32.@)
6763 HRESULT WINAPI WriteFmtUserTypeStg(
6764 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6767 WCHAR szwClipName[0x40];
6768 WCHAR szCLSIDName[OLESTREAM_MAX_STR_LEN];
6772 LPMALLOC allocator = NULL;
6774 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6776 r = CoGetMalloc(0, &allocator);
6778 return E_OUTOFMEMORY;
6780 /* get the clipboard format name */
6781 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6784 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6788 r = CLSIDFromUserType(lpszUserType, &clsid);
6792 TRACE("CLSID is %s\n",debugstr_guid(&clsid));
6794 /* get the real program ID */
6795 r = ProgIDFromCLSID( &clsid, &wstrProgID);
6799 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6801 /* if we have a good string, write the stream */
6803 r = STORAGE_WriteCompObj( pstg, &clsid,
6804 lpszUserType, szwClipName, wstrProgID );
6808 IMalloc_Free( allocator, wstrProgID);
6814 /******************************************************************************
6815 * ReadFmtUserTypeStg [OLE32.@]
6817 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6821 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6822 unsigned char unknown1[12];
6823 unsigned char unknown2[16];
6825 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6828 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6830 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6831 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6834 ERR("Failed to open stream\n");
6838 /* read the various parts of the structure */
6839 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6840 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6842 r = ReadClassStm( stm, &clsid );
6846 r = STREAM_ReadString( stm, &szCLSIDName );
6850 r = STREAM_ReadString( stm, &szOleTypeName );
6854 r = STREAM_ReadString( stm, &szProgIDName );
6858 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6859 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6862 /* ok, success... now we just need to store what we found */
6864 *pcf = RegisterClipboardFormatW( szOleTypeName );
6865 CoTaskMemFree( szOleTypeName );
6867 if( lplpszUserType )
6868 *lplpszUserType = szCLSIDName;
6869 CoTaskMemFree( szProgIDName );
6872 IStream_Release( stm );
6878 /*************************************************************************
6879 * OLECONVERT_CreateCompObjStream [Internal]
6881 * Creates a "\001CompObj" is the destination IStorage if necessary.
6884 * pStorage [I] The dest IStorage to create the CompObj Stream
6886 * strOleTypeName [I] The ProgID
6890 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6893 * This function is used by OleConvertOLESTREAMToIStorage only.
6895 * The stream data is stored in the OLESTREAM and there should be
6896 * no need to recreate the stream. If the stream is manually
6897 * deleted it will attempt to create it by querying the registry.
6901 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6904 HRESULT hStorageRes, hRes = S_OK;
6905 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6906 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6907 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
6909 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6910 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6912 /* Initialize the CompObj structure */
6913 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6914 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6915 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6918 /* Create a CompObj stream if it doesn't exist */
6919 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6920 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6921 if(hStorageRes == S_OK)
6923 /* copy the OleTypeName to the compobj struct */
6924 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6925 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6927 /* copy the OleTypeName to the compobj struct */
6928 /* Note: in the test made, these were Identical */
6929 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6930 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6933 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
6934 bufferW, OLESTREAM_MAX_STR_LEN );
6935 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
6941 /* Get the CLSID Default Name from the Registry */
6942 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6943 if(hErr == ERROR_SUCCESS)
6945 char strTemp[OLESTREAM_MAX_STR_LEN];
6946 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6947 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6948 if(hErr == ERROR_SUCCESS)
6950 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6956 /* Write CompObj Structure to stream */
6957 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6959 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6961 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6962 if(IStorageCompObj.dwCLSIDNameLength > 0)
6964 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6966 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6967 if(IStorageCompObj.dwOleTypeNameLength > 0)
6969 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6971 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6972 if(IStorageCompObj.dwProgIDNameLength > 0)
6974 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6976 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6977 IStream_Release(pStream);
6983 /*************************************************************************
6984 * OLECONVERT_CreateOlePresStream[Internal]
6986 * Creates the "\002OlePres000" Stream with the Metafile data
6989 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6990 * dwExtentX [I] Width of the Metafile
6991 * dwExtentY [I] Height of the Metafile
6992 * pData [I] Metafile data
6993 * dwDataLength [I] Size of the Metafile data
6997 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7000 * This function is used by OleConvertOLESTREAMToIStorage only.
7003 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7007 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7008 BYTE pOlePresStreamHeader [] =
7010 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7011 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7012 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7013 0x00, 0x00, 0x00, 0x00
7016 BYTE pOlePresStreamHeaderEmpty [] =
7018 0x00, 0x00, 0x00, 0x00,
7019 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7020 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7021 0x00, 0x00, 0x00, 0x00
7024 /* Create the OlePres000 Stream */
7025 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7026 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7031 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7033 memset(&OlePres, 0, sizeof(OlePres));
7034 /* Do we have any metafile data to save */
7035 if(dwDataLength > 0)
7037 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7038 nHeaderSize = sizeof(pOlePresStreamHeader);
7042 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7043 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7045 /* Set width and height of the metafile */
7046 OlePres.dwExtentX = dwExtentX;
7047 OlePres.dwExtentY = -dwExtentY;
7049 /* Set Data and Length */
7050 if(dwDataLength > sizeof(METAFILEPICT16))
7052 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7053 OlePres.pData = &(pData[8]);
7055 /* Save OlePres000 Data to Stream */
7056 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7057 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7058 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7059 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7060 if(OlePres.dwSize > 0)
7062 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7064 IStream_Release(pStream);
7068 /*************************************************************************
7069 * OLECONVERT_CreateOle10NativeStream [Internal]
7071 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7074 * pStorage [I] Dest storage to create the stream in
7075 * pData [I] Ole10 Native Data (ex. bmp)
7076 * dwDataLength [I] Size of the Ole10 Native Data
7082 * This function is used by OleConvertOLESTREAMToIStorage only.
7084 * Might need to verify the data and return appropriate error message
7087 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7091 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7093 /* Create the Ole10Native Stream */
7094 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7095 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7099 /* Write info to stream */
7100 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7101 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7102 IStream_Release(pStream);
7107 /*************************************************************************
7108 * OLECONVERT_GetOLE10ProgID [Internal]
7110 * Finds the ProgID (or OleTypeID) from the IStorage
7113 * pStorage [I] The Src IStorage to get the ProgID
7114 * strProgID [I] the ProgID string to get
7115 * dwSize [I] the size of the string
7119 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7122 * This function is used by OleConvertIStorageToOLESTREAM only.
7126 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7130 LARGE_INTEGER iSeekPos;
7131 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7132 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7134 /* Open the CompObj Stream */
7135 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7136 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7140 /*Get the OleType from the CompObj Stream */
7141 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7142 iSeekPos.u.HighPart = 0;
7144 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7145 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7146 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7147 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7148 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7149 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7150 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7152 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7155 IStream_Read(pStream, strProgID, *dwSize, NULL);
7157 IStream_Release(pStream);
7162 LPOLESTR wstrProgID;
7164 /* Get the OleType from the registry */
7165 REFCLSID clsid = &(stat.clsid);
7166 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7167 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7170 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7177 /*************************************************************************
7178 * OLECONVERT_GetOle10PresData [Internal]
7180 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7183 * pStorage [I] Src IStroage
7184 * pOleStream [I] Dest OleStream Mem Struct
7190 * This function is used by OleConvertIStorageToOLESTREAM only.
7192 * Memory allocated for pData must be freed by the caller
7196 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7201 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7203 /* Initialize Default data for OLESTREAM */
7204 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7205 pOleStreamData[0].dwTypeID = 2;
7206 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7207 pOleStreamData[1].dwTypeID = 0;
7208 pOleStreamData[0].dwMetaFileWidth = 0;
7209 pOleStreamData[0].dwMetaFileHeight = 0;
7210 pOleStreamData[0].pData = NULL;
7211 pOleStreamData[1].pData = NULL;
7213 /* Open Ole10Native Stream */
7214 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7215 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7219 /* Read Size and Data */
7220 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7221 if(pOleStreamData->dwDataLength > 0)
7223 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7224 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7226 IStream_Release(pStream);
7232 /*************************************************************************
7233 * OLECONVERT_GetOle20PresData[Internal]
7235 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7238 * pStorage [I] Src IStroage
7239 * pOleStreamData [I] Dest OleStream Mem Struct
7245 * This function is used by OleConvertIStorageToOLESTREAM only.
7247 * Memory allocated for pData must be freed by the caller
7249 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7253 OLECONVERT_ISTORAGE_OLEPRES olePress;
7254 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7256 /* Initialize Default data for OLESTREAM */
7257 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7258 pOleStreamData[0].dwTypeID = 2;
7259 pOleStreamData[0].dwMetaFileWidth = 0;
7260 pOleStreamData[0].dwMetaFileHeight = 0;
7261 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7262 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7263 pOleStreamData[1].dwTypeID = 0;
7264 pOleStreamData[1].dwOleTypeNameLength = 0;
7265 pOleStreamData[1].strOleTypeName[0] = 0;
7266 pOleStreamData[1].dwMetaFileWidth = 0;
7267 pOleStreamData[1].dwMetaFileHeight = 0;
7268 pOleStreamData[1].pData = NULL;
7269 pOleStreamData[1].dwDataLength = 0;
7272 /* Open OlePress000 stream */
7273 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7274 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7277 LARGE_INTEGER iSeekPos;
7278 METAFILEPICT16 MetaFilePict;
7279 static const char strMetafilePictName[] = "METAFILEPICT";
7281 /* Set the TypeID for a Metafile */
7282 pOleStreamData[1].dwTypeID = 5;
7284 /* Set the OleTypeName to Metafile */
7285 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7286 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7288 iSeekPos.u.HighPart = 0;
7289 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7291 /* Get Presentation Data */
7292 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7293 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7294 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7295 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7297 /*Set width and Height */
7298 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7299 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7300 if(olePress.dwSize > 0)
7303 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7305 /* Set MetaFilePict struct */
7306 MetaFilePict.mm = 8;
7307 MetaFilePict.xExt = olePress.dwExtentX;
7308 MetaFilePict.yExt = olePress.dwExtentY;
7309 MetaFilePict.hMF = 0;
7311 /* Get Metafile Data */
7312 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7313 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7314 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7316 IStream_Release(pStream);
7320 /*************************************************************************
7321 * OleConvertOLESTREAMToIStorage [OLE32.@]
7326 * DVTARGETDEVICE paramenter is not handled
7327 * Still unsure of some mem fields for OLE 10 Stream
7328 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7329 * and "\001OLE" streams
7332 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7333 LPOLESTREAM pOleStream,
7335 const DVTARGETDEVICE* ptd)
7339 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7341 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7345 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7348 if(pstg == NULL || pOleStream == NULL)
7350 hRes = E_INVALIDARG;
7355 /* Load the OLESTREAM to Memory */
7356 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7361 /* Load the OLESTREAM to Memory (part 2)*/
7362 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7368 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7370 /* Do we have the IStorage Data in the OLESTREAM */
7371 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7373 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7374 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7378 /* It must be an original OLE 1.0 source */
7379 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7384 /* It must be an original OLE 1.0 source */
7385 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7388 /* Create CompObj Stream if necessary */
7389 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7392 /*Create the Ole Stream if necessary */
7393 OLECONVERT_CreateOleStream(pstg);
7398 /* Free allocated memory */
7399 for(i=0; i < 2; i++)
7401 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7402 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7403 pOleStreamData[i].pstrOleObjFileName = NULL;
7408 /*************************************************************************
7409 * OleConvertIStorageToOLESTREAM [OLE32.@]
7416 * Still unsure of some mem fields for OLE 10 Stream
7417 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7418 * and "\001OLE" streams.
7421 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7423 LPOLESTREAM pOleStream)
7426 HRESULT hRes = S_OK;
7428 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7429 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7432 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7434 if(pstg == NULL || pOleStream == NULL)
7436 hRes = E_INVALIDARG;
7440 /* Get the ProgID */
7441 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7442 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7446 /* Was it originally Ole10 */
7447 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7450 IStream_Release(pStream);
7451 /* Get Presentation Data for Ole10Native */
7452 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7456 /* Get Presentation Data (OLE20) */
7457 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7460 /* Save OLESTREAM */
7461 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7464 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7469 /* Free allocated memory */
7470 for(i=0; i < 2; i++)
7472 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7478 /***********************************************************************
7479 * GetConvertStg (OLE32.@)
7481 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7482 FIXME("unimplemented stub!\n");
7486 /******************************************************************************
7487 * StgIsStorageFile [OLE32.@]
7490 StgIsStorageFile(LPCOLESTR fn)
7496 TRACE("(\'%s\')\n", debugstr_w(fn));
7497 hf = CreateFileW(fn, GENERIC_READ,
7498 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7499 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7501 if (hf == INVALID_HANDLE_VALUE)
7502 return STG_E_FILENOTFOUND;
7504 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7506 WARN(" unable to read file\n");
7513 if (bytes_read != 8) {
7514 WARN(" too short\n");
7518 if (!memcmp(magic,STORAGE_magic,8)) {
7523 WARN(" -> Invalid header.\n");