2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
42 #include "wine/unicode.h"
43 #include "wine/debug.h"
45 #include "storage32.h"
46 #include "ole2.h" /* For Write/ReadClassStm */
49 #include "wine/wingdi16.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(storage);
55 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
56 #define OLESTREAM_ID 0x501
57 #define OLESTREAM_MAX_STR_LEN 255
59 static const char rootPropertyName[] = "Root Entry";
62 /* OLESTREAM memory structure to use for Get and Put Routines */
63 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
68 DWORD dwOleTypeNameLength;
69 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
70 CHAR *pstrOleObjFileName;
71 DWORD dwOleObjFileNameLength;
72 DWORD dwMetaFileWidth;
73 DWORD dwMetaFileHeight;
74 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
77 }OLECONVERT_OLESTREAM_DATA;
79 /* CompObj Stream structure */
80 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
85 DWORD dwCLSIDNameLength;
86 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
87 DWORD dwOleTypeNameLength;
88 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
89 DWORD dwProgIDNameLength;
90 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
92 }OLECONVERT_ISTORAGE_COMPOBJ;
95 /* Ole Presention Stream structure */
96 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
104 }OLECONVERT_ISTORAGE_OLEPRES;
108 /***********************************************************************
109 * Forward declaration of internal functions used by the method DestroyElement
111 static HRESULT deleteStorageProperty(
112 StorageImpl *parentStorage,
113 ULONG foundPropertyIndexToDelete,
114 StgProperty propertyToDelete);
116 static HRESULT deleteStreamProperty(
117 StorageImpl *parentStorage,
118 ULONG foundPropertyIndexToDelete,
119 StgProperty propertyToDelete);
121 static HRESULT findPlaceholder(
122 StorageImpl *storage,
123 ULONG propertyIndexToStore,
124 ULONG storagePropertyIndex,
127 static HRESULT adjustPropertyChain(
129 StgProperty propertyToDelete,
130 StgProperty parentProperty,
131 ULONG parentPropertyId,
134 /***********************************************************************
135 * Declaration of the functions used to manipulate StgProperty
138 static ULONG getFreeProperty(
139 StorageImpl *storage);
141 static void updatePropertyChain(
142 StorageImpl *storage,
143 ULONG newPropertyIndex,
144 StgProperty newProperty);
146 static LONG propertyNameCmp(
147 const OLECHAR *newProperty,
148 const OLECHAR *currentProperty);
151 /***********************************************************************
152 * Declaration of miscellaneous functions...
154 static HRESULT validateSTGM(DWORD stgmValue);
156 static DWORD GetShareModeFromSTGM(DWORD stgm);
157 static DWORD GetAccessModeFromSTGM(DWORD stgm);
158 static DWORD GetCreationModeFromSTGM(DWORD stgm);
160 extern IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
164 /************************************************************************
165 ** Storage32BaseImpl implementatiion
168 /************************************************************************
169 * Storage32BaseImpl_QueryInterface (IUnknown)
171 * This method implements the common QueryInterface for all IStorage32
172 * implementations contained in this file.
174 * See Windows documentation for more details on IUnknown methods.
176 HRESULT WINAPI StorageBaseImpl_QueryInterface(
181 StorageBaseImpl *This = (StorageBaseImpl *)iface;
183 * Perform a sanity check on the parameters.
185 if ( (This==0) || (ppvObject==0) )
189 * Initialize the return parameter.
194 * Compare the riid with the interface IDs implemented by this object.
196 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
198 *ppvObject = (IStorage*)This;
200 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
202 *ppvObject = (IStorage*)This;
204 else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)
206 *ppvObject = (IStorage*)&This->pssVtbl;
210 * Check that we obtained an interface.
213 return E_NOINTERFACE;
216 * Query Interface always increases the reference count by one when it is
219 IStorage_AddRef(iface);
224 /************************************************************************
225 * Storage32BaseImpl_AddRef (IUnknown)
227 * This method implements the common AddRef for all IStorage32
228 * implementations contained in this file.
230 * See Windows documentation for more details on IUnknown methods.
232 ULONG WINAPI StorageBaseImpl_AddRef(
235 StorageBaseImpl *This = (StorageBaseImpl *)iface;
236 ULONG ref = InterlockedIncrement(&This->ref);
238 TRACE("(%p) AddRef to %ld\n", This, ref);
243 /************************************************************************
244 * Storage32BaseImpl_Release (IUnknown)
246 * This method implements the common Release for all IStorage32
247 * implementations contained in this file.
249 * See Windows documentation for more details on IUnknown methods.
251 ULONG WINAPI StorageBaseImpl_Release(
254 StorageBaseImpl *This = (StorageBaseImpl *)iface;
256 * Decrease the reference count on this object.
258 ULONG ref = InterlockedDecrement(&This->ref);
260 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
263 * If the reference count goes down to 0, perform suicide.
268 * Since we are using a system of base-classes, we want to call the
269 * destructor of the appropriate derived class. To do this, we are
270 * using virtual functions to implement the destructor.
272 This->v_destructor(This);
278 /************************************************************************
279 * Storage32BaseImpl_OpenStream (IStorage)
281 * This method will open the specified stream object from the current storage.
283 * See Windows documentation for more details on IStorage methods.
285 HRESULT WINAPI StorageBaseImpl_OpenStream(
287 const OLECHAR* pwcsName, /* [string][in] */
288 void* reserved1, /* [unique][in] */
289 DWORD grfMode, /* [in] */
290 DWORD reserved2, /* [in] */
291 IStream** ppstm) /* [out] */
293 StorageBaseImpl *This = (StorageBaseImpl *)iface;
294 IEnumSTATSTGImpl* propertyEnumeration;
295 StgStreamImpl* newStream;
296 StgProperty currentProperty;
297 ULONG foundPropertyIndex;
298 HRESULT res = STG_E_UNKNOWN;
299 DWORD parent_grfMode;
301 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
302 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
305 * Perform a sanity check on the parameters.
307 if ( (pwcsName==NULL) || (ppstm==0) )
314 * Initialize the out parameter
319 * Validate the STGM flags
321 if ( FAILED( validateSTGM(grfMode) ))
323 res = STG_E_INVALIDFLAG;
330 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
331 (grfMode & STGM_DELETEONRELEASE) ||
332 (grfMode & STGM_TRANSACTED) )
334 res = STG_E_INVALIDFUNCTION;
339 * Check that we're compatible with the parent's storage mode
341 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
342 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
344 res = STG_E_ACCESSDENIED;
349 * Create a property enumeration to search the properties
351 propertyEnumeration = IEnumSTATSTGImpl_Construct(
352 This->ancestorStorage,
353 This->rootPropertySetIndex);
356 * Search the enumeration for the property with the given name
358 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
364 * Delete the property enumeration since we don't need it anymore
366 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
369 * If it was found, construct the stream object and return a pointer to it.
371 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
372 (currentProperty.propertyType==PROPTYPE_STREAM) )
374 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
378 newStream->grfMode = grfMode;
379 *ppstm = (IStream*)newStream;
382 * Since we are returning a pointer to the interface, we have to
383 * nail down the reference.
385 IStream_AddRef(*ppstm);
395 res = STG_E_FILENOTFOUND;
399 TRACE("<-- IStream %p\n", *ppstm);
400 TRACE("<-- %08lx\n", res);
404 /************************************************************************
405 * Storage32BaseImpl_OpenStorage (IStorage)
407 * This method will open a new storage object from the current storage.
409 * See Windows documentation for more details on IStorage methods.
411 HRESULT WINAPI StorageBaseImpl_OpenStorage(
413 const OLECHAR* pwcsName, /* [string][unique][in] */
414 IStorage* pstgPriority, /* [unique][in] */
415 DWORD grfMode, /* [in] */
416 SNB snbExclude, /* [unique][in] */
417 DWORD reserved, /* [in] */
418 IStorage** ppstg) /* [out] */
420 StorageBaseImpl *This = (StorageBaseImpl *)iface;
421 StorageInternalImpl* newStorage;
422 IEnumSTATSTGImpl* propertyEnumeration;
423 StgProperty currentProperty;
424 ULONG foundPropertyIndex;
425 HRESULT res = STG_E_UNKNOWN;
426 DWORD parent_grfMode;
428 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
429 iface, debugstr_w(pwcsName), pstgPriority,
430 grfMode, snbExclude, reserved, ppstg);
433 * Perform a sanity check on the parameters.
435 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
442 if (snbExclude != NULL)
444 res = STG_E_INVALIDPARAMETER;
449 * Validate the STGM flags
451 if ( FAILED( validateSTGM(grfMode) ))
453 res = STG_E_INVALIDFLAG;
460 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
461 (grfMode & STGM_DELETEONRELEASE) ||
462 (grfMode & STGM_PRIORITY) )
464 res = STG_E_INVALIDFUNCTION;
469 * Check that we're compatible with the parent's storage mode
471 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
472 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
474 res = STG_E_ACCESSDENIED;
479 * Initialize the out parameter
484 * Create a property enumeration to search the properties
486 propertyEnumeration = IEnumSTATSTGImpl_Construct(
487 This->ancestorStorage,
488 This->rootPropertySetIndex);
491 * Search the enumeration for the property with the given name
493 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
499 * Delete the property enumeration since we don't need it anymore
501 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
504 * If it was found, construct the stream object and return a pointer to it.
506 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
507 (currentProperty.propertyType==PROPTYPE_STORAGE) )
510 * Construct a new Storage object
512 newStorage = StorageInternalImpl_Construct(
513 This->ancestorStorage,
519 *ppstg = (IStorage*)newStorage;
522 * Since we are returning a pointer to the interface,
523 * we have to nail down the reference.
525 StorageBaseImpl_AddRef(*ppstg);
531 res = STG_E_INSUFFICIENTMEMORY;
535 res = STG_E_FILENOTFOUND;
538 TRACE("<-- %08lx\n", res);
542 /************************************************************************
543 * Storage32BaseImpl_EnumElements (IStorage)
545 * This method will create an enumerator object that can be used to
546 * retrieve informatino about all the properties in the storage object.
548 * See Windows documentation for more details on IStorage methods.
550 HRESULT WINAPI StorageBaseImpl_EnumElements(
552 DWORD reserved1, /* [in] */
553 void* reserved2, /* [size_is][unique][in] */
554 DWORD reserved3, /* [in] */
555 IEnumSTATSTG** ppenum) /* [out] */
557 StorageBaseImpl *This = (StorageBaseImpl *)iface;
558 IEnumSTATSTGImpl* newEnum;
560 TRACE("(%p, %ld, %p, %ld, %p)\n",
561 iface, reserved1, reserved2, reserved3, ppenum);
564 * Perform a sanity check on the parameters.
566 if ( (This==0) || (ppenum==0))
570 * Construct the enumerator.
572 newEnum = IEnumSTATSTGImpl_Construct(
573 This->ancestorStorage,
574 This->rootPropertySetIndex);
578 *ppenum = (IEnumSTATSTG*)newEnum;
581 * Don't forget to nail down a reference to the new object before
584 IEnumSTATSTG_AddRef(*ppenum);
589 return E_OUTOFMEMORY;
592 /************************************************************************
593 * Storage32BaseImpl_Stat (IStorage)
595 * This method will retrieve information about this storage object.
597 * See Windows documentation for more details on IStorage methods.
599 HRESULT WINAPI StorageBaseImpl_Stat(
601 STATSTG* pstatstg, /* [out] */
602 DWORD grfStatFlag) /* [in] */
604 StorageBaseImpl *This = (StorageBaseImpl *)iface;
605 StgProperty curProperty;
607 HRESULT res = STG_E_UNKNOWN;
609 TRACE("(%p, %p, %lx)\n",
610 iface, pstatstg, grfStatFlag);
613 * Perform a sanity check on the parameters.
615 if ( (This==0) || (pstatstg==0))
622 * Read the information from the property.
624 readSuccessful = StorageImpl_ReadProperty(
625 This->ancestorStorage,
626 This->rootPropertySetIndex,
631 StorageUtl_CopyPropertyToSTATSTG(
645 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);
647 TRACE("<-- %08lx\n", res);
651 /************************************************************************
652 * Storage32BaseImpl_RenameElement (IStorage)
654 * This method will rename the specified element.
656 * See Windows documentation for more details on IStorage methods.
658 * Implementation notes: The method used to rename consists of creating a clone
659 * of the deleted StgProperty object setting it with the new name and to
660 * perform a DestroyElement of the old StgProperty.
662 HRESULT WINAPI StorageBaseImpl_RenameElement(
664 const OLECHAR* pwcsOldName, /* [in] */
665 const OLECHAR* pwcsNewName) /* [in] */
667 StorageBaseImpl *This = (StorageBaseImpl *)iface;
668 IEnumSTATSTGImpl* propertyEnumeration;
669 StgProperty currentProperty;
670 ULONG foundPropertyIndex;
672 TRACE("(%p, %s, %s)\n",
673 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
676 * Create a property enumeration to search the properties
678 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
679 This->rootPropertySetIndex);
682 * Search the enumeration for the new property name
684 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
688 if (foundPropertyIndex != PROPERTY_NULL)
691 * There is already a property with the new name
693 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
694 return STG_E_FILEALREADYEXISTS;
697 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
700 * Search the enumeration for the old property name
702 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
707 * Delete the property enumeration since we don't need it anymore
709 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
711 if (foundPropertyIndex != PROPERTY_NULL)
713 StgProperty renamedProperty;
714 ULONG renamedPropertyIndex;
717 * Setup a new property for the renamed property
719 renamedProperty.sizeOfNameString =
720 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
722 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
723 return STG_E_INVALIDNAME;
725 strcpyW(renamedProperty.name, pwcsNewName);
727 renamedProperty.propertyType = currentProperty.propertyType;
728 renamedProperty.startingBlock = currentProperty.startingBlock;
729 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
730 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
732 renamedProperty.previousProperty = PROPERTY_NULL;
733 renamedProperty.nextProperty = PROPERTY_NULL;
736 * Bring the dirProperty link in case it is a storage and in which
737 * case the renamed storage elements don't require to be reorganized.
739 renamedProperty.dirProperty = currentProperty.dirProperty;
741 /* call CoFileTime to get the current time
742 renamedProperty.timeStampS1
743 renamedProperty.timeStampD1
744 renamedProperty.timeStampS2
745 renamedProperty.timeStampD2
746 renamedProperty.propertyUniqueID
750 * Obtain a free property in the property chain
752 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
755 * Save the new property into the new property spot
757 StorageImpl_WriteProperty(
758 This->ancestorStorage,
759 renamedPropertyIndex,
763 * Find a spot in the property chain for our newly created property.
767 renamedPropertyIndex,
771 * At this point the renamed property has been inserted in the tree,
772 * now, before to Destroy the old property we must zeroed it's dirProperty
773 * otherwise the DestroyProperty below will zap it all and we do not want
775 * Also, we fake that the old property is a storage so the DestroyProperty
776 * will not do a SetSize(0) on the stream data.
778 * This means that we need to tweek the StgProperty if it is a stream or a
781 StorageImpl_ReadProperty(This->ancestorStorage,
785 currentProperty.dirProperty = PROPERTY_NULL;
786 currentProperty.propertyType = PROPTYPE_STORAGE;
787 StorageImpl_WriteProperty(
788 This->ancestorStorage,
793 * Invoke Destroy to get rid of the ole property and automatically redo
794 * the linking of it's previous and next members...
796 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
802 * There is no property with the old name
804 return STG_E_FILENOTFOUND;
810 /************************************************************************
811 * Storage32BaseImpl_CreateStream (IStorage)
813 * This method will create a stream object within this storage
815 * See Windows documentation for more details on IStorage methods.
817 HRESULT WINAPI StorageBaseImpl_CreateStream(
819 const OLECHAR* pwcsName, /* [string][in] */
820 DWORD grfMode, /* [in] */
821 DWORD reserved1, /* [in] */
822 DWORD reserved2, /* [in] */
823 IStream** ppstm) /* [out] */
825 StorageBaseImpl *This = (StorageBaseImpl *)iface;
826 IEnumSTATSTGImpl* propertyEnumeration;
827 StgStreamImpl* newStream;
828 StgProperty currentProperty, newStreamProperty;
829 ULONG foundPropertyIndex, newPropertyIndex;
830 DWORD parent_grfMode;
832 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
833 iface, debugstr_w(pwcsName), grfMode,
834 reserved1, reserved2, ppstm);
837 * Validate parameters
840 return STG_E_INVALIDPOINTER;
843 return STG_E_INVALIDNAME;
845 if (reserved1 || reserved2)
846 return STG_E_INVALIDPARAMETER;
849 * Validate the STGM flags
851 if ( FAILED( validateSTGM(grfMode) ))
852 return STG_E_INVALIDFLAG;
854 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
855 return STG_E_INVALIDFLAG;
860 if ((grfMode & STGM_DELETEONRELEASE) ||
861 (grfMode & STGM_TRANSACTED))
862 return STG_E_INVALIDFUNCTION;
865 * Check that we're compatible with the parent's storage mode
867 parent_grfMode = STGM_ACCESS_MODE( This->ancestorStorage->base.openFlags );
868 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
869 return STG_E_ACCESSDENIED;
872 * Initialize the out parameter
877 * Create a property enumeration to search the properties
879 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
880 This->rootPropertySetIndex);
882 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
886 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
888 if (foundPropertyIndex != PROPERTY_NULL)
891 * An element with this name already exists
893 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
895 IStorage_DestroyElement(iface, pwcsName);
898 return STG_E_FILEALREADYEXISTS;
902 * memset the empty property
904 memset(&newStreamProperty, 0, sizeof(StgProperty));
906 newStreamProperty.sizeOfNameString =
907 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
909 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
910 return STG_E_INVALIDNAME;
912 strcpyW(newStreamProperty.name, pwcsName);
914 newStreamProperty.propertyType = PROPTYPE_STREAM;
915 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
916 newStreamProperty.size.u.LowPart = 0;
917 newStreamProperty.size.u.HighPart = 0;
919 newStreamProperty.previousProperty = PROPERTY_NULL;
920 newStreamProperty.nextProperty = PROPERTY_NULL;
921 newStreamProperty.dirProperty = PROPERTY_NULL;
923 /* call CoFileTime to get the current time
924 newStreamProperty.timeStampS1
925 newStreamProperty.timeStampD1
926 newStreamProperty.timeStampS2
927 newStreamProperty.timeStampD2
930 /* newStreamProperty.propertyUniqueID */
933 * Get a free property or create a new one
935 newPropertyIndex = getFreeProperty(This->ancestorStorage);
938 * Save the new property into the new property spot
940 StorageImpl_WriteProperty(
941 This->ancestorStorage,
946 * Find a spot in the property chain for our newly created property.
954 * Open the stream to return it.
956 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
960 *ppstm = (IStream*)newStream;
963 * Since we are returning a pointer to the interface, we have to nail down
966 IStream_AddRef(*ppstm);
970 return STG_E_INSUFFICIENTMEMORY;
976 /************************************************************************
977 * Storage32BaseImpl_SetClass (IStorage)
979 * This method will write the specified CLSID in the property of this
982 * See Windows documentation for more details on IStorage methods.
984 HRESULT WINAPI StorageBaseImpl_SetClass(
986 REFCLSID clsid) /* [in] */
988 StorageBaseImpl *This = (StorageBaseImpl *)iface;
989 HRESULT hRes = E_FAIL;
990 StgProperty curProperty;
993 TRACE("(%p, %p)\n", iface, clsid);
995 success = StorageImpl_ReadProperty(This->ancestorStorage,
996 This->rootPropertySetIndex,
1000 curProperty.propertyUniqueID = *clsid;
1002 success = StorageImpl_WriteProperty(This->ancestorStorage,
1003 This->rootPropertySetIndex,
1012 /************************************************************************
1013 ** Storage32Impl implementation
1016 /************************************************************************
1017 * Storage32Impl_CreateStorage (IStorage)
1019 * This method will create the storage object within the provided storage.
1021 * See Windows documentation for more details on IStorage methods.
1023 HRESULT WINAPI StorageImpl_CreateStorage(
1025 const OLECHAR *pwcsName, /* [string][in] */
1026 DWORD grfMode, /* [in] */
1027 DWORD reserved1, /* [in] */
1028 DWORD reserved2, /* [in] */
1029 IStorage **ppstg) /* [out] */
1031 StorageImpl* const This=(StorageImpl*)iface;
1033 IEnumSTATSTGImpl *propertyEnumeration;
1034 StgProperty currentProperty;
1035 StgProperty newProperty;
1036 ULONG foundPropertyIndex;
1037 ULONG newPropertyIndex;
1039 DWORD parent_grfMode;
1041 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1042 iface, debugstr_w(pwcsName), grfMode,
1043 reserved1, reserved2, ppstg);
1046 * Validate parameters
1049 return STG_E_INVALIDPOINTER;
1052 return STG_E_INVALIDNAME;
1055 * Validate the STGM flags
1057 if ( FAILED( validateSTGM(grfMode) ) ||
1058 (grfMode & STGM_DELETEONRELEASE) )
1059 return STG_E_INVALIDFLAG;
1062 * Check that we're compatible with the parent's storage mode
1064 parent_grfMode = STGM_ACCESS_MODE( This->base.ancestorStorage->base.openFlags );
1065 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( parent_grfMode ) )
1066 return STG_E_ACCESSDENIED;
1069 * Initialize the out parameter
1074 * Create a property enumeration and search the properties
1076 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1077 This->base.rootPropertySetIndex);
1079 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1082 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1084 if (foundPropertyIndex != PROPERTY_NULL)
1087 * An element with this name already exists
1089 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1090 IStorage_DestroyElement(iface, pwcsName);
1092 return STG_E_FILEALREADYEXISTS;
1096 * memset the empty property
1098 memset(&newProperty, 0, sizeof(StgProperty));
1100 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1102 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1103 return STG_E_INVALIDNAME;
1105 strcpyW(newProperty.name, pwcsName);
1107 newProperty.propertyType = PROPTYPE_STORAGE;
1108 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1109 newProperty.size.u.LowPart = 0;
1110 newProperty.size.u.HighPart = 0;
1112 newProperty.previousProperty = PROPERTY_NULL;
1113 newProperty.nextProperty = PROPERTY_NULL;
1114 newProperty.dirProperty = PROPERTY_NULL;
1116 /* call CoFileTime to get the current time
1117 newProperty.timeStampS1
1118 newProperty.timeStampD1
1119 newProperty.timeStampS2
1120 newProperty.timeStampD2
1123 /* newStorageProperty.propertyUniqueID */
1126 * Obtain a free property in the property chain
1128 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1131 * Save the new property into the new property spot
1133 StorageImpl_WriteProperty(
1134 This->base.ancestorStorage,
1139 * Find a spot in the property chain for our newly created property.
1141 updatePropertyChain(
1147 * Open it to get a pointer to return.
1149 hr = IStorage_OpenStorage(
1151 (const OLECHAR*)pwcsName,
1158 if( (hr != S_OK) || (*ppstg == NULL))
1168 /***************************************************************************
1172 * Get a free property or create a new one.
1174 static ULONG getFreeProperty(
1175 StorageImpl *storage)
1177 ULONG currentPropertyIndex = 0;
1178 ULONG newPropertyIndex = PROPERTY_NULL;
1179 BOOL readSuccessful = TRUE;
1180 StgProperty currentProperty;
1185 * Start by reading the root property
1187 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1188 currentPropertyIndex,
1192 if (currentProperty.sizeOfNameString == 0)
1195 * The property existis and is available, we found it.
1197 newPropertyIndex = currentPropertyIndex;
1203 * We exhausted the property list, we will create more space below
1205 newPropertyIndex = currentPropertyIndex;
1207 currentPropertyIndex++;
1209 } while (newPropertyIndex == PROPERTY_NULL);
1212 * grow the property chain
1214 if (! readSuccessful)
1216 StgProperty emptyProperty;
1217 ULARGE_INTEGER newSize;
1218 ULONG propertyIndex;
1219 ULONG lastProperty = 0;
1220 ULONG blockCount = 0;
1223 * obtain the new count of property blocks
1225 blockCount = BlockChainStream_GetCount(
1226 storage->base.ancestorStorage->rootBlockChain)+1;
1229 * initialize the size used by the property stream
1231 newSize.u.HighPart = 0;
1232 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1235 * add a property block to the property chain
1237 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1240 * memset the empty property in order to initialize the unused newly
1243 memset(&emptyProperty, 0, sizeof(StgProperty));
1248 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1251 propertyIndex = newPropertyIndex;
1252 propertyIndex < lastProperty;
1255 StorageImpl_WriteProperty(
1256 storage->base.ancestorStorage,
1262 return newPropertyIndex;
1265 /****************************************************************************
1269 * Case insensitive comparaison of StgProperty.name by first considering
1272 * Returns <0 when newPrpoerty < currentProperty
1273 * >0 when newPrpoerty > currentProperty
1274 * 0 when newPrpoerty == currentProperty
1276 static LONG propertyNameCmp(
1277 const OLECHAR *newProperty,
1278 const OLECHAR *currentProperty)
1280 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1285 * We compare the string themselves only when they are of the same length
1287 diff = lstrcmpiW( newProperty, currentProperty);
1293 /****************************************************************************
1297 * Properly link this new element in the property chain.
1299 static void updatePropertyChain(
1300 StorageImpl *storage,
1301 ULONG newPropertyIndex,
1302 StgProperty newProperty)
1304 StgProperty currentProperty;
1307 * Read the root property
1309 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1310 storage->base.rootPropertySetIndex,
1313 if (currentProperty.dirProperty != PROPERTY_NULL)
1316 * The root storage contains some element, therefore, start the research
1317 * for the appropriate location.
1320 ULONG current, next, previous, currentPropertyId;
1323 * Keep the StgProperty sequence number of the storage first property
1325 currentPropertyId = currentProperty.dirProperty;
1330 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1331 currentProperty.dirProperty,
1334 previous = currentProperty.previousProperty;
1335 next = currentProperty.nextProperty;
1336 current = currentPropertyId;
1340 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1344 if (previous != PROPERTY_NULL)
1346 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1353 currentProperty.previousProperty = newPropertyIndex;
1354 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1362 if (next != PROPERTY_NULL)
1364 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1371 currentProperty.nextProperty = newPropertyIndex;
1372 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1381 * Trying to insert an item with the same name in the
1382 * subtree structure.
1387 previous = currentProperty.previousProperty;
1388 next = currentProperty.nextProperty;
1394 * The root storage is empty, link the new property to it's dir property
1396 currentProperty.dirProperty = newPropertyIndex;
1397 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1398 storage->base.rootPropertySetIndex,
1404 /*************************************************************************
1407 HRESULT WINAPI StorageImpl_CopyTo(
1409 DWORD ciidExclude, /* [in] */
1410 const IID* rgiidExclude, /* [size_is][unique][in] */
1411 SNB snbExclude, /* [unique][in] */
1412 IStorage* pstgDest) /* [unique][in] */
1414 IEnumSTATSTG *elements = 0;
1415 STATSTG curElement, strStat;
1417 IStorage *pstgTmp, *pstgChild;
1418 IStream *pstrTmp, *pstrChild;
1420 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1421 FIXME("Exclude option not implemented\n");
1423 TRACE("(%p, %ld, %p, %p, %p)\n",
1424 iface, ciidExclude, rgiidExclude,
1425 snbExclude, pstgDest);
1428 * Perform a sanity check
1430 if ( pstgDest == 0 )
1431 return STG_E_INVALIDPOINTER;
1434 * Enumerate the elements
1436 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1444 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1445 IStorage_SetClass( pstgDest, &curElement.clsid );
1450 * Obtain the next element
1452 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1454 if ( hr == S_FALSE )
1456 hr = S_OK; /* done, every element has been copied */
1460 if (curElement.type == STGTY_STORAGE)
1463 * open child source storage
1465 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1466 STGM_READ|STGM_SHARE_EXCLUSIVE,
1467 NULL, 0, &pstgChild );
1473 * Check if destination storage is not a child of the source
1474 * storage, which will cause an infinite loop
1476 if (pstgChild == pstgDest)
1478 IEnumSTATSTG_Release(elements);
1480 return STG_E_ACCESSDENIED;
1484 * create a new storage in destination storage
1486 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1487 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1491 * if it already exist, don't create a new one use this one
1493 if (hr == STG_E_FILEALREADYEXISTS)
1495 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1496 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1497 NULL, 0, &pstgTmp );
1505 * do the copy recursively
1507 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1508 snbExclude, pstgTmp );
1510 IStorage_Release( pstgTmp );
1511 IStorage_Release( pstgChild );
1513 else if (curElement.type == STGTY_STREAM)
1516 * create a new stream in destination storage. If the stream already
1517 * exist, it will be deleted and a new one will be created.
1519 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1520 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1527 * open child stream storage
1529 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1530 STGM_READ|STGM_SHARE_EXCLUSIVE,
1537 * Get the size of the source stream
1539 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1542 * Set the size of the destination stream.
1544 IStream_SetSize(pstrTmp, strStat.cbSize);
1549 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1552 IStream_Release( pstrTmp );
1553 IStream_Release( pstrChild );
1557 WARN("unknown element type: %ld\n", curElement.type);
1560 } while (hr == S_OK);
1565 IEnumSTATSTG_Release(elements);
1570 /*************************************************************************
1571 * MoveElementTo (IStorage)
1573 HRESULT WINAPI StorageImpl_MoveElementTo(
1575 const OLECHAR *pwcsName, /* [string][in] */
1576 IStorage *pstgDest, /* [unique][in] */
1577 const OLECHAR *pwcsNewName,/* [string][in] */
1578 DWORD grfFlags) /* [in] */
1580 FIXME("not implemented!\n");
1584 /*************************************************************************
1587 HRESULT WINAPI StorageImpl_Commit(
1589 DWORD grfCommitFlags)/* [in] */
1591 FIXME("(%ld): stub!\n", grfCommitFlags);
1595 /*************************************************************************
1598 HRESULT WINAPI StorageImpl_Revert(
1601 FIXME("not implemented!\n");
1605 /*************************************************************************
1606 * DestroyElement (IStorage)
1608 * Stategy: This implementation is build this way for simplicity not for speed.
1609 * I always delete the top most element of the enumeration and adjust
1610 * the deleted element pointer all the time. This takes longer to
1611 * do but allow to reinvoke DestroyElement whenever we encounter a
1612 * storage object. The optimisation reside in the usage of another
1613 * enumeration stategy that would give all the leaves of a storage
1614 * first. (postfix order)
1616 HRESULT WINAPI StorageImpl_DestroyElement(
1618 const OLECHAR *pwcsName)/* [string][in] */
1620 StorageImpl* const This=(StorageImpl*)iface;
1622 IEnumSTATSTGImpl* propertyEnumeration;
1625 StgProperty propertyToDelete;
1626 StgProperty parentProperty;
1627 ULONG foundPropertyIndexToDelete;
1628 ULONG typeOfRelation;
1629 ULONG parentPropertyId;
1632 iface, debugstr_w(pwcsName));
1635 * Perform a sanity check on the parameters.
1638 return STG_E_INVALIDPOINTER;
1641 * Create a property enumeration to search the property with the given name
1643 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1644 This->base.ancestorStorage,
1645 This->base.rootPropertySetIndex);
1647 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1648 propertyEnumeration,
1652 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1654 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1656 return STG_E_FILENOTFOUND;
1660 * Find the parent property of the property to delete (the one that
1661 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1662 * the parent is This. Otherwise, the parent is one of it's sibling...
1666 * First, read This's StgProperty..
1668 res = StorageImpl_ReadProperty(
1669 This->base.ancestorStorage,
1670 This->base.rootPropertySetIndex,
1676 * Second, check to see if by any chance the actual storage (This) is not
1677 * the parent of the property to delete... We never know...
1679 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1682 * Set data as it would have been done in the else part...
1684 typeOfRelation = PROPERTY_RELATION_DIR;
1685 parentPropertyId = This->base.rootPropertySetIndex;
1690 * Create a property enumeration to search the parent properties, and
1691 * delete it once done.
1693 IEnumSTATSTGImpl* propertyEnumeration2;
1695 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1696 This->base.ancestorStorage,
1697 This->base.rootPropertySetIndex);
1699 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1700 propertyEnumeration2,
1701 foundPropertyIndexToDelete,
1705 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1708 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1710 hr = deleteStorageProperty(
1712 foundPropertyIndexToDelete,
1715 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1717 hr = deleteStreamProperty(
1719 foundPropertyIndexToDelete,
1727 * Adjust the property chain
1729 hr = adjustPropertyChain(
1740 /************************************************************************
1741 * StorageImpl_Stat (IStorage)
1743 * This method will retrieve information about this storage object.
1745 * See Windows documentation for more details on IStorage methods.
1747 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1748 STATSTG* pstatstg, /* [out] */
1749 DWORD grfStatFlag) /* [in] */
1751 StorageImpl* const This = (StorageImpl*)iface;
1752 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1754 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1756 CoTaskMemFree(pstatstg->pwcsName);
1757 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1758 strcpyW(pstatstg->pwcsName, This->pwcsName);
1766 /*********************************************************************
1770 * Perform the deletion of a complete storage node
1773 static HRESULT deleteStorageProperty(
1774 StorageImpl *parentStorage,
1775 ULONG indexOfPropertyToDelete,
1776 StgProperty propertyToDelete)
1778 IEnumSTATSTG *elements = 0;
1779 IStorage *childStorage = 0;
1780 STATSTG currentElement;
1782 HRESULT destroyHr = S_OK;
1785 * Open the storage and enumerate it
1787 hr = StorageBaseImpl_OpenStorage(
1788 (IStorage*)parentStorage,
1789 propertyToDelete.name,
1791 STGM_SHARE_EXCLUSIVE,
1802 * Enumerate the elements
1804 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1809 * Obtain the next element
1811 hr = IEnumSTATSTG_Next(elements, 1, ¤tElement, NULL);
1814 destroyHr = StorageImpl_DestroyElement(
1815 (IStorage*)childStorage,
1816 (OLECHAR*)currentElement.pwcsName);
1818 CoTaskMemFree(currentElement.pwcsName);
1822 * We need to Reset the enumeration every time because we delete elements
1823 * and the enumeration could be invalid
1825 IEnumSTATSTG_Reset(elements);
1827 } while ((hr == S_OK) && (destroyHr == S_OK));
1830 * Invalidate the property by zeroing it's name member.
1832 propertyToDelete.sizeOfNameString = 0;
1834 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1835 indexOfPropertyToDelete,
1838 IStorage_Release(childStorage);
1839 IEnumSTATSTG_Release(elements);
1844 /*********************************************************************
1848 * Perform the deletion of a stream node
1851 static HRESULT deleteStreamProperty(
1852 StorageImpl *parentStorage,
1853 ULONG indexOfPropertyToDelete,
1854 StgProperty propertyToDelete)
1858 ULARGE_INTEGER size;
1860 size.u.HighPart = 0;
1863 hr = StorageBaseImpl_OpenStream(
1864 (IStorage*)parentStorage,
1865 (OLECHAR*)propertyToDelete.name,
1867 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1879 hr = IStream_SetSize(pis, size);
1887 * Release the stream object.
1889 IStream_Release(pis);
1892 * Invalidate the property by zeroing it's name member.
1894 propertyToDelete.sizeOfNameString = 0;
1897 * Here we should re-read the property so we get the updated pointer
1898 * but since we are here to zap it, I don't do it...
1900 StorageImpl_WriteProperty(
1901 parentStorage->base.ancestorStorage,
1902 indexOfPropertyToDelete,
1908 /*********************************************************************
1912 * Finds a placeholder for the StgProperty within the Storage
1915 static HRESULT findPlaceholder(
1916 StorageImpl *storage,
1917 ULONG propertyIndexToStore,
1918 ULONG storePropertyIndex,
1921 StgProperty storeProperty;
1926 * Read the storage property
1928 res = StorageImpl_ReadProperty(
1929 storage->base.ancestorStorage,
1938 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1940 if (storeProperty.previousProperty != PROPERTY_NULL)
1942 return findPlaceholder(
1944 propertyIndexToStore,
1945 storeProperty.previousProperty,
1950 storeProperty.previousProperty = propertyIndexToStore;
1953 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1955 if (storeProperty.nextProperty != PROPERTY_NULL)
1957 return findPlaceholder(
1959 propertyIndexToStore,
1960 storeProperty.nextProperty,
1965 storeProperty.nextProperty = propertyIndexToStore;
1968 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1970 if (storeProperty.dirProperty != PROPERTY_NULL)
1972 return findPlaceholder(
1974 propertyIndexToStore,
1975 storeProperty.dirProperty,
1980 storeProperty.dirProperty = propertyIndexToStore;
1984 hr = StorageImpl_WriteProperty(
1985 storage->base.ancestorStorage,
1997 /*************************************************************************
2001 * This method takes the previous and the next property link of a property
2002 * to be deleted and find them a place in the Storage.
2004 static HRESULT adjustPropertyChain(
2006 StgProperty propertyToDelete,
2007 StgProperty parentProperty,
2008 ULONG parentPropertyId,
2011 ULONG newLinkProperty = PROPERTY_NULL;
2012 BOOL needToFindAPlaceholder = FALSE;
2013 ULONG storeNode = PROPERTY_NULL;
2014 ULONG toStoreNode = PROPERTY_NULL;
2015 INT relationType = 0;
2019 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2021 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2024 * Set the parent previous to the property to delete previous
2026 newLinkProperty = propertyToDelete.previousProperty;
2028 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2031 * We also need to find a storage for the other link, setup variables
2032 * to do this at the end...
2034 needToFindAPlaceholder = TRUE;
2035 storeNode = propertyToDelete.previousProperty;
2036 toStoreNode = propertyToDelete.nextProperty;
2037 relationType = PROPERTY_RELATION_NEXT;
2040 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2043 * Set the parent previous to the property to delete next
2045 newLinkProperty = propertyToDelete.nextProperty;
2049 * Link it for real...
2051 parentProperty.previousProperty = newLinkProperty;
2054 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2056 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2059 * Set the parent next to the property to delete next previous
2061 newLinkProperty = propertyToDelete.previousProperty;
2063 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2066 * We also need to find a storage for the other link, setup variables
2067 * to do this at the end...
2069 needToFindAPlaceholder = TRUE;
2070 storeNode = propertyToDelete.previousProperty;
2071 toStoreNode = propertyToDelete.nextProperty;
2072 relationType = PROPERTY_RELATION_NEXT;
2075 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2078 * Set the parent next to the property to delete next
2080 newLinkProperty = propertyToDelete.nextProperty;
2084 * Link it for real...
2086 parentProperty.nextProperty = newLinkProperty;
2088 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2090 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2093 * Set the parent dir to the property to delete previous
2095 newLinkProperty = propertyToDelete.previousProperty;
2097 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2100 * We also need to find a storage for the other link, setup variables
2101 * to do this at the end...
2103 needToFindAPlaceholder = TRUE;
2104 storeNode = propertyToDelete.previousProperty;
2105 toStoreNode = propertyToDelete.nextProperty;
2106 relationType = PROPERTY_RELATION_NEXT;
2109 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2112 * Set the parent dir to the property to delete next
2114 newLinkProperty = propertyToDelete.nextProperty;
2118 * Link it for real...
2120 parentProperty.dirProperty = newLinkProperty;
2124 * Write back the parent property
2126 res = StorageImpl_WriteProperty(
2127 This->base.ancestorStorage,
2136 * If a placeholder is required for the other link, then, find one and
2137 * get out of here...
2139 if (needToFindAPlaceholder)
2141 hr = findPlaceholder(
2152 /******************************************************************************
2153 * SetElementTimes (IStorage)
2155 HRESULT WINAPI StorageImpl_SetElementTimes(
2157 const OLECHAR *pwcsName,/* [string][in] */
2158 const FILETIME *pctime, /* [in] */
2159 const FILETIME *patime, /* [in] */
2160 const FILETIME *pmtime) /* [in] */
2162 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2166 /******************************************************************************
2167 * SetStateBits (IStorage)
2169 HRESULT WINAPI StorageImpl_SetStateBits(
2171 DWORD grfStateBits,/* [in] */
2172 DWORD grfMask) /* [in] */
2174 FIXME("not implemented!\n");
2179 * Virtual function table for the IStorage32Impl class.
2181 static IStorageVtbl Storage32Impl_Vtbl =
2183 StorageBaseImpl_QueryInterface,
2184 StorageBaseImpl_AddRef,
2185 StorageBaseImpl_Release,
2186 StorageBaseImpl_CreateStream,
2187 StorageBaseImpl_OpenStream,
2188 StorageImpl_CreateStorage,
2189 StorageBaseImpl_OpenStorage,
2191 StorageImpl_MoveElementTo,
2194 StorageBaseImpl_EnumElements,
2195 StorageImpl_DestroyElement,
2196 StorageBaseImpl_RenameElement,
2197 StorageImpl_SetElementTimes,
2198 StorageBaseImpl_SetClass,
2199 StorageImpl_SetStateBits,
2203 HRESULT StorageImpl_Construct(
2213 StgProperty currentProperty;
2214 BOOL readSuccessful;
2215 ULONG currentPropertyIndex;
2217 if ( FAILED( validateSTGM(openFlags) ))
2218 return STG_E_INVALIDFLAG;
2220 memset(This, 0, sizeof(StorageImpl));
2223 * Initialize the virtual function table.
2225 This->base.lpVtbl = &Storage32Impl_Vtbl;
2226 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2227 This->base.v_destructor = &StorageImpl_Destroy;
2228 This->base.openFlags = openFlags;
2231 * This is the top-level storage so initialize the ancestor pointer
2234 This->base.ancestorStorage = This;
2237 * Initialize the physical support of the storage.
2239 This->hFile = hFile;
2242 * Store copy of file path.
2245 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2246 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2247 if (!This->pwcsName)
2248 return STG_E_INSUFFICIENTMEMORY;
2249 strcpyW(This->pwcsName, pwcsName);
2253 * Initialize the big block cache.
2255 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2256 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2257 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2263 if (This->bigBlockFile == 0)
2268 ULARGE_INTEGER size;
2269 BYTE* bigBlockBuffer;
2272 * Initialize all header variables:
2273 * - The big block depot consists of one block and it is at block 0
2274 * - The properties start at block 1
2275 * - There is no small block depot
2277 memset( This->bigBlockDepotStart,
2279 sizeof(This->bigBlockDepotStart));
2281 This->bigBlockDepotCount = 1;
2282 This->bigBlockDepotStart[0] = 0;
2283 This->rootStartBlock = 1;
2284 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2285 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2286 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2287 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2288 This->extBigBlockDepotCount = 0;
2290 StorageImpl_SaveFileHeader(This);
2293 * Add one block for the big block depot and one block for the properties
2295 size.u.HighPart = 0;
2296 size.u.LowPart = This->bigBlockSize * 3;
2297 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2300 * Initialize the big block depot
2302 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2303 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2304 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2305 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2306 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2311 * Load the header for the file.
2313 hr = StorageImpl_LoadFileHeader(This);
2317 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2324 * There is no block depot cached yet.
2326 This->indexBlockDepotCached = 0xFFFFFFFF;
2329 * Start searching for free blocks with block 0.
2331 This->prevFreeBlock = 0;
2334 * Create the block chain abstractions.
2336 if(!(This->rootBlockChain =
2337 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2338 return STG_E_READFAULT;
2340 if(!(This->smallBlockDepotChain =
2341 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2343 return STG_E_READFAULT;
2346 * Write the root property
2350 StgProperty rootProp;
2352 * Initialize the property chain
2354 memset(&rootProp, 0, sizeof(rootProp));
2355 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2356 sizeof(rootProp.name)/sizeof(WCHAR) );
2357 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2358 rootProp.propertyType = PROPTYPE_ROOT;
2359 rootProp.previousProperty = PROPERTY_NULL;
2360 rootProp.nextProperty = PROPERTY_NULL;
2361 rootProp.dirProperty = PROPERTY_NULL;
2362 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2363 rootProp.size.u.HighPart = 0;
2364 rootProp.size.u.LowPart = 0;
2366 StorageImpl_WriteProperty(This, 0, &rootProp);
2370 * Find the ID of the root in the property sets.
2372 currentPropertyIndex = 0;
2376 readSuccessful = StorageImpl_ReadProperty(
2378 currentPropertyIndex,
2383 if ( (currentProperty.sizeOfNameString != 0 ) &&
2384 (currentProperty.propertyType == PROPTYPE_ROOT) )
2386 This->base.rootPropertySetIndex = currentPropertyIndex;
2390 currentPropertyIndex++;
2392 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2394 if (!readSuccessful)
2397 return STG_E_READFAULT;
2401 * Create the block chain abstraction for the small block root chain.
2403 if(!(This->smallBlockRootChain =
2404 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2405 return STG_E_READFAULT;
2410 void StorageImpl_Destroy(StorageBaseImpl* iface)
2412 StorageImpl *This = (StorageImpl*) iface;
2413 TRACE("(%p)\n", This);
2415 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2417 BlockChainStream_Destroy(This->smallBlockRootChain);
2418 BlockChainStream_Destroy(This->rootBlockChain);
2419 BlockChainStream_Destroy(This->smallBlockDepotChain);
2421 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2422 HeapFree(GetProcessHeap(), 0, This);
2425 /******************************************************************************
2426 * Storage32Impl_GetNextFreeBigBlock
2428 * Returns the index of the next free big block.
2429 * If the big block depot is filled, this method will enlarge it.
2432 ULONG StorageImpl_GetNextFreeBigBlock(
2435 ULONG depotBlockIndexPos;
2437 ULONG depotBlockOffset;
2438 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2439 ULONG nextBlockIndex = BLOCK_SPECIAL;
2441 ULONG freeBlock = BLOCK_UNUSED;
2443 depotIndex = This->prevFreeBlock / blocksPerDepot;
2444 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2447 * Scan the entire big block depot until we find a block marked free
2449 while (nextBlockIndex != BLOCK_UNUSED)
2451 if (depotIndex < COUNT_BBDEPOTINHEADER)
2453 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2456 * Grow the primary depot.
2458 if (depotBlockIndexPos == BLOCK_UNUSED)
2460 depotBlockIndexPos = depotIndex*blocksPerDepot;
2463 * Add a block depot.
2465 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2466 This->bigBlockDepotCount++;
2467 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2470 * Flag it as a block depot.
2472 StorageImpl_SetNextBlockInChain(This,
2476 /* Save new header information.
2478 StorageImpl_SaveFileHeader(This);
2483 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2485 if (depotBlockIndexPos == BLOCK_UNUSED)
2488 * Grow the extended depot.
2490 ULONG extIndex = BLOCK_UNUSED;
2491 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2492 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2494 if (extBlockOffset == 0)
2496 /* We need an extended block.
2498 extIndex = Storage32Impl_AddExtBlockDepot(This);
2499 This->extBigBlockDepotCount++;
2500 depotBlockIndexPos = extIndex + 1;
2503 depotBlockIndexPos = depotIndex * blocksPerDepot;
2506 * Add a block depot and mark it in the extended block.
2508 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2509 This->bigBlockDepotCount++;
2510 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2512 /* Flag the block depot.
2514 StorageImpl_SetNextBlockInChain(This,
2518 /* If necessary, flag the extended depot block.
2520 if (extIndex != BLOCK_UNUSED)
2521 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2523 /* Save header information.
2525 StorageImpl_SaveFileHeader(This);
2529 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2531 if (depotBuffer != 0)
2533 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2534 ( nextBlockIndex != BLOCK_UNUSED))
2536 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2538 if (nextBlockIndex == BLOCK_UNUSED)
2540 freeBlock = (depotIndex * blocksPerDepot) +
2541 (depotBlockOffset/sizeof(ULONG));
2544 depotBlockOffset += sizeof(ULONG);
2547 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2551 depotBlockOffset = 0;
2554 This->prevFreeBlock = freeBlock;
2559 /******************************************************************************
2560 * Storage32Impl_AddBlockDepot
2562 * This will create a depot block, essentially it is a block initialized
2565 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2569 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2572 * Initialize blocks as free
2574 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2576 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2579 /******************************************************************************
2580 * Storage32Impl_GetExtDepotBlock
2582 * Returns the index of the block that corresponds to the specified depot
2583 * index. This method is only for depot indexes equal or greater than
2584 * COUNT_BBDEPOTINHEADER.
2586 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2588 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2589 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2590 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2591 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2592 ULONG blockIndex = BLOCK_UNUSED;
2593 ULONG extBlockIndex = This->extBigBlockDepotStart;
2595 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2597 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2598 return BLOCK_UNUSED;
2600 while (extBlockCount > 0)
2602 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2606 if (extBlockIndex != BLOCK_UNUSED)
2610 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2612 if (depotBuffer != 0)
2614 StorageUtl_ReadDWord(depotBuffer,
2615 extBlockOffset * sizeof(ULONG),
2618 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2625 /******************************************************************************
2626 * Storage32Impl_SetExtDepotBlock
2628 * Associates the specified block index to the specified depot index.
2629 * This method is only for depot indexes equal or greater than
2630 * COUNT_BBDEPOTINHEADER.
2632 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2636 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2637 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2638 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2639 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2640 ULONG extBlockIndex = This->extBigBlockDepotStart;
2642 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2644 while (extBlockCount > 0)
2646 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2650 if (extBlockIndex != BLOCK_UNUSED)
2654 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2656 if (depotBuffer != 0)
2658 StorageUtl_WriteDWord(depotBuffer,
2659 extBlockOffset * sizeof(ULONG),
2662 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2667 /******************************************************************************
2668 * Storage32Impl_AddExtBlockDepot
2670 * Creates an extended depot block.
2672 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2674 ULONG numExtBlocks = This->extBigBlockDepotCount;
2675 ULONG nextExtBlock = This->extBigBlockDepotStart;
2676 BYTE* depotBuffer = NULL;
2677 ULONG index = BLOCK_UNUSED;
2678 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2679 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2680 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2682 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2683 blocksPerDepotBlock;
2685 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2688 * The first extended block.
2690 This->extBigBlockDepotStart = index;
2696 * Follow the chain to the last one.
2698 for (i = 0; i < (numExtBlocks - 1); i++)
2700 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2704 * Add the new extended block to the chain.
2706 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2707 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2708 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2712 * Initialize this block.
2714 depotBuffer = StorageImpl_GetBigBlock(This, index);
2715 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2716 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2721 /******************************************************************************
2722 * Storage32Impl_FreeBigBlock
2724 * This method will flag the specified block as free in the big block depot.
2726 void StorageImpl_FreeBigBlock(
2730 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2732 if (blockIndex < This->prevFreeBlock)
2733 This->prevFreeBlock = blockIndex;
2736 /************************************************************************
2737 * Storage32Impl_GetNextBlockInChain
2739 * This method will retrieve the block index of the next big block in
2742 * Params: This - Pointer to the Storage object.
2743 * blockIndex - Index of the block to retrieve the chain
2745 * nextBlockIndex - receives the return value.
2747 * Returns: This method returns the index of the next block in the chain.
2748 * It will return the constants:
2749 * BLOCK_SPECIAL - If the block given was not part of a
2751 * BLOCK_END_OF_CHAIN - If the block given was the last in
2753 * BLOCK_UNUSED - If the block given was not past of a chain
2755 * BLOCK_EXTBBDEPOT - This block is part of the extended
2758 * See Windows documentation for more details on IStorage methods.
2760 HRESULT StorageImpl_GetNextBlockInChain(
2763 ULONG* nextBlockIndex)
2765 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2766 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2767 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2769 ULONG depotBlockIndexPos;
2772 *nextBlockIndex = BLOCK_SPECIAL;
2774 if(depotBlockCount >= This->bigBlockDepotCount)
2776 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2777 This->bigBlockDepotCount);
2778 return STG_E_READFAULT;
2782 * Cache the currently accessed depot block.
2784 if (depotBlockCount != This->indexBlockDepotCached)
2786 This->indexBlockDepotCached = depotBlockCount;
2788 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2790 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2795 * We have to look in the extended depot.
2797 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2800 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2803 return STG_E_READFAULT;
2805 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2807 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2808 This->blockDepotCached[index] = *nextBlockIndex;
2810 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2813 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2818 /******************************************************************************
2819 * Storage32Impl_GetNextExtendedBlock
2821 * Given an extended block this method will return the next extended block.
2824 * The last ULONG of an extended block is the block index of the next
2825 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2829 * - The index of the next extended block
2830 * - BLOCK_UNUSED: there is no next extended block.
2831 * - Any other return values denotes failure.
2833 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2835 ULONG nextBlockIndex = BLOCK_SPECIAL;
2836 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2839 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2843 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2845 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2848 return nextBlockIndex;
2851 /******************************************************************************
2852 * Storage32Impl_SetNextBlockInChain
2854 * This method will write the index of the specified block's next block
2855 * in the big block depot.
2857 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2860 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2861 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2862 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2865 void StorageImpl_SetNextBlockInChain(
2870 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2871 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2872 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2873 ULONG depotBlockIndexPos;
2876 assert(depotBlockCount < This->bigBlockDepotCount);
2877 assert(blockIndex != nextBlock);
2879 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2881 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2886 * We have to look in the extended depot.
2888 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2891 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2895 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2896 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2900 * Update the cached block depot, if necessary.
2902 if (depotBlockCount == This->indexBlockDepotCached)
2904 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2908 /******************************************************************************
2909 * Storage32Impl_LoadFileHeader
2911 * This method will read in the file header, i.e. big block index -1.
2913 HRESULT StorageImpl_LoadFileHeader(
2916 HRESULT hr = STG_E_FILENOTFOUND;
2917 void* headerBigBlock = NULL;
2921 * Get a pointer to the big block of data containing the header.
2923 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2926 * Extract the information from the header.
2928 if (headerBigBlock!=0)
2931 * Check for the "magic number" signature and return an error if it is not
2934 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2936 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2937 return STG_E_OLDFORMAT;
2940 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2942 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2943 return STG_E_INVALIDHEADER;
2946 StorageUtl_ReadWord(
2948 OFFSET_BIGBLOCKSIZEBITS,
2949 &This->bigBlockSizeBits);
2951 StorageUtl_ReadWord(
2953 OFFSET_SMALLBLOCKSIZEBITS,
2954 &This->smallBlockSizeBits);
2956 StorageUtl_ReadDWord(
2958 OFFSET_BBDEPOTCOUNT,
2959 &This->bigBlockDepotCount);
2961 StorageUtl_ReadDWord(
2963 OFFSET_ROOTSTARTBLOCK,
2964 &This->rootStartBlock);
2966 StorageUtl_ReadDWord(
2968 OFFSET_SBDEPOTSTART,
2969 &This->smallBlockDepotStart);
2971 StorageUtl_ReadDWord(
2973 OFFSET_EXTBBDEPOTSTART,
2974 &This->extBigBlockDepotStart);
2976 StorageUtl_ReadDWord(
2978 OFFSET_EXTBBDEPOTCOUNT,
2979 &This->extBigBlockDepotCount);
2981 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2983 StorageUtl_ReadDWord(
2985 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2986 &(This->bigBlockDepotStart[index]));
2990 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2994 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2995 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2999 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3000 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3004 * Right now, the code is making some assumptions about the size of the
3005 * blocks, just make sure they are what we're expecting.
3007 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3008 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3010 WARN("Broken OLE storage file\n");
3011 hr = STG_E_INVALIDHEADER;
3017 * Release the block.
3019 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3025 /******************************************************************************
3026 * Storage32Impl_SaveFileHeader
3028 * This method will save to the file the header, i.e. big block -1.
3030 void StorageImpl_SaveFileHeader(
3033 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3038 * Get a pointer to the big block of data containing the header.
3040 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3043 * If the block read failed, the file is probably new.
3048 * Initialize for all unknown fields.
3050 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3053 * Initialize the magic number.
3055 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3058 * And a bunch of things we don't know what they mean
3060 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3061 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3062 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3063 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3067 * Write the information to the header.
3069 StorageUtl_WriteWord(
3071 OFFSET_BIGBLOCKSIZEBITS,
3072 This->bigBlockSizeBits);
3074 StorageUtl_WriteWord(
3076 OFFSET_SMALLBLOCKSIZEBITS,
3077 This->smallBlockSizeBits);
3079 StorageUtl_WriteDWord(
3081 OFFSET_BBDEPOTCOUNT,
3082 This->bigBlockDepotCount);
3084 StorageUtl_WriteDWord(
3086 OFFSET_ROOTSTARTBLOCK,
3087 This->rootStartBlock);
3089 StorageUtl_WriteDWord(
3091 OFFSET_SBDEPOTSTART,
3092 This->smallBlockDepotStart);
3094 StorageUtl_WriteDWord(
3096 OFFSET_SBDEPOTCOUNT,
3097 This->smallBlockDepotChain ?
3098 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3100 StorageUtl_WriteDWord(
3102 OFFSET_EXTBBDEPOTSTART,
3103 This->extBigBlockDepotStart);
3105 StorageUtl_WriteDWord(
3107 OFFSET_EXTBBDEPOTCOUNT,
3108 This->extBigBlockDepotCount);
3110 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3112 StorageUtl_WriteDWord(
3114 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3115 (This->bigBlockDepotStart[index]));
3119 * Write the big block back to the file.
3121 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3124 /******************************************************************************
3125 * Storage32Impl_ReadProperty
3127 * This method will read the specified property from the property chain.
3129 BOOL StorageImpl_ReadProperty(
3132 StgProperty* buffer)
3134 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3135 ULARGE_INTEGER offsetInPropSet;
3136 BOOL readSuccessful;
3139 offsetInPropSet.u.HighPart = 0;
3140 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3142 readSuccessful = BlockChainStream_ReadAt(
3143 This->rootBlockChain,
3151 /* replace the name of root entry (often "Root Entry") by the file name */
3152 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3153 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3155 memset(buffer->name, 0, sizeof(buffer->name));
3159 PROPERTY_NAME_BUFFER_LEN );
3160 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3162 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3164 StorageUtl_ReadWord(
3166 OFFSET_PS_NAMELENGTH,
3167 &buffer->sizeOfNameString);
3169 StorageUtl_ReadDWord(
3171 OFFSET_PS_PREVIOUSPROP,
3172 &buffer->previousProperty);
3174 StorageUtl_ReadDWord(
3177 &buffer->nextProperty);
3179 StorageUtl_ReadDWord(
3182 &buffer->dirProperty);
3184 StorageUtl_ReadGUID(
3187 &buffer->propertyUniqueID);
3189 StorageUtl_ReadDWord(
3192 &buffer->timeStampS1);
3194 StorageUtl_ReadDWord(
3197 &buffer->timeStampD1);
3199 StorageUtl_ReadDWord(
3202 &buffer->timeStampS2);
3204 StorageUtl_ReadDWord(
3207 &buffer->timeStampD2);
3209 StorageUtl_ReadDWord(
3211 OFFSET_PS_STARTBLOCK,
3212 &buffer->startingBlock);
3214 StorageUtl_ReadDWord(
3217 &buffer->size.u.LowPart);
3219 buffer->size.u.HighPart = 0;
3222 return readSuccessful;
3225 /*********************************************************************
3226 * Write the specified property into the property chain
3228 BOOL StorageImpl_WriteProperty(
3231 StgProperty* buffer)
3233 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3234 ULARGE_INTEGER offsetInPropSet;
3235 BOOL writeSuccessful;
3238 offsetInPropSet.u.HighPart = 0;
3239 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3241 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3244 currentProperty + OFFSET_PS_NAME,
3246 PROPERTY_NAME_BUFFER_LEN );
3248 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3250 StorageUtl_WriteWord(
3252 OFFSET_PS_NAMELENGTH,
3253 buffer->sizeOfNameString);
3255 StorageUtl_WriteDWord(
3257 OFFSET_PS_PREVIOUSPROP,
3258 buffer->previousProperty);
3260 StorageUtl_WriteDWord(
3263 buffer->nextProperty);
3265 StorageUtl_WriteDWord(
3268 buffer->dirProperty);
3270 StorageUtl_WriteGUID(
3273 &buffer->propertyUniqueID);
3275 StorageUtl_WriteDWord(
3278 buffer->timeStampS1);
3280 StorageUtl_WriteDWord(
3283 buffer->timeStampD1);
3285 StorageUtl_WriteDWord(
3288 buffer->timeStampS2);
3290 StorageUtl_WriteDWord(
3293 buffer->timeStampD2);
3295 StorageUtl_WriteDWord(
3297 OFFSET_PS_STARTBLOCK,
3298 buffer->startingBlock);
3300 StorageUtl_WriteDWord(
3303 buffer->size.u.LowPart);
3305 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3310 return writeSuccessful;
3313 BOOL StorageImpl_ReadBigBlock(
3318 void* bigBlockBuffer;
3320 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3322 if (bigBlockBuffer!=0)
3324 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3326 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3334 BOOL StorageImpl_WriteBigBlock(
3339 void* bigBlockBuffer;
3341 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3343 if (bigBlockBuffer!=0)
3345 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3347 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3355 void* StorageImpl_GetROBigBlock(
3359 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3362 void* StorageImpl_GetBigBlock(
3366 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3369 void StorageImpl_ReleaseBigBlock(
3373 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3376 /******************************************************************************
3377 * Storage32Impl_SmallBlocksToBigBlocks
3379 * This method will convert a small block chain to a big block chain.
3380 * The small block chain will be destroyed.
3382 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3384 SmallBlockChainStream** ppsbChain)
3386 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3387 ULARGE_INTEGER size, offset;
3388 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3389 ULONG propertyIndex;
3390 BOOL successRead, successWrite;
3391 StgProperty chainProperty;
3393 BlockChainStream *bbTempChain = NULL;
3394 BlockChainStream *bigBlockChain = NULL;
3397 * Create a temporary big block chain that doesn't have
3398 * an associated property. This temporary chain will be
3399 * used to copy data from small blocks to big blocks.
3401 bbTempChain = BlockChainStream_Construct(This,
3404 if(!bbTempChain) return NULL;
3406 * Grow the big block chain.
3408 size = SmallBlockChainStream_GetSize(*ppsbChain);
3409 BlockChainStream_SetSize(bbTempChain, size);
3412 * Copy the contents of the small block chain to the big block chain
3413 * by small block size increments.
3415 offset.u.LowPart = 0;
3416 offset.u.HighPart = 0;
3420 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3423 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3425 DEF_SMALL_BLOCK_SIZE,
3428 cbTotalRead += cbRead;
3430 successWrite = BlockChainStream_WriteAt(bbTempChain,
3435 cbTotalWritten += cbWritten;
3437 offset.u.LowPart += This->smallBlockSize;
3439 } while (successRead && successWrite);
3440 HeapFree(GetProcessHeap(),0,buffer);
3442 assert(cbTotalRead == cbTotalWritten);
3445 * Destroy the small block chain.
3447 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3448 size.u.HighPart = 0;
3450 SmallBlockChainStream_SetSize(*ppsbChain, size);
3451 SmallBlockChainStream_Destroy(*ppsbChain);
3455 * Change the property information. This chain is now a big block chain
3456 * and it doesn't reside in the small blocks chain anymore.
3458 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3460 chainProperty.startingBlock = bbHeadOfChain;
3462 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3465 * Destroy the temporary propertyless big block chain.
3466 * Create a new big block chain associated with this property.
3468 BlockChainStream_Destroy(bbTempChain);
3469 bigBlockChain = BlockChainStream_Construct(This,
3473 return bigBlockChain;
3476 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3478 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3480 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3481 HeapFree(GetProcessHeap(), 0, This);
3484 /******************************************************************************
3486 ** Storage32InternalImpl_Commit
3488 ** The non-root storages cannot be opened in transacted mode thus this function
3491 HRESULT WINAPI StorageInternalImpl_Commit(
3493 DWORD grfCommitFlags) /* [in] */
3498 /******************************************************************************
3500 ** Storage32InternalImpl_Revert
3502 ** The non-root storages cannot be opened in transacted mode thus this function
3505 HRESULT WINAPI StorageInternalImpl_Revert(
3511 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3513 IStorage_Release((IStorage*)This->parentStorage);
3514 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3515 HeapFree(GetProcessHeap(), 0, This);
3518 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3519 IEnumSTATSTG* iface,
3523 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3526 * Perform a sanity check on the parameters.
3529 return E_INVALIDARG;
3532 * Initialize the return parameter.
3537 * Compare the riid with the interface IDs implemented by this object.
3539 if (IsEqualGUID(&IID_IUnknown, riid) ||
3540 IsEqualGUID(&IID_IStorage, riid))
3542 *ppvObject = (IEnumSTATSTG*)This;
3543 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3547 return E_NOINTERFACE;
3550 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3551 IEnumSTATSTG* iface)
3553 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3554 return InterlockedIncrement(&This->ref);
3557 ULONG WINAPI IEnumSTATSTGImpl_Release(
3558 IEnumSTATSTG* iface)
3560 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3564 newRef = InterlockedDecrement(&This->ref);
3567 * If the reference count goes down to 0, perform suicide.
3571 IEnumSTATSTGImpl_Destroy(This);
3577 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3578 IEnumSTATSTG* iface,
3581 ULONG* pceltFetched)
3583 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3585 StgProperty currentProperty;
3586 STATSTG* currentReturnStruct = rgelt;
3587 ULONG objectFetched = 0;
3588 ULONG currentSearchNode;
3591 * Perform a sanity check on the parameters.
3593 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3594 return E_INVALIDARG;
3597 * To avoid the special case, get another pointer to a ULONG value if
3598 * the caller didn't supply one.
3600 if (pceltFetched==0)
3601 pceltFetched = &objectFetched;
3604 * Start the iteration, we will iterate until we hit the end of the
3605 * linked list or until we hit the number of items to iterate through
3610 * Start with the node at the top of the stack.
3612 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3614 while ( ( *pceltFetched < celt) &&
3615 ( currentSearchNode!=PROPERTY_NULL) )
3618 * Remove the top node from the stack
3620 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3623 * Read the property from the storage.
3625 StorageImpl_ReadProperty(This->parentStorage,
3630 * Copy the information to the return buffer.
3632 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3637 * Step to the next item in the iteration
3640 currentReturnStruct++;
3643 * Push the next search node in the search stack.
3645 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3648 * continue the iteration.
3650 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3653 if (*pceltFetched == celt)
3660 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3661 IEnumSTATSTG* iface,
3664 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3666 StgProperty currentProperty;
3667 ULONG objectFetched = 0;
3668 ULONG currentSearchNode;
3671 * Start with the node at the top of the stack.
3673 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3675 while ( (objectFetched < celt) &&
3676 (currentSearchNode!=PROPERTY_NULL) )
3679 * Remove the top node from the stack
3681 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3684 * Read the property from the storage.
3686 StorageImpl_ReadProperty(This->parentStorage,
3691 * Step to the next item in the iteration
3696 * Push the next search node in the search stack.
3698 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3701 * continue the iteration.
3703 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3706 if (objectFetched == celt)
3712 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3713 IEnumSTATSTG* iface)
3715 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3717 StgProperty rootProperty;
3718 BOOL readSuccessful;
3721 * Re-initialize the search stack to an empty stack
3723 This->stackSize = 0;
3726 * Read the root property from the storage.
3728 readSuccessful = StorageImpl_ReadProperty(
3729 This->parentStorage,
3730 This->firstPropertyNode,
3735 assert(rootProperty.sizeOfNameString!=0);
3738 * Push the search node in the search stack.
3740 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3746 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3747 IEnumSTATSTG* iface,
3748 IEnumSTATSTG** ppenum)
3750 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3752 IEnumSTATSTGImpl* newClone;
3755 * Perform a sanity check on the parameters.
3758 return E_INVALIDARG;
3760 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3761 This->firstPropertyNode);
3765 * The new clone enumeration must point to the same current node as
3768 newClone->stackSize = This->stackSize ;
3769 newClone->stackMaxSize = This->stackMaxSize ;
3770 newClone->stackToVisit =
3771 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3774 newClone->stackToVisit,
3776 sizeof(ULONG) * newClone->stackSize);
3778 *ppenum = (IEnumSTATSTG*)newClone;
3781 * Don't forget to nail down a reference to the clone before
3784 IEnumSTATSTGImpl_AddRef(*ppenum);
3789 INT IEnumSTATSTGImpl_FindParentProperty(
3790 IEnumSTATSTGImpl *This,
3791 ULONG childProperty,
3792 StgProperty *currentProperty,
3795 ULONG currentSearchNode;
3799 * To avoid the special case, get another pointer to a ULONG value if
3800 * the caller didn't supply one.
3803 thisNodeId = &foundNode;
3806 * Start with the node at the top of the stack.
3808 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3811 while (currentSearchNode!=PROPERTY_NULL)
3814 * Store the current node in the returned parameters
3816 *thisNodeId = currentSearchNode;
3819 * Remove the top node from the stack
3821 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3824 * Read the property from the storage.
3826 StorageImpl_ReadProperty(
3827 This->parentStorage,
3831 if (currentProperty->previousProperty == childProperty)
3832 return PROPERTY_RELATION_PREVIOUS;
3834 else if (currentProperty->nextProperty == childProperty)
3835 return PROPERTY_RELATION_NEXT;
3837 else if (currentProperty->dirProperty == childProperty)
3838 return PROPERTY_RELATION_DIR;
3841 * Push the next search node in the search stack.
3843 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3846 * continue the iteration.
3848 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3851 return PROPERTY_NULL;
3854 ULONG IEnumSTATSTGImpl_FindProperty(
3855 IEnumSTATSTGImpl* This,
3856 const OLECHAR* lpszPropName,
3857 StgProperty* currentProperty)
3859 ULONG currentSearchNode;
3862 * Start with the node at the top of the stack.
3864 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3866 while (currentSearchNode!=PROPERTY_NULL)
3869 * Remove the top node from the stack
3871 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3874 * Read the property from the storage.
3876 StorageImpl_ReadProperty(This->parentStorage,
3880 if ( propertyNameCmp(
3881 (const OLECHAR*)currentProperty->name,
3882 (const OLECHAR*)lpszPropName) == 0)
3883 return currentSearchNode;
3886 * Push the next search node in the search stack.
3888 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3891 * continue the iteration.
3893 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3896 return PROPERTY_NULL;
3899 void IEnumSTATSTGImpl_PushSearchNode(
3900 IEnumSTATSTGImpl* This,
3903 StgProperty rootProperty;
3904 BOOL readSuccessful;
3907 * First, make sure we're not trying to push an unexisting node.
3909 if (nodeToPush==PROPERTY_NULL)
3913 * First push the node to the stack
3915 if (This->stackSize == This->stackMaxSize)
3917 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3919 This->stackToVisit = HeapReAlloc(
3923 sizeof(ULONG) * This->stackMaxSize);
3926 This->stackToVisit[This->stackSize] = nodeToPush;
3930 * Read the root property from the storage.
3932 readSuccessful = StorageImpl_ReadProperty(
3933 This->parentStorage,
3939 assert(rootProperty.sizeOfNameString!=0);
3942 * Push the previous search node in the search stack.
3944 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3948 ULONG IEnumSTATSTGImpl_PopSearchNode(
3949 IEnumSTATSTGImpl* This,
3954 if (This->stackSize == 0)
3955 return PROPERTY_NULL;
3957 topNode = This->stackToVisit[This->stackSize-1];
3966 * Virtual function table for the IEnumSTATSTGImpl class.
3968 static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
3970 IEnumSTATSTGImpl_QueryInterface,
3971 IEnumSTATSTGImpl_AddRef,
3972 IEnumSTATSTGImpl_Release,
3973 IEnumSTATSTGImpl_Next,
3974 IEnumSTATSTGImpl_Skip,
3975 IEnumSTATSTGImpl_Reset,
3976 IEnumSTATSTGImpl_Clone
3979 /******************************************************************************
3980 ** IEnumSTATSTGImpl implementation
3983 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3984 StorageImpl* parentStorage,
3985 ULONG firstPropertyNode)
3987 IEnumSTATSTGImpl* newEnumeration;
3989 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3991 if (newEnumeration!=0)
3994 * Set-up the virtual function table and reference count.
3996 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3997 newEnumeration->ref = 0;
4000 * We want to nail-down the reference to the storage in case the
4001 * enumeration out-lives the storage in the client application.
4003 newEnumeration->parentStorage = parentStorage;
4004 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4006 newEnumeration->firstPropertyNode = firstPropertyNode;
4009 * Initialize the search stack
4011 newEnumeration->stackSize = 0;
4012 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4013 newEnumeration->stackToVisit =
4014 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4017 * Make sure the current node of the iterator is the first one.
4019 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4022 return newEnumeration;
4026 * Virtual function table for the Storage32InternalImpl class.
4028 static IStorageVtbl Storage32InternalImpl_Vtbl =
4030 StorageBaseImpl_QueryInterface,
4031 StorageBaseImpl_AddRef,
4032 StorageBaseImpl_Release,
4033 StorageBaseImpl_CreateStream,
4034 StorageBaseImpl_OpenStream,
4035 StorageImpl_CreateStorage,
4036 StorageBaseImpl_OpenStorage,
4038 StorageImpl_MoveElementTo,
4039 StorageInternalImpl_Commit,
4040 StorageInternalImpl_Revert,
4041 StorageBaseImpl_EnumElements,
4042 StorageImpl_DestroyElement,
4043 StorageBaseImpl_RenameElement,
4044 StorageImpl_SetElementTimes,
4045 StorageBaseImpl_SetClass,
4046 StorageImpl_SetStateBits,
4047 StorageBaseImpl_Stat
4050 /******************************************************************************
4051 ** Storage32InternalImpl implementation
4054 StorageInternalImpl* StorageInternalImpl_Construct(
4055 StorageImpl* ancestorStorage,
4057 ULONG rootPropertyIndex)
4059 StorageInternalImpl* newStorage;
4062 * Allocate space for the new storage object
4064 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4068 memset(newStorage, 0, sizeof(StorageInternalImpl));
4071 * Initialize the virtual function table.
4073 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4074 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4075 newStorage->base.openFlags = openFlags;
4078 * Keep the ancestor storage pointer and nail a reference to it.
4080 newStorage->base.ancestorStorage = ancestorStorage;
4081 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4084 * Keep the index of the root property set for this storage,
4086 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4094 /******************************************************************************
4095 ** StorageUtl implementation
4098 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4102 memcpy(&tmp, buffer+offset, sizeof(WORD));
4103 *value = le16toh(tmp);
4106 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4108 value = htole16(value);
4109 memcpy(buffer+offset, &value, sizeof(WORD));
4112 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4116 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4117 *value = le32toh(tmp);
4120 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4122 value = htole32(value);
4123 memcpy(buffer+offset, &value, sizeof(DWORD));
4126 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4128 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4129 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4130 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4132 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4135 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4137 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4138 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4139 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4141 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4144 void StorageUtl_CopyPropertyToSTATSTG(
4145 STATSTG* destination,
4146 StgProperty* source,
4150 * The copy of the string occurs only when the flag is not set
4152 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4153 (source->name == NULL) ||
4154 (source->name[0] == 0) )
4156 destination->pwcsName = 0;
4160 destination->pwcsName =
4161 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4163 strcpyW((LPWSTR)destination->pwcsName, source->name);
4166 switch (source->propertyType)
4168 case PROPTYPE_STORAGE:
4170 destination->type = STGTY_STORAGE;
4172 case PROPTYPE_STREAM:
4173 destination->type = STGTY_STREAM;
4176 destination->type = STGTY_STREAM;
4180 destination->cbSize = source->size;
4182 currentReturnStruct->mtime = {0}; TODO
4183 currentReturnStruct->ctime = {0};
4184 currentReturnStruct->atime = {0};
4186 destination->grfMode = 0;
4187 destination->grfLocksSupported = 0;
4188 destination->clsid = source->propertyUniqueID;
4189 destination->grfStateBits = 0;
4190 destination->reserved = 0;
4193 /******************************************************************************
4194 ** BlockChainStream implementation
4197 BlockChainStream* BlockChainStream_Construct(
4198 StorageImpl* parentStorage,
4199 ULONG* headOfStreamPlaceHolder,
4200 ULONG propertyIndex)
4202 BlockChainStream* newStream;
4205 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4207 newStream->parentStorage = parentStorage;
4208 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4209 newStream->ownerPropertyIndex = propertyIndex;
4210 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4211 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4212 newStream->numBlocks = 0;
4214 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4216 while (blockIndex != BLOCK_END_OF_CHAIN)
4218 newStream->numBlocks++;
4219 newStream->tailIndex = blockIndex;
4221 if(FAILED(StorageImpl_GetNextBlockInChain(
4226 HeapFree(GetProcessHeap(), 0, newStream);
4234 void BlockChainStream_Destroy(BlockChainStream* This)
4236 HeapFree(GetProcessHeap(), 0, This);
4239 /******************************************************************************
4240 * BlockChainStream_GetHeadOfChain
4242 * Returns the head of this stream chain.
4243 * Some special chains don't have properties, their heads are kept in
4244 * This->headOfStreamPlaceHolder.
4247 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4249 StgProperty chainProperty;
4250 BOOL readSuccessful;
4252 if (This->headOfStreamPlaceHolder != 0)
4253 return *(This->headOfStreamPlaceHolder);
4255 if (This->ownerPropertyIndex != PROPERTY_NULL)
4257 readSuccessful = StorageImpl_ReadProperty(
4258 This->parentStorage,
4259 This->ownerPropertyIndex,
4264 return chainProperty.startingBlock;
4268 return BLOCK_END_OF_CHAIN;
4271 /******************************************************************************
4272 * BlockChainStream_GetCount
4274 * Returns the number of blocks that comprises this chain.
4275 * This is not the size of the stream as the last block may not be full!
4278 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4283 blockIndex = BlockChainStream_GetHeadOfChain(This);
4285 while (blockIndex != BLOCK_END_OF_CHAIN)
4289 if(FAILED(StorageImpl_GetNextBlockInChain(
4290 This->parentStorage,
4299 /******************************************************************************
4300 * BlockChainStream_ReadAt
4302 * Reads a specified number of bytes from this chain at the specified offset.
4303 * bytesRead may be NULL.
4304 * Failure will be returned if the specified number of bytes has not been read.
4306 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4307 ULARGE_INTEGER offset,
4312 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4313 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4314 ULONG bytesToReadInBuffer;
4317 BYTE* bigBlockBuffer;
4320 * Find the first block in the stream that contains part of the buffer.
4322 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4323 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4324 (blockNoInSequence < This->lastBlockNoInSequence) )
4326 blockIndex = BlockChainStream_GetHeadOfChain(This);
4327 This->lastBlockNoInSequence = blockNoInSequence;
4331 ULONG temp = blockNoInSequence;
4333 blockIndex = This->lastBlockNoInSequenceIndex;
4334 blockNoInSequence -= This->lastBlockNoInSequence;
4335 This->lastBlockNoInSequence = temp;
4338 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4340 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4342 blockNoInSequence--;
4345 This->lastBlockNoInSequenceIndex = blockIndex;
4348 * Start reading the buffer.
4351 bufferWalker = buffer;
4353 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4356 * Calculate how many bytes we can copy from this big block.
4358 bytesToReadInBuffer =
4359 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4362 * Copy those bytes to the buffer
4365 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4367 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4369 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4372 * Step to the next big block.
4374 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4377 bufferWalker += bytesToReadInBuffer;
4378 size -= bytesToReadInBuffer;
4379 *bytesRead += bytesToReadInBuffer;
4380 offsetInBlock = 0; /* There is no offset on the next block */
4387 /******************************************************************************
4388 * BlockChainStream_WriteAt
4390 * Writes the specified number of bytes to this chain at the specified offset.
4391 * bytesWritten may be NULL.
4392 * Will fail if not all specified number of bytes have been written.
4394 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4395 ULARGE_INTEGER offset,
4398 ULONG* bytesWritten)
4400 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4401 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4404 const BYTE* bufferWalker;
4405 BYTE* bigBlockBuffer;
4408 * Find the first block in the stream that contains part of the buffer.
4410 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4411 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4412 (blockNoInSequence < This->lastBlockNoInSequence) )
4414 blockIndex = BlockChainStream_GetHeadOfChain(This);
4415 This->lastBlockNoInSequence = blockNoInSequence;
4419 ULONG temp = blockNoInSequence;
4421 blockIndex = This->lastBlockNoInSequenceIndex;
4422 blockNoInSequence -= This->lastBlockNoInSequence;
4423 This->lastBlockNoInSequence = temp;
4426 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4428 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4431 blockNoInSequence--;
4434 This->lastBlockNoInSequenceIndex = blockIndex;
4437 * Here, I'm casting away the constness on the buffer variable
4438 * This is OK since we don't intend to modify that buffer.
4441 bufferWalker = (const BYTE*)buffer;
4443 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4446 * Calculate how many bytes we can copy from this big block.
4449 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4452 * Copy those bytes to the buffer
4454 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4456 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4458 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4461 * Step to the next big block.
4463 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4466 bufferWalker += bytesToWrite;
4467 size -= bytesToWrite;
4468 *bytesWritten += bytesToWrite;
4469 offsetInBlock = 0; /* There is no offset on the next block */
4475 /******************************************************************************
4476 * BlockChainStream_Shrink
4478 * Shrinks this chain in the big block depot.
4480 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4481 ULARGE_INTEGER newSize)
4483 ULONG blockIndex, extraBlock;
4488 * Reset the last accessed block cache.
4490 This->lastBlockNoInSequence = 0xFFFFFFFF;
4491 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4494 * Figure out how many blocks are needed to contain the new size
4496 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4498 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4501 blockIndex = BlockChainStream_GetHeadOfChain(This);
4504 * Go to the new end of chain
4506 while (count < numBlocks)
4508 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4514 /* Get the next block before marking the new end */
4515 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4519 /* Mark the new end of chain */
4520 StorageImpl_SetNextBlockInChain(
4521 This->parentStorage,
4523 BLOCK_END_OF_CHAIN);
4525 This->tailIndex = blockIndex;
4526 This->numBlocks = numBlocks;
4529 * Mark the extra blocks as free
4531 while (extraBlock != BLOCK_END_OF_CHAIN)
4533 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4536 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4537 extraBlock = blockIndex;
4543 /******************************************************************************
4544 * BlockChainStream_Enlarge
4546 * Grows this chain in the big block depot.
4548 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4549 ULARGE_INTEGER newSize)
4551 ULONG blockIndex, currentBlock;
4553 ULONG oldNumBlocks = 0;
4555 blockIndex = BlockChainStream_GetHeadOfChain(This);
4558 * Empty chain. Create the head.
4560 if (blockIndex == BLOCK_END_OF_CHAIN)
4562 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4563 StorageImpl_SetNextBlockInChain(This->parentStorage,
4565 BLOCK_END_OF_CHAIN);
4567 if (This->headOfStreamPlaceHolder != 0)
4569 *(This->headOfStreamPlaceHolder) = blockIndex;
4573 StgProperty chainProp;
4574 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4576 StorageImpl_ReadProperty(
4577 This->parentStorage,
4578 This->ownerPropertyIndex,
4581 chainProp.startingBlock = blockIndex;
4583 StorageImpl_WriteProperty(
4584 This->parentStorage,
4585 This->ownerPropertyIndex,
4589 This->tailIndex = blockIndex;
4590 This->numBlocks = 1;
4594 * Figure out how many blocks are needed to contain this stream
4596 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4598 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4602 * Go to the current end of chain
4604 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4606 currentBlock = blockIndex;
4608 while (blockIndex != BLOCK_END_OF_CHAIN)
4611 currentBlock = blockIndex;
4613 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4618 This->tailIndex = currentBlock;
4621 currentBlock = This->tailIndex;
4622 oldNumBlocks = This->numBlocks;
4625 * Add new blocks to the chain
4627 if (oldNumBlocks < newNumBlocks)
4629 while (oldNumBlocks < newNumBlocks)
4631 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4633 StorageImpl_SetNextBlockInChain(
4634 This->parentStorage,
4638 StorageImpl_SetNextBlockInChain(
4639 This->parentStorage,
4641 BLOCK_END_OF_CHAIN);
4643 currentBlock = blockIndex;
4647 This->tailIndex = blockIndex;
4648 This->numBlocks = newNumBlocks;
4654 /******************************************************************************
4655 * BlockChainStream_SetSize
4657 * Sets the size of this stream. The big block depot will be updated.
4658 * The file will grow if we grow the chain.
4660 * TODO: Free the actual blocks in the file when we shrink the chain.
4661 * Currently, the blocks are still in the file. So the file size
4662 * doesn't shrink even if we shrink streams.
4664 BOOL BlockChainStream_SetSize(
4665 BlockChainStream* This,
4666 ULARGE_INTEGER newSize)
4668 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4670 if (newSize.u.LowPart == size.u.LowPart)
4673 if (newSize.u.LowPart < size.u.LowPart)
4675 BlockChainStream_Shrink(This, newSize);
4679 ULARGE_INTEGER fileSize =
4680 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4682 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4685 * Make sure the file stays a multiple of blocksize
4687 if ((diff % This->parentStorage->bigBlockSize) != 0)
4688 diff += (This->parentStorage->bigBlockSize -
4689 (diff % This->parentStorage->bigBlockSize) );
4691 fileSize.u.LowPart += diff;
4692 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4694 BlockChainStream_Enlarge(This, newSize);
4700 /******************************************************************************
4701 * BlockChainStream_GetSize
4703 * Returns the size of this chain.
4704 * Will return the block count if this chain doesn't have a property.
4706 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4708 StgProperty chainProperty;
4710 if(This->headOfStreamPlaceHolder == NULL)
4713 * This chain is a data stream read the property and return
4714 * the appropriate size
4716 StorageImpl_ReadProperty(
4717 This->parentStorage,
4718 This->ownerPropertyIndex,
4721 return chainProperty.size;
4726 * this chain is a chain that does not have a property, figure out the
4727 * size by making the product number of used blocks times the
4730 ULARGE_INTEGER result;
4731 result.u.HighPart = 0;
4734 BlockChainStream_GetCount(This) *
4735 This->parentStorage->bigBlockSize;
4741 /******************************************************************************
4742 ** SmallBlockChainStream implementation
4745 SmallBlockChainStream* SmallBlockChainStream_Construct(
4746 StorageImpl* parentStorage,
4747 ULONG propertyIndex)
4749 SmallBlockChainStream* newStream;
4751 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4753 newStream->parentStorage = parentStorage;
4754 newStream->ownerPropertyIndex = propertyIndex;
4759 void SmallBlockChainStream_Destroy(
4760 SmallBlockChainStream* This)
4762 HeapFree(GetProcessHeap(), 0, This);
4765 /******************************************************************************
4766 * SmallBlockChainStream_GetHeadOfChain
4768 * Returns the head of this chain of small blocks.
4770 ULONG SmallBlockChainStream_GetHeadOfChain(
4771 SmallBlockChainStream* This)
4773 StgProperty chainProperty;
4774 BOOL readSuccessful;
4776 if (This->ownerPropertyIndex)
4778 readSuccessful = StorageImpl_ReadProperty(
4779 This->parentStorage,
4780 This->ownerPropertyIndex,
4785 return chainProperty.startingBlock;
4790 return BLOCK_END_OF_CHAIN;
4793 /******************************************************************************
4794 * SmallBlockChainStream_GetNextBlockInChain
4796 * Returns the index of the next small block in this chain.
4799 * - BLOCK_END_OF_CHAIN: end of this chain
4800 * - BLOCK_UNUSED: small block 'blockIndex' is free
4802 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4803 SmallBlockChainStream* This,
4805 ULONG* nextBlockInChain)
4807 ULARGE_INTEGER offsetOfBlockInDepot;
4812 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4814 offsetOfBlockInDepot.u.HighPart = 0;
4815 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4818 * Read those bytes in the buffer from the small block file.
4820 success = BlockChainStream_ReadAt(
4821 This->parentStorage->smallBlockDepotChain,
4822 offsetOfBlockInDepot,
4829 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4833 return STG_E_READFAULT;
4836 /******************************************************************************
4837 * SmallBlockChainStream_SetNextBlockInChain
4839 * Writes the index of the next block of the specified block in the small
4841 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4842 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4844 void SmallBlockChainStream_SetNextBlockInChain(
4845 SmallBlockChainStream* This,
4849 ULARGE_INTEGER offsetOfBlockInDepot;
4853 offsetOfBlockInDepot.u.HighPart = 0;
4854 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4856 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4859 * Read those bytes in the buffer from the small block file.
4861 BlockChainStream_WriteAt(
4862 This->parentStorage->smallBlockDepotChain,
4863 offsetOfBlockInDepot,
4869 /******************************************************************************
4870 * SmallBlockChainStream_FreeBlock
4872 * Flag small block 'blockIndex' as free in the small block depot.
4874 void SmallBlockChainStream_FreeBlock(
4875 SmallBlockChainStream* This,
4878 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4881 /******************************************************************************
4882 * SmallBlockChainStream_GetNextFreeBlock
4884 * Returns the index of a free small block. The small block depot will be
4885 * enlarged if necessary. The small block chain will also be enlarged if
4888 ULONG SmallBlockChainStream_GetNextFreeBlock(
4889 SmallBlockChainStream* This)
4891 ULARGE_INTEGER offsetOfBlockInDepot;
4894 ULONG blockIndex = 0;
4895 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4896 BOOL success = TRUE;
4897 ULONG smallBlocksPerBigBlock;
4899 offsetOfBlockInDepot.u.HighPart = 0;
4902 * Scan the small block depot for a free block
4904 while (nextBlockIndex != BLOCK_UNUSED)
4906 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4908 success = BlockChainStream_ReadAt(
4909 This->parentStorage->smallBlockDepotChain,
4910 offsetOfBlockInDepot,
4916 * If we run out of space for the small block depot, enlarge it
4920 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4922 if (nextBlockIndex != BLOCK_UNUSED)
4928 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4930 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4931 ULONG nextBlock, newsbdIndex;
4932 BYTE* smallBlockDepot;
4934 nextBlock = sbdIndex;
4935 while (nextBlock != BLOCK_END_OF_CHAIN)
4937 sbdIndex = nextBlock;
4938 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4941 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4942 if (sbdIndex != BLOCK_END_OF_CHAIN)
4943 StorageImpl_SetNextBlockInChain(
4944 This->parentStorage,
4948 StorageImpl_SetNextBlockInChain(
4949 This->parentStorage,
4951 BLOCK_END_OF_CHAIN);
4954 * Initialize all the small blocks to free
4957 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4959 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4960 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4965 * We have just created the small block depot.
4967 StgProperty rootProp;
4971 * Save it in the header
4973 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4974 StorageImpl_SaveFileHeader(This->parentStorage);
4977 * And allocate the first big block that will contain small blocks
4980 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4982 StorageImpl_SetNextBlockInChain(
4983 This->parentStorage,
4985 BLOCK_END_OF_CHAIN);
4987 StorageImpl_ReadProperty(
4988 This->parentStorage,
4989 This->parentStorage->base.rootPropertySetIndex,
4992 rootProp.startingBlock = sbStartIndex;
4993 rootProp.size.u.HighPart = 0;
4994 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
4996 StorageImpl_WriteProperty(
4997 This->parentStorage,
4998 This->parentStorage->base.rootPropertySetIndex,
5004 smallBlocksPerBigBlock =
5005 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5008 * Verify if we have to allocate big blocks to contain small blocks
5010 if (blockIndex % smallBlocksPerBigBlock == 0)
5012 StgProperty rootProp;
5013 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5015 StorageImpl_ReadProperty(
5016 This->parentStorage,
5017 This->parentStorage->base.rootPropertySetIndex,
5020 if (rootProp.size.u.LowPart <
5021 (blocksRequired * This->parentStorage->bigBlockSize))
5023 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5025 BlockChainStream_SetSize(
5026 This->parentStorage->smallBlockRootChain,
5029 StorageImpl_WriteProperty(
5030 This->parentStorage,
5031 This->parentStorage->base.rootPropertySetIndex,
5039 /******************************************************************************
5040 * SmallBlockChainStream_ReadAt
5042 * Reads a specified number of bytes from this chain at the specified offset.
5043 * bytesRead may be NULL.
5044 * Failure will be returned if the specified number of bytes has not been read.
5046 BOOL SmallBlockChainStream_ReadAt(
5047 SmallBlockChainStream* This,
5048 ULARGE_INTEGER offset,
5053 ULARGE_INTEGER offsetInBigBlockFile;
5054 ULONG blockNoInSequence =
5055 offset.u.LowPart / This->parentStorage->smallBlockSize;
5057 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5058 ULONG bytesToReadInBuffer;
5060 ULONG bytesReadFromBigBlockFile;
5064 * This should never happen on a small block file.
5066 assert(offset.u.HighPart==0);
5069 * Find the first block in the stream that contains part of the buffer.
5071 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5073 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5075 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5078 blockNoInSequence--;
5082 * Start reading the buffer.
5085 bufferWalker = buffer;
5087 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5090 * Calculate how many bytes we can copy from this small block.
5092 bytesToReadInBuffer =
5093 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5096 * Calculate the offset of the small block in the small block file.
5098 offsetInBigBlockFile.u.HighPart = 0;
5099 offsetInBigBlockFile.u.LowPart =
5100 blockIndex * This->parentStorage->smallBlockSize;
5102 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5105 * Read those bytes in the buffer from the small block file.
5107 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5108 offsetInBigBlockFile,
5109 bytesToReadInBuffer,
5111 &bytesReadFromBigBlockFile);
5113 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5116 * Step to the next big block.
5118 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5120 bufferWalker += bytesToReadInBuffer;
5121 size -= bytesToReadInBuffer;
5122 *bytesRead += bytesToReadInBuffer;
5123 offsetInBlock = 0; /* There is no offset on the next block */
5129 /******************************************************************************
5130 * SmallBlockChainStream_WriteAt
5132 * Writes the specified number of bytes to this chain at the specified offset.
5133 * bytesWritten may be NULL.
5134 * Will fail if not all specified number of bytes have been written.
5136 BOOL SmallBlockChainStream_WriteAt(
5137 SmallBlockChainStream* This,
5138 ULARGE_INTEGER offset,
5141 ULONG* bytesWritten)
5143 ULARGE_INTEGER offsetInBigBlockFile;
5144 ULONG blockNoInSequence =
5145 offset.u.LowPart / This->parentStorage->smallBlockSize;
5147 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5148 ULONG bytesToWriteInBuffer;
5150 ULONG bytesWrittenFromBigBlockFile;
5151 const BYTE* bufferWalker;
5154 * This should never happen on a small block file.
5156 assert(offset.u.HighPart==0);
5159 * Find the first block in the stream that contains part of the buffer.
5161 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5163 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5165 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5167 blockNoInSequence--;
5171 * Start writing the buffer.
5173 * Here, I'm casting away the constness on the buffer variable
5174 * This is OK since we don't intend to modify that buffer.
5177 bufferWalker = (const BYTE*)buffer;
5178 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5181 * Calculate how many bytes we can copy to this small block.
5183 bytesToWriteInBuffer =
5184 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5187 * Calculate the offset of the small block in the small block file.
5189 offsetInBigBlockFile.u.HighPart = 0;
5190 offsetInBigBlockFile.u.LowPart =
5191 blockIndex * This->parentStorage->smallBlockSize;
5193 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5196 * Write those bytes in the buffer to the small block file.
5198 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5199 offsetInBigBlockFile,
5200 bytesToWriteInBuffer,
5202 &bytesWrittenFromBigBlockFile);
5204 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5207 * Step to the next big block.
5209 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5212 bufferWalker += bytesToWriteInBuffer;
5213 size -= bytesToWriteInBuffer;
5214 *bytesWritten += bytesToWriteInBuffer;
5215 offsetInBlock = 0; /* There is no offset on the next block */
5221 /******************************************************************************
5222 * SmallBlockChainStream_Shrink
5224 * Shrinks this chain in the small block depot.
5226 BOOL SmallBlockChainStream_Shrink(
5227 SmallBlockChainStream* This,
5228 ULARGE_INTEGER newSize)
5230 ULONG blockIndex, extraBlock;
5234 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5236 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5239 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5242 * Go to the new end of chain
5244 while (count < numBlocks)
5246 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5253 * If the count is 0, we have a special case, the head of the chain was
5258 StgProperty chainProp;
5260 StorageImpl_ReadProperty(This->parentStorage,
5261 This->ownerPropertyIndex,
5264 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5266 StorageImpl_WriteProperty(This->parentStorage,
5267 This->ownerPropertyIndex,
5271 * We start freeing the chain at the head block.
5273 extraBlock = blockIndex;
5277 /* Get the next block before marking the new end */
5278 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5282 /* Mark the new end of chain */
5283 SmallBlockChainStream_SetNextBlockInChain(
5286 BLOCK_END_OF_CHAIN);
5290 * Mark the extra blocks as free
5292 while (extraBlock != BLOCK_END_OF_CHAIN)
5294 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5297 SmallBlockChainStream_FreeBlock(This, extraBlock);
5298 extraBlock = blockIndex;
5304 /******************************************************************************
5305 * SmallBlockChainStream_Enlarge
5307 * Grows this chain in the small block depot.
5309 BOOL SmallBlockChainStream_Enlarge(
5310 SmallBlockChainStream* This,
5311 ULARGE_INTEGER newSize)
5313 ULONG blockIndex, currentBlock;
5315 ULONG oldNumBlocks = 0;
5317 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5322 if (blockIndex == BLOCK_END_OF_CHAIN)
5325 StgProperty chainProp;
5327 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5330 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5332 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5335 blockIndex = chainProp.startingBlock;
5336 SmallBlockChainStream_SetNextBlockInChain(
5339 BLOCK_END_OF_CHAIN);
5342 currentBlock = blockIndex;
5345 * Figure out how many blocks are needed to contain this stream
5347 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5349 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5353 * Go to the current end of chain
5355 while (blockIndex != BLOCK_END_OF_CHAIN)
5358 currentBlock = blockIndex;
5359 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5364 * Add new blocks to the chain
5366 while (oldNumBlocks < newNumBlocks)
5368 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5369 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5371 SmallBlockChainStream_SetNextBlockInChain(
5374 BLOCK_END_OF_CHAIN);
5376 currentBlock = blockIndex;
5383 /******************************************************************************
5384 * SmallBlockChainStream_GetCount
5386 * Returns the number of blocks that comprises this chain.
5387 * This is not the size of this chain as the last block may not be full!
5389 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5394 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5396 while (blockIndex != BLOCK_END_OF_CHAIN)
5400 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5407 /******************************************************************************
5408 * SmallBlockChainStream_SetSize
5410 * Sets the size of this stream.
5411 * The file will grow if we grow the chain.
5413 * TODO: Free the actual blocks in the file when we shrink the chain.
5414 * Currently, the blocks are still in the file. So the file size
5415 * doesn't shrink even if we shrink streams.
5417 BOOL SmallBlockChainStream_SetSize(
5418 SmallBlockChainStream* This,
5419 ULARGE_INTEGER newSize)
5421 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5423 if (newSize.u.LowPart == size.u.LowPart)
5426 if (newSize.u.LowPart < size.u.LowPart)
5428 SmallBlockChainStream_Shrink(This, newSize);
5432 SmallBlockChainStream_Enlarge(This, newSize);
5438 /******************************************************************************
5439 * SmallBlockChainStream_GetSize
5441 * Returns the size of this chain.
5443 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5445 StgProperty chainProperty;
5447 StorageImpl_ReadProperty(
5448 This->parentStorage,
5449 This->ownerPropertyIndex,
5452 return chainProperty.size;
5455 /******************************************************************************
5456 * StgCreateDocfile [OLE32.@]
5458 HRESULT WINAPI StgCreateDocfile(
5462 IStorage **ppstgOpen)
5464 StorageImpl* newStorage = 0;
5465 HANDLE hFile = INVALID_HANDLE_VALUE;
5466 HRESULT hr = STG_E_INVALIDFLAG;
5470 DWORD fileAttributes;
5471 WCHAR tempFileName[MAX_PATH];
5473 TRACE("(%s, %lx, %ld, %p)\n",
5474 debugstr_w(pwcsName), grfMode,
5475 reserved, ppstgOpen);
5478 * Validate the parameters
5481 return STG_E_INVALIDPOINTER;
5483 return STG_E_INVALIDPARAMETER;
5486 * Validate the STGM flags
5488 if ( FAILED( validateSTGM(grfMode) ))
5491 /* StgCreateDocFile always opens for write */
5492 switch(STGM_ACCESS_MODE(grfMode))
5495 case STGM_READWRITE:
5501 /* can't share write */
5502 switch(STGM_SHARE_MODE(grfMode))
5504 case STGM_SHARE_EXCLUSIVE:
5505 case STGM_SHARE_DENY_WRITE:
5511 /* shared reading requires transacted mode */
5512 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5513 !(grfMode&STGM_TRANSACTED) )
5517 * Generate a unique name.
5521 WCHAR tempPath[MAX_PATH];
5522 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5524 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
5527 memset(tempPath, 0, sizeof(tempPath));
5528 memset(tempFileName, 0, sizeof(tempFileName));
5530 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5533 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5534 pwcsName = tempFileName;
5537 hr = STG_E_INSUFFICIENTMEMORY;
5541 creationMode = TRUNCATE_EXISTING;
5545 creationMode = GetCreationModeFromSTGM(grfMode);
5549 * Interpret the STGM value grfMode
5551 shareMode = GetShareModeFromSTGM(grfMode);
5552 accessMode = GetAccessModeFromSTGM(grfMode);
5554 if (grfMode & STGM_DELETEONRELEASE)
5555 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5557 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5559 if (grfMode & STGM_TRANSACTED)
5560 FIXME("Transacted mode not implemented.\n");
5563 * Initialize the "out" parameter.
5567 hFile = CreateFileW(pwcsName,
5575 if (hFile == INVALID_HANDLE_VALUE)
5577 if(GetLastError() == ERROR_FILE_EXISTS)
5578 hr = STG_E_FILEALREADYEXISTS;
5585 * Allocate and initialize the new IStorage32object.
5587 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5589 if (newStorage == 0)
5591 hr = STG_E_INSUFFICIENTMEMORY;
5595 hr = StorageImpl_Construct(
5606 HeapFree(GetProcessHeap(), 0, newStorage);
5611 * Get an "out" pointer for the caller.
5613 hr = StorageBaseImpl_QueryInterface(
5614 (IStorage*)newStorage,
5615 (REFIID)&IID_IStorage,
5618 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5623 /******************************************************************************
5624 * StgCreateStorageEx [OLE32.@]
5626 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5628 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5629 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5631 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5633 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5634 return STG_E_INVALIDPARAMETER;
5637 if (stgfmt != STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5639 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5640 return STG_E_INVALIDPARAMETER;
5643 if (stgfmt == STGFMT_FILE)
5645 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5646 return STG_E_INVALIDPARAMETER;
5649 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5651 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5652 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5655 ERR("Invalid stgfmt argument\n");
5656 return STG_E_INVALIDPARAMETER;
5659 /******************************************************************************
5660 * StgCreatePropSetStg [OLE32.@]
5662 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5663 IPropertySetStorage **ppPropSetStg)
5667 TRACE("(%p, 0x%lx, %p): stub\n", pstg, reserved, ppPropSetStg);
5669 hr = STG_E_INVALIDPARAMETER;
5671 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5672 (void**)ppPropSetStg);
5676 /******************************************************************************
5677 * StgOpenStorageEx [OLE32.@]
5679 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5681 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5682 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5684 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5686 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5687 return STG_E_INVALIDPARAMETER;
5690 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5692 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5693 return STG_E_INVALIDPARAMETER;
5696 if (stgfmt == STGFMT_FILE)
5698 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5699 return STG_E_INVALIDPARAMETER;
5702 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE || stgfmt == STGFMT_ANY)
5704 if (stgfmt == STGFMT_ANY)
5705 WARN("STGFMT_ANY assuming storage\n");
5706 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5707 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5710 ERR("Invalid stgfmt argument\n");
5711 return STG_E_INVALIDPARAMETER;
5715 /******************************************************************************
5716 * StgOpenStorage [OLE32.@]
5718 HRESULT WINAPI StgOpenStorage(
5719 const OLECHAR *pwcsName,
5720 IStorage *pstgPriority,
5724 IStorage **ppstgOpen)
5726 StorageImpl* newStorage = 0;
5731 WCHAR fullname[MAX_PATH];
5734 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5735 debugstr_w(pwcsName), pstgPriority, grfMode,
5736 snbExclude, reserved, ppstgOpen);
5739 * Perform sanity checks
5743 hr = STG_E_INVALIDNAME;
5749 hr = STG_E_INVALIDPOINTER;
5755 hr = STG_E_INVALIDPARAMETER;
5760 * Validate the sharing mode
5762 switch(STGM_SHARE_MODE(grfMode))
5764 case STGM_SHARE_EXCLUSIVE:
5765 case STGM_SHARE_DENY_WRITE:
5768 hr = STG_E_INVALIDFLAG;
5773 * Validate the STGM flags
5775 if ( FAILED( validateSTGM(grfMode) ) ||
5776 (grfMode&STGM_CREATE))
5778 hr = STG_E_INVALIDFLAG;
5782 /* shared reading requires transacted mode */
5783 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5784 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5785 !(grfMode&STGM_TRANSACTED) )
5787 hr = STG_E_INVALIDFLAG;
5792 * Interpret the STGM value grfMode
5794 shareMode = GetShareModeFromSTGM(grfMode);
5795 accessMode = GetAccessModeFromSTGM(grfMode);
5798 * Initialize the "out" parameter.
5802 hFile = CreateFileW( pwcsName,
5807 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5810 if (hFile==INVALID_HANDLE_VALUE)
5812 DWORD last_error = GetLastError();
5818 case ERROR_FILE_NOT_FOUND:
5819 hr = STG_E_FILENOTFOUND;
5822 case ERROR_PATH_NOT_FOUND:
5823 hr = STG_E_PATHNOTFOUND;
5826 case ERROR_ACCESS_DENIED:
5827 case ERROR_WRITE_PROTECT:
5828 hr = STG_E_ACCESSDENIED;
5831 case ERROR_SHARING_VIOLATION:
5832 hr = STG_E_SHAREVIOLATION;
5843 * Refuse to open the file if it's too small to be a structured storage file
5844 * FIXME: verify the file when reading instead of here
5846 length = GetFileSize(hFile, NULL);
5850 hr = STG_E_FILEALREADYEXISTS;
5855 * Allocate and initialize the new IStorage32object.
5857 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5859 if (newStorage == 0)
5861 hr = STG_E_INSUFFICIENTMEMORY;
5865 /* if the file's length was zero, initialize the storage */
5866 hr = StorageImpl_Construct(
5877 HeapFree(GetProcessHeap(), 0, newStorage);
5879 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5881 if(hr == STG_E_INVALIDHEADER)
5882 hr = STG_E_FILEALREADYEXISTS;
5886 /* prepare the file name string given in lieu of the root property name */
5887 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5888 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5889 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5892 * Get an "out" pointer for the caller.
5894 hr = StorageBaseImpl_QueryInterface(
5895 (IStorage*)newStorage,
5896 (REFIID)&IID_IStorage,
5900 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5904 /******************************************************************************
5905 * StgCreateDocfileOnILockBytes [OLE32.@]
5907 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5911 IStorage** ppstgOpen)
5913 StorageImpl* newStorage = 0;
5917 * Validate the parameters
5919 if ((ppstgOpen == 0) || (plkbyt == 0))
5920 return STG_E_INVALIDPOINTER;
5923 * Allocate and initialize the new IStorage object.
5925 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5927 if (newStorage == 0)
5928 return STG_E_INSUFFICIENTMEMORY;
5930 hr = StorageImpl_Construct(
5941 HeapFree(GetProcessHeap(), 0, newStorage);
5946 * Get an "out" pointer for the caller.
5948 hr = StorageBaseImpl_QueryInterface(
5949 (IStorage*)newStorage,
5950 (REFIID)&IID_IStorage,
5956 /******************************************************************************
5957 * StgOpenStorageOnILockBytes [OLE32.@]
5959 HRESULT WINAPI StgOpenStorageOnILockBytes(
5961 IStorage *pstgPriority,
5965 IStorage **ppstgOpen)
5967 StorageImpl* newStorage = 0;
5971 * Perform a sanity check
5973 if ((plkbyt == 0) || (ppstgOpen == 0))
5974 return STG_E_INVALIDPOINTER;
5977 * Validate the STGM flags
5979 if ( FAILED( validateSTGM(grfMode) ))
5980 return STG_E_INVALIDFLAG;
5983 * Initialize the "out" parameter.
5988 * Allocate and initialize the new IStorage object.
5990 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5992 if (newStorage == 0)
5993 return STG_E_INSUFFICIENTMEMORY;
5995 hr = StorageImpl_Construct(
6006 HeapFree(GetProcessHeap(), 0, newStorage);
6011 * Get an "out" pointer for the caller.
6013 hr = StorageBaseImpl_QueryInterface(
6014 (IStorage*)newStorage,
6015 (REFIID)&IID_IStorage,
6021 /******************************************************************************
6022 * StgSetTimes [ole32.@]
6023 * StgSetTimes [OLE32.@]
6027 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6028 FILETIME const *patime, FILETIME const *pmtime)
6030 IStorage *stg = NULL;
6033 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6035 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6039 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6040 IStorage_Release(stg);
6046 /******************************************************************************
6047 * StgIsStorageILockBytes [OLE32.@]
6049 * Determines if the ILockBytes contains a storage object.
6051 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6054 ULARGE_INTEGER offset;
6056 offset.u.HighPart = 0;
6057 offset.u.LowPart = 0;
6059 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6061 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6067 /******************************************************************************
6068 * WriteClassStg [OLE32.@]
6070 * This method will store the specified CLSID in the specified storage object
6072 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6078 hRes = IStorage_SetClass(pStg, rclsid);
6083 /***********************************************************************
6084 * ReadClassStg (OLE32.@)
6086 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
6088 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6098 * read a STATSTG structure (contains the clsid) from the storage
6100 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6103 *pclsid=pstatstg.clsid;
6108 /***********************************************************************
6109 * OleLoadFromStream (OLE32.@)
6111 * This function loads an object from stream
6113 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6117 LPPERSISTSTREAM xstm;
6119 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6121 res=ReadClassStm(pStm,&clsid);
6122 if (!SUCCEEDED(res))
6124 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6125 if (!SUCCEEDED(res))
6127 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6128 if (!SUCCEEDED(res)) {
6129 IUnknown_Release((IUnknown*)*ppvObj);
6132 res=IPersistStream_Load(xstm,pStm);
6133 IPersistStream_Release(xstm);
6134 /* FIXME: all refcounts ok at this point? I think they should be:
6137 * xstm : 0 (released)
6142 /***********************************************************************
6143 * OleSaveToStream (OLE32.@)
6145 * This function saves an object with the IPersistStream interface on it
6146 * to the specified stream.
6148 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6154 TRACE("(%p,%p)\n",pPStm,pStm);
6156 res=IPersistStream_GetClassID(pPStm,&clsid);
6158 if (SUCCEEDED(res)){
6160 res=WriteClassStm(pStm,&clsid);
6164 res=IPersistStream_Save(pPStm,pStm,TRUE);
6167 TRACE("Finished Save\n");
6171 /****************************************************************************
6172 * This method validate a STGM parameter that can contain the values below
6174 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6175 * The stgm values contained in 0xffff0000 are bitmasks.
6177 * STGM_DIRECT 0x00000000
6178 * STGM_TRANSACTED 0x00010000
6179 * STGM_SIMPLE 0x08000000
6181 * STGM_READ 0x00000000
6182 * STGM_WRITE 0x00000001
6183 * STGM_READWRITE 0x00000002
6185 * STGM_SHARE_DENY_NONE 0x00000040
6186 * STGM_SHARE_DENY_READ 0x00000030
6187 * STGM_SHARE_DENY_WRITE 0x00000020
6188 * STGM_SHARE_EXCLUSIVE 0x00000010
6190 * STGM_PRIORITY 0x00040000
6191 * STGM_DELETEONRELEASE 0x04000000
6193 * STGM_CREATE 0x00001000
6194 * STGM_CONVERT 0x00020000
6195 * STGM_FAILIFTHERE 0x00000000
6197 * STGM_NOSCRATCH 0x00100000
6198 * STGM_NOSNAPSHOT 0x00200000
6200 static HRESULT validateSTGM(DWORD stgm)
6202 DWORD access = STGM_ACCESS_MODE(stgm);
6203 DWORD share = STGM_SHARE_MODE(stgm);
6204 DWORD create = STGM_CREATE_MODE(stgm);
6206 if (stgm&~STGM_KNOWN_FLAGS)
6208 ERR("unknown flags %08lx\n", stgm);
6216 case STGM_READWRITE:
6224 case STGM_SHARE_DENY_NONE:
6225 case STGM_SHARE_DENY_READ:
6226 case STGM_SHARE_DENY_WRITE:
6227 case STGM_SHARE_EXCLUSIVE:
6236 case STGM_FAILIFTHERE:
6243 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6245 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6249 * STGM_CREATE | STGM_CONVERT
6250 * if both are false, STGM_FAILIFTHERE is set to TRUE
6252 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6256 * STGM_NOSCRATCH requires STGM_TRANSACTED
6258 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6262 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6263 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6265 if ( (stgm & STGM_NOSNAPSHOT) &&
6266 (!(stgm & STGM_TRANSACTED) ||
6267 share == STGM_SHARE_EXCLUSIVE ||
6268 share == STGM_SHARE_DENY_WRITE) )
6274 /****************************************************************************
6275 * GetShareModeFromSTGM
6277 * This method will return a share mode flag from a STGM value.
6278 * The STGM value is assumed valid.
6280 static DWORD GetShareModeFromSTGM(DWORD stgm)
6282 switch (STGM_SHARE_MODE(stgm))
6284 case STGM_SHARE_DENY_NONE:
6285 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6286 case STGM_SHARE_DENY_READ:
6287 return FILE_SHARE_WRITE;
6288 case STGM_SHARE_DENY_WRITE:
6289 return FILE_SHARE_READ;
6290 case STGM_SHARE_EXCLUSIVE:
6293 ERR("Invalid share mode!\n");
6298 /****************************************************************************
6299 * GetAccessModeFromSTGM
6301 * This method will return an access mode flag from a STGM value.
6302 * The STGM value is assumed valid.
6304 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6306 switch (STGM_ACCESS_MODE(stgm))
6309 return GENERIC_READ;
6311 case STGM_READWRITE:
6312 return GENERIC_READ | GENERIC_WRITE;
6314 ERR("Invalid access mode!\n");
6319 /****************************************************************************
6320 * GetCreationModeFromSTGM
6322 * This method will return a creation mode flag from a STGM value.
6323 * The STGM value is assumed valid.
6325 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6327 switch(STGM_CREATE_MODE(stgm))
6330 return CREATE_ALWAYS;
6332 FIXME("STGM_CONVERT not implemented!\n");
6334 case STGM_FAILIFTHERE:
6337 ERR("Invalid create mode!\n");
6343 /*************************************************************************
6344 * OLECONVERT_LoadOLE10 [Internal]
6346 * Loads the OLE10 STREAM to memory
6349 * pOleStream [I] The OLESTREAM
6350 * pData [I] Data Structure for the OLESTREAM Data
6354 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6355 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6358 * This function is used by OleConvertOLESTREAMToIStorage only.
6360 * Memory allocated for pData must be freed by the caller
6362 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6365 HRESULT hRes = S_OK;
6369 pData->pData = NULL;
6370 pData->pstrOleObjFileName = (CHAR *) NULL;
6372 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6375 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6376 if(dwSize != sizeof(pData->dwOleID))
6378 hRes = CONVERT10_E_OLESTREAM_GET;
6380 else if(pData->dwOleID != OLESTREAM_ID)
6382 hRes = CONVERT10_E_OLESTREAM_FMT;
6393 /* Get the TypeID...more info needed for this field */
6394 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6395 if(dwSize != sizeof(pData->dwTypeID))
6397 hRes = CONVERT10_E_OLESTREAM_GET;
6402 if(pData->dwTypeID != 0)
6404 /* Get the length of the OleTypeName */
6405 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6406 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6408 hRes = CONVERT10_E_OLESTREAM_GET;
6413 if(pData->dwOleTypeNameLength > 0)
6415 /* Get the OleTypeName */
6416 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6417 if(dwSize != pData->dwOleTypeNameLength)
6419 hRes = CONVERT10_E_OLESTREAM_GET;
6425 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6426 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6428 hRes = CONVERT10_E_OLESTREAM_GET;
6432 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6433 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6434 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6435 if(pData->pstrOleObjFileName)
6437 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6438 if(dwSize != pData->dwOleObjFileNameLength)
6440 hRes = CONVERT10_E_OLESTREAM_GET;
6444 hRes = CONVERT10_E_OLESTREAM_GET;
6449 /* Get the Width of the Metafile */
6450 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6451 if(dwSize != sizeof(pData->dwMetaFileWidth))
6453 hRes = CONVERT10_E_OLESTREAM_GET;
6457 /* Get the Height of the Metafile */
6458 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6459 if(dwSize != sizeof(pData->dwMetaFileHeight))
6461 hRes = CONVERT10_E_OLESTREAM_GET;
6467 /* Get the Length of the Data */
6468 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6469 if(dwSize != sizeof(pData->dwDataLength))
6471 hRes = CONVERT10_E_OLESTREAM_GET;
6475 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6477 if(!bStrem1) /* if it is a second OLE stream data */
6479 pData->dwDataLength -= 8;
6480 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6481 if(dwSize != sizeof(pData->strUnknown))
6483 hRes = CONVERT10_E_OLESTREAM_GET;
6489 if(pData->dwDataLength > 0)
6491 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6493 /* Get Data (ex. IStorage, Metafile, or BMP) */
6496 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6497 if(dwSize != pData->dwDataLength)
6499 hRes = CONVERT10_E_OLESTREAM_GET;
6504 hRes = CONVERT10_E_OLESTREAM_GET;
6513 /*************************************************************************
6514 * OLECONVERT_SaveOLE10 [Internal]
6516 * Saves the OLE10 STREAM From memory
6519 * pData [I] Data Structure for the OLESTREAM Data
6520 * pOleStream [I] The OLESTREAM to save
6524 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6527 * This function is used by OleConvertIStorageToOLESTREAM only.
6530 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6533 HRESULT hRes = S_OK;
6537 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6538 if(dwSize != sizeof(pData->dwOleID))
6540 hRes = CONVERT10_E_OLESTREAM_PUT;
6545 /* Set the TypeID */
6546 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6547 if(dwSize != sizeof(pData->dwTypeID))
6549 hRes = CONVERT10_E_OLESTREAM_PUT;
6553 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6555 /* Set the Length of the OleTypeName */
6556 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6557 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6559 hRes = CONVERT10_E_OLESTREAM_PUT;
6564 if(pData->dwOleTypeNameLength > 0)
6566 /* Set the OleTypeName */
6567 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6568 if(dwSize != pData->dwOleTypeNameLength)
6570 hRes = CONVERT10_E_OLESTREAM_PUT;
6577 /* Set the width of the Metafile */
6578 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6579 if(dwSize != sizeof(pData->dwMetaFileWidth))
6581 hRes = CONVERT10_E_OLESTREAM_PUT;
6587 /* Set the height of the Metafile */
6588 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6589 if(dwSize != sizeof(pData->dwMetaFileHeight))
6591 hRes = CONVERT10_E_OLESTREAM_PUT;
6597 /* Set the length of the Data */
6598 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6599 if(dwSize != sizeof(pData->dwDataLength))
6601 hRes = CONVERT10_E_OLESTREAM_PUT;
6607 if(pData->dwDataLength > 0)
6609 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6610 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6611 if(dwSize != pData->dwDataLength)
6613 hRes = CONVERT10_E_OLESTREAM_PUT;
6621 /*************************************************************************
6622 * OLECONVERT_GetOLE20FromOLE10[Internal]
6624 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6625 * opens it, and copies the content to the dest IStorage for
6626 * OleConvertOLESTREAMToIStorage
6630 * pDestStorage [I] The IStorage to copy the data to
6631 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6632 * nBufferLength [I] The size of the buffer
6641 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6645 IStorage *pTempStorage;
6646 DWORD dwNumOfBytesWritten;
6647 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6648 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6650 /* Create a temp File */
6651 GetTempPathW(MAX_PATH, wstrTempDir);
6652 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6653 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6655 if(hFile != INVALID_HANDLE_VALUE)
6657 /* Write IStorage Data to File */
6658 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6661 /* Open and copy temp storage to the Dest Storage */
6662 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6665 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6666 StorageBaseImpl_Release(pTempStorage);
6668 DeleteFileW(wstrTempFile);
6673 /*************************************************************************
6674 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6676 * Saves the OLE10 STREAM From memory
6679 * pStorage [I] The Src IStorage to copy
6680 * pData [I] The Dest Memory to write to.
6683 * The size in bytes allocated for pData
6686 * Memory allocated for pData must be freed by the caller
6688 * Used by OleConvertIStorageToOLESTREAM only.
6691 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6695 DWORD nDataLength = 0;
6696 IStorage *pTempStorage;
6697 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6698 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6702 /* Create temp Storage */
6703 GetTempPathW(MAX_PATH, wstrTempDir);
6704 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6705 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6709 /* Copy Src Storage to the Temp Storage */
6710 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6711 StorageBaseImpl_Release(pTempStorage);
6713 /* Open Temp Storage as a file and copy to memory */
6714 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6715 if(hFile != INVALID_HANDLE_VALUE)
6717 nDataLength = GetFileSize(hFile, NULL);
6718 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6719 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6722 DeleteFileW(wstrTempFile);
6727 /*************************************************************************
6728 * OLECONVERT_CreateOleStream [Internal]
6730 * Creates the "\001OLE" stream in the IStorage if necessary.
6733 * pStorage [I] Dest storage to create the stream in
6739 * This function is used by OleConvertOLESTREAMToIStorage only.
6741 * This stream is still unknown, MS Word seems to have extra data
6742 * but since the data is stored in the OLESTREAM there should be
6743 * no need to recreate the stream. If the stream is manually
6744 * deleted it will create it with this default data.
6747 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6751 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6752 BYTE pOleStreamHeader [] =
6754 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6755 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6756 0x00, 0x00, 0x00, 0x00
6759 /* Create stream if not present */
6760 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6761 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6765 /* Write default Data */
6766 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6767 IStream_Release(pStream);
6771 /* write a string to a stream, preceded by its length */
6772 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6779 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6780 r = IStream_Write( stm, &len, sizeof(len), NULL);
6785 str = CoTaskMemAlloc( len );
6786 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6787 r = IStream_Write( stm, str, len, NULL);
6788 CoTaskMemFree( str );
6792 /* read a string preceded by its length from a stream */
6793 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6796 DWORD len, count = 0;
6800 r = IStream_Read( stm, &len, sizeof(len), &count );
6803 if( count != sizeof(len) )
6804 return E_OUTOFMEMORY;
6806 TRACE("%ld bytes\n",len);
6808 str = CoTaskMemAlloc( len );
6810 return E_OUTOFMEMORY;
6812 r = IStream_Read( stm, str, len, &count );
6817 CoTaskMemFree( str );
6818 return E_OUTOFMEMORY;
6821 TRACE("Read string %s\n",debugstr_an(str,len));
6823 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6824 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6826 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6827 CoTaskMemFree( str );
6835 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6836 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6840 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6842 static const BYTE unknown1[12] =
6843 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6844 0xFF, 0xFF, 0xFF, 0xFF};
6845 static const BYTE unknown2[16] =
6846 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6847 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6849 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6850 debugstr_w(lpszUserType), debugstr_w(szClipName),
6851 debugstr_w(szProgIDName));
6853 /* Create a CompObj stream if it doesn't exist */
6854 r = IStorage_CreateStream(pstg, szwStreamName,
6855 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6859 /* Write CompObj Structure to stream */
6860 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6862 if( SUCCEEDED( r ) )
6863 r = WriteClassStm( pstm, clsid );
6865 if( SUCCEEDED( r ) )
6866 r = STREAM_WriteString( pstm, lpszUserType );
6867 if( SUCCEEDED( r ) )
6868 r = STREAM_WriteString( pstm, szClipName );
6869 if( SUCCEEDED( r ) )
6870 r = STREAM_WriteString( pstm, szProgIDName );
6871 if( SUCCEEDED( r ) )
6872 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6874 IStream_Release( pstm );
6879 /***********************************************************************
6880 * WriteFmtUserTypeStg (OLE32.@)
6882 HRESULT WINAPI WriteFmtUserTypeStg(
6883 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6886 WCHAR szwClipName[0x40];
6887 CLSID clsid = CLSID_NULL;
6888 LPWSTR wstrProgID = NULL;
6891 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6893 /* get the clipboard format name */
6894 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6897 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6899 /* FIXME: There's room to save a CLSID and its ProgID, but
6900 the CLSID is not looked up in the registry and in all the
6901 tests I wrote it was CLSID_NULL. Where does it come from?
6904 /* get the real program ID. This may fail, but that's fine */
6905 ProgIDFromCLSID(&clsid, &wstrProgID);
6907 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6909 r = STORAGE_WriteCompObj( pstg, &clsid,
6910 lpszUserType, szwClipName, wstrProgID );
6912 CoTaskMemFree(wstrProgID);
6918 /******************************************************************************
6919 * ReadFmtUserTypeStg [OLE32.@]
6921 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6925 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6926 unsigned char unknown1[12];
6927 unsigned char unknown2[16];
6929 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6932 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6934 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6935 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6938 WARN("Failed to open stream r = %08lx\n", r);
6942 /* read the various parts of the structure */
6943 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6944 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6946 r = ReadClassStm( stm, &clsid );
6950 r = STREAM_ReadString( stm, &szCLSIDName );
6954 r = STREAM_ReadString( stm, &szOleTypeName );
6958 r = STREAM_ReadString( stm, &szProgIDName );
6962 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6963 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6966 /* ok, success... now we just need to store what we found */
6968 *pcf = RegisterClipboardFormatW( szOleTypeName );
6969 CoTaskMemFree( szOleTypeName );
6971 if( lplpszUserType )
6972 *lplpszUserType = szCLSIDName;
6973 CoTaskMemFree( szProgIDName );
6976 IStream_Release( stm );
6982 /*************************************************************************
6983 * OLECONVERT_CreateCompObjStream [Internal]
6985 * Creates a "\001CompObj" is the destination IStorage if necessary.
6988 * pStorage [I] The dest IStorage to create the CompObj Stream
6990 * strOleTypeName [I] The ProgID
6994 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6997 * This function is used by OleConvertOLESTREAMToIStorage only.
6999 * The stream data is stored in the OLESTREAM and there should be
7000 * no need to recreate the stream. If the stream is manually
7001 * deleted it will attempt to create it by querying the registry.
7005 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7008 HRESULT hStorageRes, hRes = S_OK;
7009 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7010 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7011 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7013 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7014 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7016 /* Initialize the CompObj structure */
7017 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7018 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7019 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7022 /* Create a CompObj stream if it doesn't exist */
7023 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7024 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7025 if(hStorageRes == S_OK)
7027 /* copy the OleTypeName to the compobj struct */
7028 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7029 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7031 /* copy the OleTypeName to the compobj struct */
7032 /* Note: in the test made, these were Identical */
7033 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7034 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7037 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7038 bufferW, OLESTREAM_MAX_STR_LEN );
7039 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7045 /* Get the CLSID Default Name from the Registry */
7046 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7047 if(hErr == ERROR_SUCCESS)
7049 char strTemp[OLESTREAM_MAX_STR_LEN];
7050 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7051 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
7052 if(hErr == ERROR_SUCCESS)
7054 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7060 /* Write CompObj Structure to stream */
7061 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7063 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7065 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7066 if(IStorageCompObj.dwCLSIDNameLength > 0)
7068 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7070 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7071 if(IStorageCompObj.dwOleTypeNameLength > 0)
7073 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7075 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7076 if(IStorageCompObj.dwProgIDNameLength > 0)
7078 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7080 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7081 IStream_Release(pStream);
7087 /*************************************************************************
7088 * OLECONVERT_CreateOlePresStream[Internal]
7090 * Creates the "\002OlePres000" Stream with the Metafile data
7093 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7094 * dwExtentX [I] Width of the Metafile
7095 * dwExtentY [I] Height of the Metafile
7096 * pData [I] Metafile data
7097 * dwDataLength [I] Size of the Metafile data
7101 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7104 * This function is used by OleConvertOLESTREAMToIStorage only.
7107 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7111 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7112 BYTE pOlePresStreamHeader [] =
7114 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7115 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7116 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7117 0x00, 0x00, 0x00, 0x00
7120 BYTE pOlePresStreamHeaderEmpty [] =
7122 0x00, 0x00, 0x00, 0x00,
7123 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7124 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7125 0x00, 0x00, 0x00, 0x00
7128 /* Create the OlePres000 Stream */
7129 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7130 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7135 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7137 memset(&OlePres, 0, sizeof(OlePres));
7138 /* Do we have any metafile data to save */
7139 if(dwDataLength > 0)
7141 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7142 nHeaderSize = sizeof(pOlePresStreamHeader);
7146 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7147 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7149 /* Set width and height of the metafile */
7150 OlePres.dwExtentX = dwExtentX;
7151 OlePres.dwExtentY = -dwExtentY;
7153 /* Set Data and Length */
7154 if(dwDataLength > sizeof(METAFILEPICT16))
7156 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7157 OlePres.pData = &(pData[8]);
7159 /* Save OlePres000 Data to Stream */
7160 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7161 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7162 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7163 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7164 if(OlePres.dwSize > 0)
7166 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7168 IStream_Release(pStream);
7172 /*************************************************************************
7173 * OLECONVERT_CreateOle10NativeStream [Internal]
7175 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7178 * pStorage [I] Dest storage to create the stream in
7179 * pData [I] Ole10 Native Data (ex. bmp)
7180 * dwDataLength [I] Size of the Ole10 Native Data
7186 * This function is used by OleConvertOLESTREAMToIStorage only.
7188 * Might need to verify the data and return appropriate error message
7191 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7195 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7197 /* Create the Ole10Native Stream */
7198 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7199 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7203 /* Write info to stream */
7204 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7205 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7206 IStream_Release(pStream);
7211 /*************************************************************************
7212 * OLECONVERT_GetOLE10ProgID [Internal]
7214 * Finds the ProgID (or OleTypeID) from the IStorage
7217 * pStorage [I] The Src IStorage to get the ProgID
7218 * strProgID [I] the ProgID string to get
7219 * dwSize [I] the size of the string
7223 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7226 * This function is used by OleConvertIStorageToOLESTREAM only.
7230 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7234 LARGE_INTEGER iSeekPos;
7235 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7236 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7238 /* Open the CompObj Stream */
7239 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7240 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7244 /*Get the OleType from the CompObj Stream */
7245 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7246 iSeekPos.u.HighPart = 0;
7248 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7249 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7250 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7251 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7252 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7253 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7254 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7256 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7259 IStream_Read(pStream, strProgID, *dwSize, NULL);
7261 IStream_Release(pStream);
7266 LPOLESTR wstrProgID;
7268 /* Get the OleType from the registry */
7269 REFCLSID clsid = &(stat.clsid);
7270 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7271 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7274 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7281 /*************************************************************************
7282 * OLECONVERT_GetOle10PresData [Internal]
7284 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7287 * pStorage [I] Src IStroage
7288 * pOleStream [I] Dest OleStream Mem Struct
7294 * This function is used by OleConvertIStorageToOLESTREAM only.
7296 * Memory allocated for pData must be freed by the caller
7300 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7305 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7307 /* Initialize Default data for OLESTREAM */
7308 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7309 pOleStreamData[0].dwTypeID = 2;
7310 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7311 pOleStreamData[1].dwTypeID = 0;
7312 pOleStreamData[0].dwMetaFileWidth = 0;
7313 pOleStreamData[0].dwMetaFileHeight = 0;
7314 pOleStreamData[0].pData = NULL;
7315 pOleStreamData[1].pData = NULL;
7317 /* Open Ole10Native Stream */
7318 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7319 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7323 /* Read Size and Data */
7324 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7325 if(pOleStreamData->dwDataLength > 0)
7327 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7328 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7330 IStream_Release(pStream);
7336 /*************************************************************************
7337 * OLECONVERT_GetOle20PresData[Internal]
7339 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7342 * pStorage [I] Src IStroage
7343 * pOleStreamData [I] Dest OleStream Mem Struct
7349 * This function is used by OleConvertIStorageToOLESTREAM only.
7351 * Memory allocated for pData must be freed by the caller
7353 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7357 OLECONVERT_ISTORAGE_OLEPRES olePress;
7358 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7360 /* Initialize Default data for OLESTREAM */
7361 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7362 pOleStreamData[0].dwTypeID = 2;
7363 pOleStreamData[0].dwMetaFileWidth = 0;
7364 pOleStreamData[0].dwMetaFileHeight = 0;
7365 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7366 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7367 pOleStreamData[1].dwTypeID = 0;
7368 pOleStreamData[1].dwOleTypeNameLength = 0;
7369 pOleStreamData[1].strOleTypeName[0] = 0;
7370 pOleStreamData[1].dwMetaFileWidth = 0;
7371 pOleStreamData[1].dwMetaFileHeight = 0;
7372 pOleStreamData[1].pData = NULL;
7373 pOleStreamData[1].dwDataLength = 0;
7376 /* Open OlePress000 stream */
7377 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7378 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7381 LARGE_INTEGER iSeekPos;
7382 METAFILEPICT16 MetaFilePict;
7383 static const char strMetafilePictName[] = "METAFILEPICT";
7385 /* Set the TypeID for a Metafile */
7386 pOleStreamData[1].dwTypeID = 5;
7388 /* Set the OleTypeName to Metafile */
7389 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7390 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7392 iSeekPos.u.HighPart = 0;
7393 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7395 /* Get Presentation Data */
7396 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7397 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7398 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7399 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7401 /*Set width and Height */
7402 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7403 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7404 if(olePress.dwSize > 0)
7407 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7409 /* Set MetaFilePict struct */
7410 MetaFilePict.mm = 8;
7411 MetaFilePict.xExt = olePress.dwExtentX;
7412 MetaFilePict.yExt = olePress.dwExtentY;
7413 MetaFilePict.hMF = 0;
7415 /* Get Metafile Data */
7416 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7417 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7418 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7420 IStream_Release(pStream);
7424 /*************************************************************************
7425 * OleConvertOLESTREAMToIStorage [OLE32.@]
7430 * DVTARGETDEVICE paramenter is not handled
7431 * Still unsure of some mem fields for OLE 10 Stream
7432 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7433 * and "\001OLE" streams
7436 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7437 LPOLESTREAM pOleStream,
7439 const DVTARGETDEVICE* ptd)
7443 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7445 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7449 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7452 if(pstg == NULL || pOleStream == NULL)
7454 hRes = E_INVALIDARG;
7459 /* Load the OLESTREAM to Memory */
7460 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7465 /* Load the OLESTREAM to Memory (part 2)*/
7466 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7472 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7474 /* Do we have the IStorage Data in the OLESTREAM */
7475 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7477 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7478 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7482 /* It must be an original OLE 1.0 source */
7483 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7488 /* It must be an original OLE 1.0 source */
7489 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7492 /* Create CompObj Stream if necessary */
7493 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7496 /*Create the Ole Stream if necessary */
7497 OLECONVERT_CreateOleStream(pstg);
7502 /* Free allocated memory */
7503 for(i=0; i < 2; i++)
7505 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7506 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7507 pOleStreamData[i].pstrOleObjFileName = NULL;
7512 /*************************************************************************
7513 * OleConvertIStorageToOLESTREAM [OLE32.@]
7520 * Still unsure of some mem fields for OLE 10 Stream
7521 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7522 * and "\001OLE" streams.
7525 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7527 LPOLESTREAM pOleStream)
7530 HRESULT hRes = S_OK;
7532 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7533 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7536 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7538 if(pstg == NULL || pOleStream == NULL)
7540 hRes = E_INVALIDARG;
7544 /* Get the ProgID */
7545 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7546 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7550 /* Was it originally Ole10 */
7551 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7554 IStream_Release(pStream);
7555 /* Get Presentation Data for Ole10Native */
7556 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7560 /* Get Presentation Data (OLE20) */
7561 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7564 /* Save OLESTREAM */
7565 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7568 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7573 /* Free allocated memory */
7574 for(i=0; i < 2; i++)
7576 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7582 /***********************************************************************
7583 * GetConvertStg (OLE32.@)
7585 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7586 FIXME("unimplemented stub!\n");
7590 /******************************************************************************
7591 * StgIsStorageFile [OLE32.@]
7594 StgIsStorageFile(LPCOLESTR fn)
7600 TRACE("(\'%s\')\n", debugstr_w(fn));
7601 hf = CreateFileW(fn, GENERIC_READ,
7602 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7603 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7605 if (hf == INVALID_HANDLE_VALUE)
7606 return STG_E_FILENOTFOUND;
7608 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7610 WARN(" unable to read file\n");
7617 if (bytes_read != 8) {
7618 WARN(" too short\n");
7622 if (!memcmp(magic,STORAGE_magic,8)) {
7627 WARN(" -> Invalid header.\n");