Release 20030408.
[wine/gsoc-2012-control.git] / dlls / ole32 / storage32.c
blob72221780de640e2ff6c267f21328f07108d2b1f5
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <assert.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
32 #define NONAMELESSUNION
33 #define NONAMELESSSTRUCT
34 #include "winbase.h" /* for lstrlenW() and the likes */
35 #include "winnls.h"
36 #include "wine/unicode.h"
37 #include "wine/debug.h"
39 #include "storage32.h"
40 #include "ole2.h" /* For Write/ReadClassStm */
42 #include "winreg.h"
43 #include "wine/wingdi16.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(storage);
47 #define FILE_BEGIN 0
50 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
51 #define OLESTREAM_ID 0x501
52 #define OLESTREAM_MAX_STR_LEN 255
54 static const char rootPropertyName[] = "Root Entry";
57 /* OLESTREAM memory structure to use for Get and Put Routines */
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 typedef struct
61 DWORD dwOleID;
62 DWORD dwTypeID;
63 DWORD dwOleTypeNameLength;
64 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
65 CHAR *pstrOleObjFileName;
66 DWORD dwOleObjFileNameLength;
67 DWORD dwMetaFileWidth;
68 DWORD dwMetaFileHeight;
69 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
70 DWORD dwDataLength;
71 BYTE *pData;
72 }OLECONVERT_OLESTREAM_DATA;
74 /* CompObj Stream structure */
75 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
76 typedef struct
78 BYTE byUnknown1[12];
79 CLSID clsid;
80 DWORD dwCLSIDNameLength;
81 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
82 DWORD dwOleTypeNameLength;
83 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
84 DWORD dwProgIDNameLength;
85 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
86 BYTE byUnknown2[16];
87 }OLECONVERT_ISTORAGE_COMPOBJ;
90 /* Ole Presention Stream structure */
91 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
92 typedef struct
94 BYTE byUnknown1[28];
95 DWORD dwExtentX;
96 DWORD dwExtentY;
97 DWORD dwSize;
98 BYTE *pData;
99 }OLECONVERT_ISTORAGE_OLEPRES;
103 /***********************************************************************
104 * Forward declaration of internal functions used by the method DestroyElement
106 static HRESULT deleteStorageProperty(
107 StorageImpl *parentStorage,
108 ULONG foundPropertyIndexToDelete,
109 StgProperty propertyToDelete);
111 static HRESULT deleteStreamProperty(
112 StorageImpl *parentStorage,
113 ULONG foundPropertyIndexToDelete,
114 StgProperty propertyToDelete);
116 static HRESULT findPlaceholder(
117 StorageImpl *storage,
118 ULONG propertyIndexToStore,
119 ULONG storagePropertyIndex,
120 INT typeOfRelation);
122 static HRESULT adjustPropertyChain(
123 StorageImpl *This,
124 StgProperty propertyToDelete,
125 StgProperty parentProperty,
126 ULONG parentPropertyId,
127 INT typeOfRelation);
129 /***********************************************************************
130 * Declaration of the functions used to manipulate StgProperty
133 static ULONG getFreeProperty(
134 StorageImpl *storage);
136 static void updatePropertyChain(
137 StorageImpl *storage,
138 ULONG newPropertyIndex,
139 StgProperty newProperty);
141 static LONG propertyNameCmp(
142 OLECHAR *newProperty,
143 OLECHAR *currentProperty);
146 /***********************************************************************
147 * Declaration of miscellaneous functions...
149 static HRESULT validateSTGM(DWORD stgmValue);
151 static DWORD GetShareModeFromSTGM(DWORD stgm);
152 static DWORD GetAccessModeFromSTGM(DWORD stgm);
153 static DWORD GetCreationModeFromSTGM(DWORD stgm);
156 * Virtual function table for the IStorage32Impl class.
158 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
160 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
161 StorageBaseImpl_QueryInterface,
162 StorageBaseImpl_AddRef,
163 StorageBaseImpl_Release,
164 StorageBaseImpl_CreateStream,
165 StorageBaseImpl_OpenStream,
166 StorageImpl_CreateStorage,
167 StorageBaseImpl_OpenStorage,
168 StorageImpl_CopyTo,
169 StorageImpl_MoveElementTo,
170 StorageImpl_Commit,
171 StorageImpl_Revert,
172 StorageBaseImpl_EnumElements,
173 StorageImpl_DestroyElement,
174 StorageBaseImpl_RenameElement,
175 StorageImpl_SetElementTimes,
176 StorageBaseImpl_SetClass,
177 StorageImpl_SetStateBits,
178 StorageImpl_Stat
182 * Virtual function table for the Storage32InternalImpl class.
184 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
186 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
187 StorageBaseImpl_QueryInterface,
188 StorageBaseImpl_AddRef,
189 StorageBaseImpl_Release,
190 StorageBaseImpl_CreateStream,
191 StorageBaseImpl_OpenStream,
192 StorageImpl_CreateStorage,
193 StorageBaseImpl_OpenStorage,
194 StorageImpl_CopyTo,
195 StorageImpl_MoveElementTo,
196 StorageInternalImpl_Commit,
197 StorageInternalImpl_Revert,
198 StorageBaseImpl_EnumElements,
199 StorageImpl_DestroyElement,
200 StorageBaseImpl_RenameElement,
201 StorageImpl_SetElementTimes,
202 StorageBaseImpl_SetClass,
203 StorageImpl_SetStateBits,
204 StorageBaseImpl_Stat
208 * Virtual function table for the IEnumSTATSTGImpl class.
210 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
212 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
213 IEnumSTATSTGImpl_QueryInterface,
214 IEnumSTATSTGImpl_AddRef,
215 IEnumSTATSTGImpl_Release,
216 IEnumSTATSTGImpl_Next,
217 IEnumSTATSTGImpl_Skip,
218 IEnumSTATSTGImpl_Reset,
219 IEnumSTATSTGImpl_Clone
226 /************************************************************************
227 ** Storage32BaseImpl implementatiion
230 /************************************************************************
231 * Storage32BaseImpl_QueryInterface (IUnknown)
233 * This method implements the common QueryInterface for all IStorage32
234 * implementations contained in this file.
236 * See Windows documentation for more details on IUnknown methods.
238 HRESULT WINAPI StorageBaseImpl_QueryInterface(
239 IStorage* iface,
240 REFIID riid,
241 void** ppvObject)
243 ICOM_THIS(StorageBaseImpl,iface);
245 * Perform a sanity check on the parameters.
247 if ( (This==0) || (ppvObject==0) )
248 return E_INVALIDARG;
251 * Initialize the return parameter.
253 *ppvObject = 0;
256 * Compare the riid with the interface IDs implemented by this object.
258 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
260 *ppvObject = (IStorage*)This;
262 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
264 *ppvObject = (IStorage*)This;
268 * Check that we obtained an interface.
270 if ((*ppvObject)==0)
271 return E_NOINTERFACE;
274 * Query Interface always increases the reference count by one when it is
275 * successful
277 StorageBaseImpl_AddRef(iface);
279 return S_OK;
282 /************************************************************************
283 * Storage32BaseImpl_AddRef (IUnknown)
285 * This method implements the common AddRef for all IStorage32
286 * implementations contained in this file.
288 * See Windows documentation for more details on IUnknown methods.
290 ULONG WINAPI StorageBaseImpl_AddRef(
291 IStorage* iface)
293 ICOM_THIS(StorageBaseImpl,iface);
294 This->ref++;
296 return This->ref;
299 /************************************************************************
300 * Storage32BaseImpl_Release (IUnknown)
302 * This method implements the common Release for all IStorage32
303 * implementations contained in this file.
305 * See Windows documentation for more details on IUnknown methods.
307 ULONG WINAPI StorageBaseImpl_Release(
308 IStorage* iface)
310 ICOM_THIS(StorageBaseImpl,iface);
312 * Decrease the reference count on this object.
314 This->ref--;
317 * If the reference count goes down to 0, perform suicide.
319 if (This->ref==0)
322 * Since we are using a system of base-classes, we want to call the
323 * destructor of the appropriate derived class. To do this, we are
324 * using virtual functions to implement the destructor.
326 This->v_destructor(This);
328 return 0;
331 return This->ref;
334 /************************************************************************
335 * Storage32BaseImpl_OpenStream (IStorage)
337 * This method will open the specified stream object from the current storage.
339 * See Windows documentation for more details on IStorage methods.
341 HRESULT WINAPI StorageBaseImpl_OpenStream(
342 IStorage* iface,
343 const OLECHAR* pwcsName, /* [string][in] */
344 void* reserved1, /* [unique][in] */
345 DWORD grfMode, /* [in] */
346 DWORD reserved2, /* [in] */
347 IStream** ppstm) /* [out] */
349 ICOM_THIS(StorageBaseImpl,iface);
350 IEnumSTATSTGImpl* propertyEnumeration;
351 StgStreamImpl* newStream;
352 StgProperty currentProperty;
353 ULONG foundPropertyIndex;
354 HRESULT res = STG_E_UNKNOWN;
356 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
357 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
360 * Perform a sanity check on the parameters.
362 if ( (pwcsName==NULL) || (ppstm==0) )
364 res = E_INVALIDARG;
365 goto end;
369 * Initialize the out parameter
371 *ppstm = NULL;
374 * Validate the STGM flags
376 if ( FAILED( validateSTGM(grfMode) ))
378 res = STG_E_INVALIDFLAG;
379 goto end;
383 * As documented.
385 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
386 (grfMode & STGM_DELETEONRELEASE) ||
387 (grfMode & STGM_TRANSACTED) )
389 res = STG_E_INVALIDFUNCTION;
390 goto end;
394 * Create a property enumeration to search the properties
396 propertyEnumeration = IEnumSTATSTGImpl_Construct(
397 This->ancestorStorage,
398 This->rootPropertySetIndex);
401 * Search the enumeration for the property with the given name
403 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
404 propertyEnumeration,
405 pwcsName,
406 &currentProperty);
409 * Delete the property enumeration since we don't need it anymore
411 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
414 * If it was found, construct the stream object and return a pointer to it.
416 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
417 (currentProperty.propertyType==PROPTYPE_STREAM) )
419 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
421 if (newStream!=0)
423 newStream->grfMode = grfMode;
424 *ppstm = (IStream*)newStream;
427 * Since we are returning a pointer to the interface, we have to
428 * nail down the reference.
430 StgStreamImpl_AddRef(*ppstm);
432 res = S_OK;
433 goto end;
436 res = E_OUTOFMEMORY;
437 goto end;
440 res = STG_E_FILENOTFOUND;
442 end:
443 if (res == S_OK)
444 TRACE("<-- IStream %p\n", *ppstm);
445 TRACE("<-- %08lx\n", res);
446 return res;
449 /************************************************************************
450 * Storage32BaseImpl_OpenStorage (IStorage)
452 * This method will open a new storage object from the current storage.
454 * See Windows documentation for more details on IStorage methods.
456 HRESULT WINAPI StorageBaseImpl_OpenStorage(
457 IStorage* iface,
458 const OLECHAR* pwcsName, /* [string][unique][in] */
459 IStorage* pstgPriority, /* [unique][in] */
460 DWORD grfMode, /* [in] */
461 SNB snbExclude, /* [unique][in] */
462 DWORD reserved, /* [in] */
463 IStorage** ppstg) /* [out] */
465 ICOM_THIS(StorageBaseImpl,iface);
466 StorageInternalImpl* newStorage;
467 IEnumSTATSTGImpl* propertyEnumeration;
468 StgProperty currentProperty;
469 ULONG foundPropertyIndex;
470 HRESULT res = STG_E_UNKNOWN;
472 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
473 iface, debugstr_w(pwcsName), pstgPriority,
474 grfMode, snbExclude, reserved, ppstg);
477 * Perform a sanity check on the parameters.
479 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
481 res = E_INVALIDARG;
482 goto end;
485 /* as documented */
486 if (snbExclude != NULL)
488 res = STG_E_INVALIDPARAMETER;
489 goto end;
493 * Validate the STGM flags
495 if ( FAILED( validateSTGM(grfMode) ))
497 res = STG_E_INVALIDFLAG;
498 goto end;
502 * As documented.
504 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
505 (grfMode & STGM_DELETEONRELEASE) ||
506 (grfMode & STGM_PRIORITY) )
508 res = STG_E_INVALIDFUNCTION;
509 goto end;
513 * Initialize the out parameter
515 *ppstg = NULL;
518 * Create a property enumeration to search the properties
520 propertyEnumeration = IEnumSTATSTGImpl_Construct(
521 This->ancestorStorage,
522 This->rootPropertySetIndex);
525 * Search the enumeration for the property with the given name
527 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
528 propertyEnumeration,
529 pwcsName,
530 &currentProperty);
533 * Delete the property enumeration since we don't need it anymore
535 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
538 * If it was found, construct the stream object and return a pointer to it.
540 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
541 (currentProperty.propertyType==PROPTYPE_STORAGE) )
544 * Construct a new Storage object
546 newStorage = StorageInternalImpl_Construct(
547 This->ancestorStorage,
548 foundPropertyIndex);
550 if (newStorage != 0)
552 *ppstg = (IStorage*)newStorage;
555 * Since we are returning a pointer to the interface,
556 * we have to nail down the reference.
558 StorageBaseImpl_AddRef(*ppstg);
560 res = S_OK;
561 goto end;
564 res = STG_E_INSUFFICIENTMEMORY;
565 goto end;
568 res = STG_E_FILENOTFOUND;
570 end:
571 TRACE("<-- %08lx\n", res);
572 return res;
575 /************************************************************************
576 * Storage32BaseImpl_EnumElements (IStorage)
578 * This method will create an enumerator object that can be used to
579 * retrieve informatino about all the properties in the storage object.
581 * See Windows documentation for more details on IStorage methods.
583 HRESULT WINAPI StorageBaseImpl_EnumElements(
584 IStorage* iface,
585 DWORD reserved1, /* [in] */
586 void* reserved2, /* [size_is][unique][in] */
587 DWORD reserved3, /* [in] */
588 IEnumSTATSTG** ppenum) /* [out] */
590 ICOM_THIS(StorageBaseImpl,iface);
591 IEnumSTATSTGImpl* newEnum;
593 TRACE("(%p, %ld, %p, %ld, %p)\n",
594 iface, reserved1, reserved2, reserved3, ppenum);
597 * Perform a sanity check on the parameters.
599 if ( (This==0) || (ppenum==0))
600 return E_INVALIDARG;
603 * Construct the enumerator.
605 newEnum = IEnumSTATSTGImpl_Construct(
606 This->ancestorStorage,
607 This->rootPropertySetIndex);
609 if (newEnum!=0)
611 *ppenum = (IEnumSTATSTG*)newEnum;
614 * Don't forget to nail down a reference to the new object before
615 * returning it.
617 IEnumSTATSTGImpl_AddRef(*ppenum);
619 return S_OK;
622 return E_OUTOFMEMORY;
625 /************************************************************************
626 * Storage32BaseImpl_Stat (IStorage)
628 * This method will retrieve information about this storage object.
630 * See Windows documentation for more details on IStorage methods.
632 HRESULT WINAPI StorageBaseImpl_Stat(
633 IStorage* iface,
634 STATSTG* pstatstg, /* [out] */
635 DWORD grfStatFlag) /* [in] */
637 ICOM_THIS(StorageBaseImpl,iface);
638 StgProperty curProperty;
639 BOOL readSuccessful;
640 HRESULT res = STG_E_UNKNOWN;
642 TRACE("(%p, %p, %lx)\n",
643 iface, pstatstg, grfStatFlag);
646 * Perform a sanity check on the parameters.
648 if ( (This==0) || (pstatstg==0))
650 res = E_INVALIDARG;
651 goto end;
655 * Read the information from the property.
657 readSuccessful = StorageImpl_ReadProperty(
658 This->ancestorStorage,
659 This->rootPropertySetIndex,
660 &curProperty);
662 if (readSuccessful)
664 StorageUtl_CopyPropertyToSTATSTG(
665 pstatstg,
666 &curProperty,
667 grfStatFlag);
669 res = S_OK;
670 goto end;
673 res = E_FAIL;
675 end:
676 if (res == S_OK)
678 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.s.LowPart, pstatstg->cbSize.s.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
680 TRACE("<-- %08lx\n", res);
681 return res;
684 /************************************************************************
685 * Storage32BaseImpl_RenameElement (IStorage)
687 * This method will rename the specified element.
689 * See Windows documentation for more details on IStorage methods.
691 * Implementation notes: The method used to rename consists of creating a clone
692 * of the deleted StgProperty object setting it with the new name and to
693 * perform a DestroyElement of the old StgProperty.
695 HRESULT WINAPI StorageBaseImpl_RenameElement(
696 IStorage* iface,
697 const OLECHAR* pwcsOldName, /* [in] */
698 const OLECHAR* pwcsNewName) /* [in] */
700 ICOM_THIS(StorageBaseImpl,iface);
701 IEnumSTATSTGImpl* propertyEnumeration;
702 StgProperty currentProperty;
703 ULONG foundPropertyIndex;
705 TRACE("(%p, %s, %s)\n",
706 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
709 * Create a property enumeration to search the properties
711 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
712 This->rootPropertySetIndex);
715 * Search the enumeration for the new property name
717 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
718 pwcsNewName,
719 &currentProperty);
721 if (foundPropertyIndex != PROPERTY_NULL)
724 * There is already a property with the new name
726 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
727 return STG_E_FILEALREADYEXISTS;
730 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
733 * Search the enumeration for the old property name
735 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
736 pwcsOldName,
737 &currentProperty);
740 * Delete the property enumeration since we don't need it anymore
742 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
744 if (foundPropertyIndex != PROPERTY_NULL)
746 StgProperty renamedProperty;
747 ULONG renamedPropertyIndex;
750 * Setup a new property for the renamed property
752 renamedProperty.sizeOfNameString =
753 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
755 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
756 return STG_E_INVALIDNAME;
758 strcpyW(renamedProperty.name, pwcsNewName);
760 renamedProperty.propertyType = currentProperty.propertyType;
761 renamedProperty.startingBlock = currentProperty.startingBlock;
762 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
763 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
765 renamedProperty.previousProperty = PROPERTY_NULL;
766 renamedProperty.nextProperty = PROPERTY_NULL;
769 * Bring the dirProperty link in case it is a storage and in which
770 * case the renamed storage elements don't require to be reorganized.
772 renamedProperty.dirProperty = currentProperty.dirProperty;
774 /* call CoFileTime to get the current time
775 renamedProperty.timeStampS1
776 renamedProperty.timeStampD1
777 renamedProperty.timeStampS2
778 renamedProperty.timeStampD2
779 renamedProperty.propertyUniqueID
783 * Obtain a free property in the property chain
785 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
788 * Save the new property into the new property spot
790 StorageImpl_WriteProperty(
791 This->ancestorStorage,
792 renamedPropertyIndex,
793 &renamedProperty);
796 * Find a spot in the property chain for our newly created property.
798 updatePropertyChain(
799 (StorageImpl*)This,
800 renamedPropertyIndex,
801 renamedProperty);
804 * At this point the renamed property has been inserted in the tree,
805 * now, before to Destroy the old property we must zeroed it's dirProperty
806 * otherwise the DestroyProperty below will zap it all and we do not want
807 * this to happen.
808 * Also, we fake that the old property is a storage so the DestroyProperty
809 * will not do a SetSize(0) on the stream data.
811 * This means that we need to tweek the StgProperty if it is a stream or a
812 * non empty storage.
814 StorageImpl_ReadProperty(This->ancestorStorage,
815 foundPropertyIndex,
816 &currentProperty);
818 currentProperty.dirProperty = PROPERTY_NULL;
819 currentProperty.propertyType = PROPTYPE_STORAGE;
820 StorageImpl_WriteProperty(
821 This->ancestorStorage,
822 foundPropertyIndex,
823 &currentProperty);
826 * Invoke Destroy to get rid of the ole property and automatically redo
827 * the linking of it's previous and next members...
829 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
832 else
835 * There is no property with the old name
837 return STG_E_FILENOTFOUND;
840 return S_OK;
843 /************************************************************************
844 * Storage32BaseImpl_CreateStream (IStorage)
846 * This method will create a stream object within this storage
848 * See Windows documentation for more details on IStorage methods.
850 HRESULT WINAPI StorageBaseImpl_CreateStream(
851 IStorage* iface,
852 const OLECHAR* pwcsName, /* [string][in] */
853 DWORD grfMode, /* [in] */
854 DWORD reserved1, /* [in] */
855 DWORD reserved2, /* [in] */
856 IStream** ppstm) /* [out] */
858 ICOM_THIS(StorageBaseImpl,iface);
859 IEnumSTATSTGImpl* propertyEnumeration;
860 StgStreamImpl* newStream;
861 StgProperty currentProperty, newStreamProperty;
862 ULONG foundPropertyIndex, newPropertyIndex;
864 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
865 iface, debugstr_w(pwcsName), grfMode,
866 reserved1, reserved2, ppstm);
869 * Validate parameters
871 if (ppstm == 0)
872 return STG_E_INVALIDPOINTER;
874 if (pwcsName == 0)
875 return STG_E_INVALIDNAME;
878 * Validate the STGM flags
880 if ( FAILED( validateSTGM(grfMode) ))
881 return STG_E_INVALIDFLAG;
884 * As documented.
886 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
887 (grfMode & STGM_DELETEONRELEASE) ||
888 (grfMode & STGM_TRANSACTED) )
889 return STG_E_INVALIDFUNCTION;
892 * Initialize the out parameter
894 *ppstm = 0;
897 * Create a property enumeration to search the properties
899 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
900 This->rootPropertySetIndex);
902 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
903 pwcsName,
904 &currentProperty);
906 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
908 if (foundPropertyIndex != PROPERTY_NULL)
911 * An element with this name already exists
913 if (grfMode & STGM_CREATE)
915 IStorage_DestroyElement(iface, pwcsName);
917 else
918 return STG_E_FILEALREADYEXISTS;
922 * memset the empty property
924 memset(&newStreamProperty, 0, sizeof(StgProperty));
926 newStreamProperty.sizeOfNameString =
927 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
929 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
930 return STG_E_INVALIDNAME;
932 strcpyW(newStreamProperty.name, pwcsName);
934 newStreamProperty.propertyType = PROPTYPE_STREAM;
935 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
936 newStreamProperty.size.s.LowPart = 0;
937 newStreamProperty.size.s.HighPart = 0;
939 newStreamProperty.previousProperty = PROPERTY_NULL;
940 newStreamProperty.nextProperty = PROPERTY_NULL;
941 newStreamProperty.dirProperty = PROPERTY_NULL;
943 /* call CoFileTime to get the current time
944 newStreamProperty.timeStampS1
945 newStreamProperty.timeStampD1
946 newStreamProperty.timeStampS2
947 newStreamProperty.timeStampD2
950 /* newStreamProperty.propertyUniqueID */
953 * Get a free property or create a new one
955 newPropertyIndex = getFreeProperty(This->ancestorStorage);
958 * Save the new property into the new property spot
960 StorageImpl_WriteProperty(
961 This->ancestorStorage,
962 newPropertyIndex,
963 &newStreamProperty);
966 * Find a spot in the property chain for our newly created property.
968 updatePropertyChain(
969 (StorageImpl*)This,
970 newPropertyIndex,
971 newStreamProperty);
974 * Open the stream to return it.
976 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
978 if (newStream != 0)
980 *ppstm = (IStream*)newStream;
983 * Since we are returning a pointer to the interface, we have to nail down
984 * the reference.
986 StgStreamImpl_AddRef(*ppstm);
988 else
990 return STG_E_INSUFFICIENTMEMORY;
993 return S_OK;
996 /************************************************************************
997 * Storage32BaseImpl_SetClass (IStorage)
999 * This method will write the specified CLSID in the property of this
1000 * storage.
1002 * See Windows documentation for more details on IStorage methods.
1004 HRESULT WINAPI StorageBaseImpl_SetClass(
1005 IStorage* iface,
1006 REFCLSID clsid) /* [in] */
1008 ICOM_THIS(StorageBaseImpl,iface);
1009 HRESULT hRes = E_FAIL;
1010 StgProperty curProperty;
1011 BOOL success;
1013 TRACE("(%p, %p)\n", iface, clsid);
1015 success = StorageImpl_ReadProperty(This->ancestorStorage,
1016 This->rootPropertySetIndex,
1017 &curProperty);
1018 if (success)
1020 curProperty.propertyUniqueID = *clsid;
1022 success = StorageImpl_WriteProperty(This->ancestorStorage,
1023 This->rootPropertySetIndex,
1024 &curProperty);
1025 if (success)
1026 hRes = S_OK;
1029 return hRes;
1032 /************************************************************************
1033 ** Storage32Impl implementation
1036 /************************************************************************
1037 * Storage32Impl_CreateStorage (IStorage)
1039 * This method will create the storage object within the provided storage.
1041 * See Windows documentation for more details on IStorage methods.
1043 HRESULT WINAPI StorageImpl_CreateStorage(
1044 IStorage* iface,
1045 const OLECHAR *pwcsName, /* [string][in] */
1046 DWORD grfMode, /* [in] */
1047 DWORD reserved1, /* [in] */
1048 DWORD reserved2, /* [in] */
1049 IStorage **ppstg) /* [out] */
1051 StorageImpl* const This=(StorageImpl*)iface;
1053 IEnumSTATSTGImpl *propertyEnumeration;
1054 StgProperty currentProperty;
1055 StgProperty newProperty;
1056 ULONG foundPropertyIndex;
1057 ULONG newPropertyIndex;
1058 HRESULT hr;
1060 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1061 iface, debugstr_w(pwcsName), grfMode,
1062 reserved1, reserved2, ppstg);
1065 * Validate parameters
1067 if (ppstg == 0)
1068 return STG_E_INVALIDPOINTER;
1070 if (pwcsName == 0)
1071 return STG_E_INVALIDNAME;
1074 * Validate the STGM flags
1076 if ( FAILED( validateSTGM(grfMode) ) ||
1077 (grfMode & STGM_DELETEONRELEASE) )
1078 return STG_E_INVALIDFLAG;
1081 * Initialize the out parameter
1083 *ppstg = 0;
1086 * Create a property enumeration and search the properties
1088 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1089 This->rootPropertySetIndex);
1091 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1092 pwcsName,
1093 &currentProperty);
1094 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1096 if (foundPropertyIndex != PROPERTY_NULL)
1099 * An element with this name already exists
1101 if (grfMode & STGM_CREATE)
1102 IStorage_DestroyElement(iface, pwcsName);
1103 else
1104 return STG_E_FILEALREADYEXISTS;
1108 * memset the empty property
1110 memset(&newProperty, 0, sizeof(StgProperty));
1112 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1114 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1115 return STG_E_INVALIDNAME;
1117 strcpyW(newProperty.name, pwcsName);
1119 newProperty.propertyType = PROPTYPE_STORAGE;
1120 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1121 newProperty.size.s.LowPart = 0;
1122 newProperty.size.s.HighPart = 0;
1124 newProperty.previousProperty = PROPERTY_NULL;
1125 newProperty.nextProperty = PROPERTY_NULL;
1126 newProperty.dirProperty = PROPERTY_NULL;
1128 /* call CoFileTime to get the current time
1129 newProperty.timeStampS1
1130 newProperty.timeStampD1
1131 newProperty.timeStampS2
1132 newProperty.timeStampD2
1135 /* newStorageProperty.propertyUniqueID */
1138 * Obtain a free property in the property chain
1140 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1143 * Save the new property into the new property spot
1145 StorageImpl_WriteProperty(
1146 This->ancestorStorage,
1147 newPropertyIndex,
1148 &newProperty);
1151 * Find a spot in the property chain for our newly created property.
1153 updatePropertyChain(
1154 This,
1155 newPropertyIndex,
1156 newProperty);
1159 * Open it to get a pointer to return.
1161 hr = IStorage_OpenStorage(
1162 iface,
1163 (OLECHAR*)pwcsName,
1165 grfMode,
1168 ppstg);
1170 if( (hr != S_OK) || (*ppstg == NULL))
1172 return hr;
1176 return S_OK;
1180 /***************************************************************************
1182 * Internal Method
1184 * Get a free property or create a new one.
1186 static ULONG getFreeProperty(
1187 StorageImpl *storage)
1189 ULONG currentPropertyIndex = 0;
1190 ULONG newPropertyIndex = PROPERTY_NULL;
1191 BOOL readSuccessful = TRUE;
1192 StgProperty currentProperty;
1197 * Start by reading the root property
1199 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1200 currentPropertyIndex,
1201 &currentProperty);
1202 if (readSuccessful)
1204 if (currentProperty.sizeOfNameString == 0)
1207 * The property existis and is available, we found it.
1209 newPropertyIndex = currentPropertyIndex;
1212 else
1215 * We exhausted the property list, we will create more space below
1217 newPropertyIndex = currentPropertyIndex;
1219 currentPropertyIndex++;
1221 } while (newPropertyIndex == PROPERTY_NULL);
1224 * grow the property chain
1226 if (! readSuccessful)
1228 StgProperty emptyProperty;
1229 ULARGE_INTEGER newSize;
1230 ULONG propertyIndex;
1231 ULONG lastProperty = 0;
1232 ULONG blockCount = 0;
1235 * obtain the new count of property blocks
1237 blockCount = BlockChainStream_GetCount(
1238 storage->ancestorStorage->rootBlockChain)+1;
1241 * initialize the size used by the property stream
1243 newSize.s.HighPart = 0;
1244 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1247 * add a property block to the property chain
1249 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1252 * memset the empty property in order to initialize the unused newly
1253 * created property
1255 memset(&emptyProperty, 0, sizeof(StgProperty));
1258 * initialize them
1260 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1262 for(
1263 propertyIndex = newPropertyIndex;
1264 propertyIndex < lastProperty;
1265 propertyIndex++)
1267 StorageImpl_WriteProperty(
1268 storage->ancestorStorage,
1269 propertyIndex,
1270 &emptyProperty);
1274 return newPropertyIndex;
1277 /****************************************************************************
1279 * Internal Method
1281 * Case insensitive comparaison of StgProperty.name by first considering
1282 * their size.
1284 * Returns <0 when newPrpoerty < currentProperty
1285 * >0 when newPrpoerty > currentProperty
1286 * 0 when newPrpoerty == currentProperty
1288 static LONG propertyNameCmp(
1289 OLECHAR *newProperty,
1290 OLECHAR *currentProperty)
1292 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1294 if (diff == 0)
1297 * We compare the string themselves only when they are of the same lenght
1299 diff = lstrcmpiW( newProperty, currentProperty);
1302 return diff;
1305 /****************************************************************************
1307 * Internal Method
1309 * Properly link this new element in the property chain.
1311 static void updatePropertyChain(
1312 StorageImpl *storage,
1313 ULONG newPropertyIndex,
1314 StgProperty newProperty)
1316 StgProperty currentProperty;
1319 * Read the root property
1321 StorageImpl_ReadProperty(storage->ancestorStorage,
1322 storage->rootPropertySetIndex,
1323 &currentProperty);
1325 if (currentProperty.dirProperty != PROPERTY_NULL)
1328 * The root storage contains some element, therefore, start the research
1329 * for the appropriate location.
1331 BOOL found = 0;
1332 ULONG current, next, previous, currentPropertyId;
1335 * Keep the StgProperty sequence number of the storage first property
1337 currentPropertyId = currentProperty.dirProperty;
1340 * Read
1342 StorageImpl_ReadProperty(storage->ancestorStorage,
1343 currentProperty.dirProperty,
1344 &currentProperty);
1346 previous = currentProperty.previousProperty;
1347 next = currentProperty.nextProperty;
1348 current = currentPropertyId;
1350 while (found == 0)
1352 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1354 if (diff < 0)
1356 if (previous != PROPERTY_NULL)
1358 StorageImpl_ReadProperty(storage->ancestorStorage,
1359 previous,
1360 &currentProperty);
1361 current = previous;
1363 else
1365 currentProperty.previousProperty = newPropertyIndex;
1366 StorageImpl_WriteProperty(storage->ancestorStorage,
1367 current,
1368 &currentProperty);
1369 found = 1;
1372 else if (diff > 0)
1374 if (next != PROPERTY_NULL)
1376 StorageImpl_ReadProperty(storage->ancestorStorage,
1377 next,
1378 &currentProperty);
1379 current = next;
1381 else
1383 currentProperty.nextProperty = newPropertyIndex;
1384 StorageImpl_WriteProperty(storage->ancestorStorage,
1385 current,
1386 &currentProperty);
1387 found = 1;
1390 else
1393 * Trying to insert an item with the same name in the
1394 * subtree structure.
1396 assert(FALSE);
1399 previous = currentProperty.previousProperty;
1400 next = currentProperty.nextProperty;
1403 else
1406 * The root storage is empty, link the new property to it's dir property
1408 currentProperty.dirProperty = newPropertyIndex;
1409 StorageImpl_WriteProperty(storage->ancestorStorage,
1410 storage->rootPropertySetIndex,
1411 &currentProperty);
1416 /*************************************************************************
1417 * CopyTo (IStorage)
1419 HRESULT WINAPI StorageImpl_CopyTo(
1420 IStorage* iface,
1421 DWORD ciidExclude, /* [in] */
1422 const IID* rgiidExclude, /* [size_is][unique][in] */
1423 SNB snbExclude, /* [unique][in] */
1424 IStorage* pstgDest) /* [unique][in] */
1426 IEnumSTATSTG *elements = 0;
1427 STATSTG curElement, strStat;
1428 HRESULT hr;
1429 IStorage *pstgTmp, *pstgChild;
1430 IStream *pstrTmp, *pstrChild;
1432 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1433 FIXME("Exclude option not implemented\n");
1435 TRACE("(%p, %ld, %p, %p, %p)\n",
1436 iface, ciidExclude, rgiidExclude,
1437 snbExclude, pstgDest);
1440 * Perform a sanity check
1442 if ( pstgDest == 0 )
1443 return STG_E_INVALIDPOINTER;
1446 * Enumerate the elements
1448 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1450 if ( hr != S_OK )
1451 return hr;
1454 * set the class ID
1456 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1457 IStorage_SetClass( pstgDest, &curElement.clsid );
1462 * Obtain the next element
1464 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1466 if ( hr == S_FALSE )
1468 hr = S_OK; /* done, every element has been copied */
1469 break;
1472 if (curElement.type == STGTY_STORAGE)
1475 * open child source storage
1477 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1478 STGM_READ|STGM_SHARE_EXCLUSIVE,
1479 NULL, 0, &pstgChild );
1481 if (hr != S_OK)
1482 break;
1485 * Check if destination storage is not a child of the source
1486 * storage, which will cause an infinite loop
1488 if (pstgChild == pstgDest)
1490 IEnumSTATSTG_Release(elements);
1492 return STG_E_ACCESSDENIED;
1496 * create a new storage in destination storage
1498 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1499 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1500 0, 0,
1501 &pstgTmp );
1503 * if it already exist, don't create a new one use this one
1505 if (hr == STG_E_FILEALREADYEXISTS)
1507 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1508 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1509 NULL, 0, &pstgTmp );
1512 if (hr != S_OK)
1513 break;
1517 * do the copy recursively
1519 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1520 snbExclude, pstgTmp );
1522 IStorage_Release( pstgTmp );
1523 IStorage_Release( pstgChild );
1525 else if (curElement.type == STGTY_STREAM)
1528 * create a new stream in destination storage. If the stream already
1529 * exist, it will be deleted and a new one will be created.
1531 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1532 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1533 0, 0, &pstrTmp );
1535 if (hr != S_OK)
1536 break;
1539 * open child stream storage
1541 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1542 STGM_READ|STGM_SHARE_EXCLUSIVE,
1543 0, &pstrChild );
1545 if (hr != S_OK)
1546 break;
1549 * Get the size of the source stream
1551 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1554 * Set the size of the destination stream.
1556 IStream_SetSize(pstrTmp, strStat.cbSize);
1559 * do the copy
1561 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1562 NULL, NULL );
1564 IStream_Release( pstrTmp );
1565 IStream_Release( pstrChild );
1567 else
1569 WARN("unknown element type: %ld\n", curElement.type);
1572 } while (hr == S_OK);
1575 * Clean-up
1577 IEnumSTATSTG_Release(elements);
1579 return hr;
1582 /*************************************************************************
1583 * MoveElementTo (IStorage)
1585 HRESULT WINAPI StorageImpl_MoveElementTo(
1586 IStorage* iface,
1587 const OLECHAR *pwcsName, /* [string][in] */
1588 IStorage *pstgDest, /* [unique][in] */
1589 const OLECHAR *pwcsNewName,/* [string][in] */
1590 DWORD grfFlags) /* [in] */
1592 FIXME("not implemented!\n");
1593 return E_NOTIMPL;
1596 /*************************************************************************
1597 * Commit (IStorage)
1599 HRESULT WINAPI StorageImpl_Commit(
1600 IStorage* iface,
1601 DWORD grfCommitFlags)/* [in] */
1603 FIXME("(%ld): stub!\n", grfCommitFlags);
1604 return S_OK;
1607 /*************************************************************************
1608 * Revert (IStorage)
1610 HRESULT WINAPI StorageImpl_Revert(
1611 IStorage* iface)
1613 FIXME("not implemented!\n");
1614 return E_NOTIMPL;
1617 /*************************************************************************
1618 * DestroyElement (IStorage)
1620 * Stategy: This implementation is build this way for simplicity not for speed.
1621 * I always delete the top most element of the enumeration and adjust
1622 * the deleted element pointer all the time. This takes longer to
1623 * do but allow to reinvoke DestroyElement whenever we encounter a
1624 * storage object. The optimisation reside in the usage of another
1625 * enumeration stategy that would give all the leaves of a storage
1626 * first. (postfix order)
1628 HRESULT WINAPI StorageImpl_DestroyElement(
1629 IStorage* iface,
1630 const OLECHAR *pwcsName)/* [string][in] */
1632 StorageImpl* const This=(StorageImpl*)iface;
1634 IEnumSTATSTGImpl* propertyEnumeration;
1635 HRESULT hr = S_OK;
1636 BOOL res;
1637 StgProperty propertyToDelete;
1638 StgProperty parentProperty;
1639 ULONG foundPropertyIndexToDelete;
1640 ULONG typeOfRelation;
1641 ULONG parentPropertyId;
1643 TRACE("(%p, %s)\n",
1644 iface, debugstr_w(pwcsName));
1647 * Perform a sanity check on the parameters.
1649 if (pwcsName==NULL)
1650 return STG_E_INVALIDPOINTER;
1653 * Create a property enumeration to search the property with the given name
1655 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1656 This->ancestorStorage,
1657 This->rootPropertySetIndex);
1659 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1660 propertyEnumeration,
1661 pwcsName,
1662 &propertyToDelete);
1664 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1666 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1668 return STG_E_FILENOTFOUND;
1672 * Find the parent property of the property to delete (the one that
1673 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1674 * the parent is This. Otherwise, the parent is one of it's sibling...
1678 * First, read This's StgProperty..
1680 res = StorageImpl_ReadProperty(
1681 This->ancestorStorage,
1682 This->rootPropertySetIndex,
1683 &parentProperty);
1685 assert(res==TRUE);
1688 * Second, check to see if by any chance the actual storage (This) is not
1689 * the parent of the property to delete... We never know...
1691 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1694 * Set data as it would have been done in the else part...
1696 typeOfRelation = PROPERTY_RELATION_DIR;
1697 parentPropertyId = This->rootPropertySetIndex;
1699 else
1702 * Create a property enumeration to search the parent properties, and
1703 * delete it once done.
1705 IEnumSTATSTGImpl* propertyEnumeration2;
1707 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1708 This->ancestorStorage,
1709 This->rootPropertySetIndex);
1711 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1712 propertyEnumeration2,
1713 foundPropertyIndexToDelete,
1714 &parentProperty,
1715 &parentPropertyId);
1717 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1720 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1722 hr = deleteStorageProperty(
1723 This,
1724 foundPropertyIndexToDelete,
1725 propertyToDelete);
1727 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1729 hr = deleteStreamProperty(
1730 This,
1731 foundPropertyIndexToDelete,
1732 propertyToDelete);
1735 if (hr!=S_OK)
1736 return hr;
1739 * Adjust the property chain
1741 hr = adjustPropertyChain(
1742 This,
1743 propertyToDelete,
1744 parentProperty,
1745 parentPropertyId,
1746 typeOfRelation);
1748 return hr;
1752 /************************************************************************
1753 * StorageImpl_Stat (IStorage)
1755 * This method will retrieve information about this storage object.
1757 * See Windows documentation for more details on IStorage methods.
1759 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1760 STATSTG* pstatstg, /* [out] */
1761 DWORD grfStatFlag) /* [in] */
1763 StorageImpl* const This = (StorageImpl*)iface;
1764 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1766 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1768 CoTaskMemFree(pstatstg->pwcsName);
1769 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1770 strcpyW(pstatstg->pwcsName, This->pwcsName);
1773 return result;
1778 /*********************************************************************
1780 * Internal Method
1782 * Perform the deletion of a complete storage node
1785 static HRESULT deleteStorageProperty(
1786 StorageImpl *parentStorage,
1787 ULONG indexOfPropertyToDelete,
1788 StgProperty propertyToDelete)
1790 IEnumSTATSTG *elements = 0;
1791 IStorage *childStorage = 0;
1792 STATSTG currentElement;
1793 HRESULT hr;
1794 HRESULT destroyHr = S_OK;
1797 * Open the storage and enumerate it
1799 hr = StorageBaseImpl_OpenStorage(
1800 (IStorage*)parentStorage,
1801 propertyToDelete.name,
1803 STGM_SHARE_EXCLUSIVE,
1806 &childStorage);
1808 if (hr != S_OK)
1810 return hr;
1814 * Enumerate the elements
1816 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1821 * Obtain the next element
1823 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1824 if (hr==S_OK)
1826 destroyHr = StorageImpl_DestroyElement(
1827 (IStorage*)childStorage,
1828 (OLECHAR*)currentElement.pwcsName);
1830 CoTaskMemFree(currentElement.pwcsName);
1834 * We need to Reset the enumeration every time because we delete elements
1835 * and the enumeration could be invalid
1837 IEnumSTATSTG_Reset(elements);
1839 } while ((hr == S_OK) && (destroyHr == S_OK));
1842 * Invalidate the property by zeroing it's name member.
1844 propertyToDelete.sizeOfNameString = 0;
1846 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1847 indexOfPropertyToDelete,
1848 &propertyToDelete);
1850 IStorage_Release(childStorage);
1851 IEnumSTATSTG_Release(elements);
1853 return destroyHr;
1856 /*********************************************************************
1858 * Internal Method
1860 * Perform the deletion of a stream node
1863 static HRESULT deleteStreamProperty(
1864 StorageImpl *parentStorage,
1865 ULONG indexOfPropertyToDelete,
1866 StgProperty propertyToDelete)
1868 IStream *pis;
1869 HRESULT hr;
1870 ULARGE_INTEGER size;
1872 size.s.HighPart = 0;
1873 size.s.LowPart = 0;
1875 hr = StorageBaseImpl_OpenStream(
1876 (IStorage*)parentStorage,
1877 (OLECHAR*)propertyToDelete.name,
1878 NULL,
1879 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1881 &pis);
1883 if (hr!=S_OK)
1885 return(hr);
1889 * Zap the stream
1891 hr = IStream_SetSize(pis, size);
1893 if(hr != S_OK)
1895 return hr;
1899 * Release the stream object.
1901 IStream_Release(pis);
1904 * Invalidate the property by zeroing it's name member.
1906 propertyToDelete.sizeOfNameString = 0;
1909 * Here we should re-read the property so we get the updated pointer
1910 * but since we are here to zap it, I don't do it...
1912 StorageImpl_WriteProperty(
1913 parentStorage->ancestorStorage,
1914 indexOfPropertyToDelete,
1915 &propertyToDelete);
1917 return S_OK;
1920 /*********************************************************************
1922 * Internal Method
1924 * Finds a placeholder for the StgProperty within the Storage
1927 static HRESULT findPlaceholder(
1928 StorageImpl *storage,
1929 ULONG propertyIndexToStore,
1930 ULONG storePropertyIndex,
1931 INT typeOfRelation)
1933 StgProperty storeProperty;
1934 HRESULT hr = S_OK;
1935 BOOL res = TRUE;
1938 * Read the storage property
1940 res = StorageImpl_ReadProperty(
1941 storage->ancestorStorage,
1942 storePropertyIndex,
1943 &storeProperty);
1945 if(! res)
1947 return E_FAIL;
1950 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1952 if (storeProperty.previousProperty != PROPERTY_NULL)
1954 return findPlaceholder(
1955 storage,
1956 propertyIndexToStore,
1957 storeProperty.previousProperty,
1958 typeOfRelation);
1960 else
1962 storeProperty.previousProperty = propertyIndexToStore;
1965 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1967 if (storeProperty.nextProperty != PROPERTY_NULL)
1969 return findPlaceholder(
1970 storage,
1971 propertyIndexToStore,
1972 storeProperty.nextProperty,
1973 typeOfRelation);
1975 else
1977 storeProperty.nextProperty = propertyIndexToStore;
1980 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1982 if (storeProperty.dirProperty != PROPERTY_NULL)
1984 return findPlaceholder(
1985 storage,
1986 propertyIndexToStore,
1987 storeProperty.dirProperty,
1988 typeOfRelation);
1990 else
1992 storeProperty.dirProperty = propertyIndexToStore;
1996 hr = StorageImpl_WriteProperty(
1997 storage->ancestorStorage,
1998 storePropertyIndex,
1999 &storeProperty);
2001 if(! hr)
2003 return E_FAIL;
2006 return S_OK;
2009 /*************************************************************************
2011 * Internal Method
2013 * This method takes the previous and the next property link of a property
2014 * to be deleted and find them a place in the Storage.
2016 static HRESULT adjustPropertyChain(
2017 StorageImpl *This,
2018 StgProperty propertyToDelete,
2019 StgProperty parentProperty,
2020 ULONG parentPropertyId,
2021 INT typeOfRelation)
2023 ULONG newLinkProperty = PROPERTY_NULL;
2024 BOOL needToFindAPlaceholder = FALSE;
2025 ULONG storeNode = PROPERTY_NULL;
2026 ULONG toStoreNode = PROPERTY_NULL;
2027 INT relationType = 0;
2028 HRESULT hr = S_OK;
2029 BOOL res = TRUE;
2031 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2033 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2036 * Set the parent previous to the property to delete previous
2038 newLinkProperty = propertyToDelete.previousProperty;
2040 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2043 * We also need to find a storage for the other link, setup variables
2044 * to do this at the end...
2046 needToFindAPlaceholder = TRUE;
2047 storeNode = propertyToDelete.previousProperty;
2048 toStoreNode = propertyToDelete.nextProperty;
2049 relationType = PROPERTY_RELATION_NEXT;
2052 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2055 * Set the parent previous to the property to delete next
2057 newLinkProperty = propertyToDelete.nextProperty;
2061 * Link it for real...
2063 parentProperty.previousProperty = newLinkProperty;
2066 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2068 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2071 * Set the parent next to the property to delete next previous
2073 newLinkProperty = propertyToDelete.previousProperty;
2075 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2078 * We also need to find a storage for the other link, setup variables
2079 * to do this at the end...
2081 needToFindAPlaceholder = TRUE;
2082 storeNode = propertyToDelete.previousProperty;
2083 toStoreNode = propertyToDelete.nextProperty;
2084 relationType = PROPERTY_RELATION_NEXT;
2087 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2090 * Set the parent next to the property to delete next
2092 newLinkProperty = propertyToDelete.nextProperty;
2096 * Link it for real...
2098 parentProperty.nextProperty = newLinkProperty;
2100 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2102 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2105 * Set the parent dir to the property to delete previous
2107 newLinkProperty = propertyToDelete.previousProperty;
2109 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2112 * We also need to find a storage for the other link, setup variables
2113 * to do this at the end...
2115 needToFindAPlaceholder = TRUE;
2116 storeNode = propertyToDelete.previousProperty;
2117 toStoreNode = propertyToDelete.nextProperty;
2118 relationType = PROPERTY_RELATION_NEXT;
2121 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2124 * Set the parent dir to the property to delete next
2126 newLinkProperty = propertyToDelete.nextProperty;
2130 * Link it for real...
2132 parentProperty.dirProperty = newLinkProperty;
2136 * Write back the parent property
2138 res = StorageImpl_WriteProperty(
2139 This->ancestorStorage,
2140 parentPropertyId,
2141 &parentProperty);
2142 if(! res)
2144 return E_FAIL;
2148 * If a placeholder is required for the other link, then, find one and
2149 * get out of here...
2151 if (needToFindAPlaceholder)
2153 hr = findPlaceholder(
2154 This,
2155 toStoreNode,
2156 storeNode,
2157 relationType);
2160 return hr;
2164 /******************************************************************************
2165 * SetElementTimes (IStorage)
2167 HRESULT WINAPI StorageImpl_SetElementTimes(
2168 IStorage* iface,
2169 const OLECHAR *pwcsName,/* [string][in] */
2170 const FILETIME *pctime, /* [in] */
2171 const FILETIME *patime, /* [in] */
2172 const FILETIME *pmtime) /* [in] */
2174 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2175 return S_OK;
2178 /******************************************************************************
2179 * SetStateBits (IStorage)
2181 HRESULT WINAPI StorageImpl_SetStateBits(
2182 IStorage* iface,
2183 DWORD grfStateBits,/* [in] */
2184 DWORD grfMask) /* [in] */
2186 FIXME("not implemented!\n");
2187 return E_NOTIMPL;
2190 HRESULT StorageImpl_Construct(
2191 StorageImpl* This,
2192 HANDLE hFile,
2193 LPCOLESTR pwcsName,
2194 ILockBytes* pLkbyt,
2195 DWORD openFlags,
2196 BOOL fileBased,
2197 BOOL fileCreate)
2199 HRESULT hr = S_OK;
2200 StgProperty currentProperty;
2201 BOOL readSuccessful;
2202 ULONG currentPropertyIndex;
2204 if ( FAILED( validateSTGM(openFlags) ))
2205 return STG_E_INVALIDFLAG;
2207 memset(This, 0, sizeof(StorageImpl));
2210 * Initialize the virtual function table.
2212 ICOM_VTBL(This) = &Storage32Impl_Vtbl;
2213 This->v_destructor = &StorageImpl_Destroy;
2216 * This is the top-level storage so initialize the ancestor pointer
2217 * to this.
2219 This->ancestorStorage = This;
2222 * Initialize the physical support of the storage.
2224 This->hFile = hFile;
2227 * Store copy of file path.
2229 if(pwcsName) {
2230 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2231 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2232 if (!This->pwcsName)
2233 return STG_E_INSUFFICIENTMEMORY;
2234 strcpyW(This->pwcsName, pwcsName);
2238 * Initialize the big block cache.
2240 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2241 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2242 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2243 pLkbyt,
2244 openFlags,
2245 This->bigBlockSize,
2246 fileBased);
2248 if (This->bigBlockFile == 0)
2249 return E_FAIL;
2251 if (fileCreate)
2253 ULARGE_INTEGER size;
2254 BYTE* bigBlockBuffer;
2257 * Initialize all header variables:
2258 * - The big block depot consists of one block and it is at block 0
2259 * - The properties start at block 1
2260 * - There is no small block depot
2262 memset( This->bigBlockDepotStart,
2263 BLOCK_UNUSED,
2264 sizeof(This->bigBlockDepotStart));
2266 This->bigBlockDepotCount = 1;
2267 This->bigBlockDepotStart[0] = 0;
2268 This->rootStartBlock = 1;
2269 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2270 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2271 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2272 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2273 This->extBigBlockDepotCount = 0;
2275 StorageImpl_SaveFileHeader(This);
2278 * Add one block for the big block depot and one block for the properties
2280 size.s.HighPart = 0;
2281 size.s.LowPart = This->bigBlockSize * 3;
2282 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2285 * Initialize the big block depot
2287 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2288 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2289 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2290 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2291 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2293 else
2296 * Load the header for the file.
2298 hr = StorageImpl_LoadFileHeader(This);
2300 if (FAILED(hr))
2302 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2304 return hr;
2309 * There is no block depot cached yet.
2311 This->indexBlockDepotCached = 0xFFFFFFFF;
2314 * Start searching for free blocks with block 0.
2316 This->prevFreeBlock = 0;
2319 * Create the block chain abstractions.
2321 if(!(This->rootBlockChain =
2322 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2323 return STG_E_READFAULT;
2325 if(!(This->smallBlockDepotChain =
2326 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2327 PROPERTY_NULL)))
2328 return STG_E_READFAULT;
2331 * Write the root property
2333 if (fileCreate)
2335 StgProperty rootProp;
2337 * Initialize the property chain
2339 memset(&rootProp, 0, sizeof(rootProp));
2340 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2341 sizeof(rootProp.name)/sizeof(WCHAR) );
2342 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2343 rootProp.propertyType = PROPTYPE_ROOT;
2344 rootProp.previousProperty = PROPERTY_NULL;
2345 rootProp.nextProperty = PROPERTY_NULL;
2346 rootProp.dirProperty = PROPERTY_NULL;
2347 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2348 rootProp.size.s.HighPart = 0;
2349 rootProp.size.s.LowPart = 0;
2351 StorageImpl_WriteProperty(This, 0, &rootProp);
2355 * Find the ID of the root in the property sets.
2357 currentPropertyIndex = 0;
2361 readSuccessful = StorageImpl_ReadProperty(
2362 This,
2363 currentPropertyIndex,
2364 &currentProperty);
2366 if (readSuccessful)
2368 if ( (currentProperty.sizeOfNameString != 0 ) &&
2369 (currentProperty.propertyType == PROPTYPE_ROOT) )
2371 This->rootPropertySetIndex = currentPropertyIndex;
2375 currentPropertyIndex++;
2377 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2379 if (!readSuccessful)
2381 /* TODO CLEANUP */
2382 return STG_E_READFAULT;
2386 * Create the block chain abstraction for the small block root chain.
2388 if(!(This->smallBlockRootChain =
2389 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2390 return STG_E_READFAULT;
2392 return hr;
2395 void StorageImpl_Destroy(
2396 StorageImpl* This)
2398 TRACE("(%p)\n", This);
2400 if(This->pwcsName)
2401 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2403 BlockChainStream_Destroy(This->smallBlockRootChain);
2404 BlockChainStream_Destroy(This->rootBlockChain);
2405 BlockChainStream_Destroy(This->smallBlockDepotChain);
2407 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2408 return;
2411 /******************************************************************************
2412 * Storage32Impl_GetNextFreeBigBlock
2414 * Returns the index of the next free big block.
2415 * If the big block depot is filled, this method will enlarge it.
2418 ULONG StorageImpl_GetNextFreeBigBlock(
2419 StorageImpl* This)
2421 ULONG depotBlockIndexPos;
2422 void *depotBuffer;
2423 ULONG depotBlockOffset;
2424 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2425 ULONG nextBlockIndex = BLOCK_SPECIAL;
2426 int depotIndex = 0;
2427 ULONG freeBlock = BLOCK_UNUSED;
2429 depotIndex = This->prevFreeBlock / blocksPerDepot;
2430 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2433 * Scan the entire big block depot until we find a block marked free
2435 while (nextBlockIndex != BLOCK_UNUSED)
2437 if (depotIndex < COUNT_BBDEPOTINHEADER)
2439 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2442 * Grow the primary depot.
2444 if (depotBlockIndexPos == BLOCK_UNUSED)
2446 depotBlockIndexPos = depotIndex*blocksPerDepot;
2449 * Add a block depot.
2451 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2452 This->bigBlockDepotCount++;
2453 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2456 * Flag it as a block depot.
2458 StorageImpl_SetNextBlockInChain(This,
2459 depotBlockIndexPos,
2460 BLOCK_SPECIAL);
2462 /* Save new header information.
2464 StorageImpl_SaveFileHeader(This);
2467 else
2469 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2471 if (depotBlockIndexPos == BLOCK_UNUSED)
2474 * Grow the extended depot.
2476 ULONG extIndex = BLOCK_UNUSED;
2477 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2478 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2480 if (extBlockOffset == 0)
2482 /* We need an extended block.
2484 extIndex = Storage32Impl_AddExtBlockDepot(This);
2485 This->extBigBlockDepotCount++;
2486 depotBlockIndexPos = extIndex + 1;
2488 else
2489 depotBlockIndexPos = depotIndex * blocksPerDepot;
2492 * Add a block depot and mark it in the extended block.
2494 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2495 This->bigBlockDepotCount++;
2496 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2498 /* Flag the block depot.
2500 StorageImpl_SetNextBlockInChain(This,
2501 depotBlockIndexPos,
2502 BLOCK_SPECIAL);
2504 /* If necessary, flag the extended depot block.
2506 if (extIndex != BLOCK_UNUSED)
2507 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2509 /* Save header information.
2511 StorageImpl_SaveFileHeader(This);
2515 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2517 if (depotBuffer != 0)
2519 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2520 ( nextBlockIndex != BLOCK_UNUSED))
2522 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2524 if (nextBlockIndex == BLOCK_UNUSED)
2526 freeBlock = (depotIndex * blocksPerDepot) +
2527 (depotBlockOffset/sizeof(ULONG));
2530 depotBlockOffset += sizeof(ULONG);
2533 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2536 depotIndex++;
2537 depotBlockOffset = 0;
2540 This->prevFreeBlock = freeBlock;
2542 return freeBlock;
2545 /******************************************************************************
2546 * Storage32Impl_AddBlockDepot
2548 * This will create a depot block, essentially it is a block initialized
2549 * to BLOCK_UNUSEDs.
2551 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2553 BYTE* blockBuffer;
2555 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2558 * Initialize blocks as free
2560 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2562 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2565 /******************************************************************************
2566 * Storage32Impl_GetExtDepotBlock
2568 * Returns the index of the block that corresponds to the specified depot
2569 * index. This method is only for depot indexes equal or greater than
2570 * COUNT_BBDEPOTINHEADER.
2572 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2574 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2575 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2576 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2577 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2578 ULONG blockIndex = BLOCK_UNUSED;
2579 ULONG extBlockIndex = This->extBigBlockDepotStart;
2581 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2583 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2584 return BLOCK_UNUSED;
2586 while (extBlockCount > 0)
2588 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2589 extBlockCount--;
2592 if (extBlockIndex != BLOCK_UNUSED)
2594 BYTE* depotBuffer;
2596 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2598 if (depotBuffer != 0)
2600 StorageUtl_ReadDWord(depotBuffer,
2601 extBlockOffset * sizeof(ULONG),
2602 &blockIndex);
2604 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2608 return blockIndex;
2611 /******************************************************************************
2612 * Storage32Impl_SetExtDepotBlock
2614 * Associates the specified block index to the specified depot index.
2615 * This method is only for depot indexes equal or greater than
2616 * COUNT_BBDEPOTINHEADER.
2618 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2619 ULONG depotIndex,
2620 ULONG blockIndex)
2622 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2623 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2624 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2625 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2626 ULONG extBlockIndex = This->extBigBlockDepotStart;
2628 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2630 while (extBlockCount > 0)
2632 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2633 extBlockCount--;
2636 if (extBlockIndex != BLOCK_UNUSED)
2638 BYTE* depotBuffer;
2640 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2642 if (depotBuffer != 0)
2644 StorageUtl_WriteDWord(depotBuffer,
2645 extBlockOffset * sizeof(ULONG),
2646 blockIndex);
2648 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2653 /******************************************************************************
2654 * Storage32Impl_AddExtBlockDepot
2656 * Creates an extended depot block.
2658 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2660 ULONG numExtBlocks = This->extBigBlockDepotCount;
2661 ULONG nextExtBlock = This->extBigBlockDepotStart;
2662 BYTE* depotBuffer = NULL;
2663 ULONG index = BLOCK_UNUSED;
2664 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2665 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2666 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2668 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2669 blocksPerDepotBlock;
2671 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2674 * The first extended block.
2676 This->extBigBlockDepotStart = index;
2678 else
2680 int i;
2682 * Follow the chain to the last one.
2684 for (i = 0; i < (numExtBlocks - 1); i++)
2686 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2690 * Add the new extended block to the chain.
2692 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2693 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2694 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2698 * Initialize this block.
2700 depotBuffer = StorageImpl_GetBigBlock(This, index);
2701 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2702 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2704 return index;
2707 /******************************************************************************
2708 * Storage32Impl_FreeBigBlock
2710 * This method will flag the specified block as free in the big block depot.
2712 void StorageImpl_FreeBigBlock(
2713 StorageImpl* This,
2714 ULONG blockIndex)
2716 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2718 if (blockIndex < This->prevFreeBlock)
2719 This->prevFreeBlock = blockIndex;
2722 /************************************************************************
2723 * Storage32Impl_GetNextBlockInChain
2725 * This method will retrieve the block index of the next big block in
2726 * in the chain.
2728 * Params: This - Pointer to the Storage object.
2729 * blockIndex - Index of the block to retrieve the chain
2730 * for.
2731 * nextBlockIndex - receives the return value.
2733 * Returns: This method returns the index of the next block in the chain.
2734 * It will return the constants:
2735 * BLOCK_SPECIAL - If the block given was not part of a
2736 * chain.
2737 * BLOCK_END_OF_CHAIN - If the block given was the last in
2738 * a chain.
2739 * BLOCK_UNUSED - If the block given was not past of a chain
2740 * and is available.
2741 * BLOCK_EXTBBDEPOT - This block is part of the extended
2742 * big block depot.
2744 * See Windows documentation for more details on IStorage methods.
2746 HRESULT StorageImpl_GetNextBlockInChain(
2747 StorageImpl* This,
2748 ULONG blockIndex,
2749 ULONG* nextBlockIndex)
2751 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2752 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2753 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2754 void* depotBuffer;
2755 ULONG depotBlockIndexPos;
2756 int index;
2758 *nextBlockIndex = BLOCK_SPECIAL;
2760 if(depotBlockCount >= This->bigBlockDepotCount)
2762 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2763 This->bigBlockDepotCount);
2764 return STG_E_READFAULT;
2768 * Cache the currently accessed depot block.
2770 if (depotBlockCount != This->indexBlockDepotCached)
2772 This->indexBlockDepotCached = depotBlockCount;
2774 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2776 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2778 else
2781 * We have to look in the extended depot.
2783 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2786 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2788 if (!depotBuffer)
2789 return STG_E_READFAULT;
2791 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2793 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2794 This->blockDepotCached[index] = *nextBlockIndex;
2796 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2799 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2801 return S_OK;
2804 /******************************************************************************
2805 * Storage32Impl_GetNextExtendedBlock
2807 * Given an extended block this method will return the next extended block.
2809 * NOTES:
2810 * The last ULONG of an extended block is the block index of the next
2811 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2812 * depot.
2814 * Return values:
2815 * - The index of the next extended block
2816 * - BLOCK_UNUSED: there is no next extended block.
2817 * - Any other return values denotes failure.
2819 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2821 ULONG nextBlockIndex = BLOCK_SPECIAL;
2822 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2823 void* depotBuffer;
2825 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2827 if (depotBuffer!=0)
2829 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2831 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2834 return nextBlockIndex;
2837 /******************************************************************************
2838 * Storage32Impl_SetNextBlockInChain
2840 * This method will write the index of the specified block's next block
2841 * in the big block depot.
2843 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2844 * do the following
2846 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2847 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2848 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2851 void StorageImpl_SetNextBlockInChain(
2852 StorageImpl* This,
2853 ULONG blockIndex,
2854 ULONG nextBlock)
2856 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2857 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2858 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2859 ULONG depotBlockIndexPos;
2860 void* depotBuffer;
2862 assert(depotBlockCount < This->bigBlockDepotCount);
2863 assert(blockIndex != nextBlock);
2865 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2867 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2869 else
2872 * We have to look in the extended depot.
2874 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2877 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2879 if (depotBuffer!=0)
2881 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2882 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2886 * Update the cached block depot, if necessary.
2888 if (depotBlockCount == This->indexBlockDepotCached)
2890 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2894 /******************************************************************************
2895 * Storage32Impl_LoadFileHeader
2897 * This method will read in the file header, i.e. big block index -1.
2899 HRESULT StorageImpl_LoadFileHeader(
2900 StorageImpl* This)
2902 HRESULT hr = STG_E_FILENOTFOUND;
2903 void* headerBigBlock = NULL;
2904 int index;
2907 * Get a pointer to the big block of data containing the header.
2909 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2912 * Extract the information from the header.
2914 if (headerBigBlock!=0)
2917 * Check for the "magic number" signature and return an error if it is not
2918 * found.
2920 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2922 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2923 return STG_E_OLDFORMAT;
2926 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2928 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2929 return STG_E_INVALIDHEADER;
2932 StorageUtl_ReadWord(
2933 headerBigBlock,
2934 OFFSET_BIGBLOCKSIZEBITS,
2935 &This->bigBlockSizeBits);
2937 StorageUtl_ReadWord(
2938 headerBigBlock,
2939 OFFSET_SMALLBLOCKSIZEBITS,
2940 &This->smallBlockSizeBits);
2942 StorageUtl_ReadDWord(
2943 headerBigBlock,
2944 OFFSET_BBDEPOTCOUNT,
2945 &This->bigBlockDepotCount);
2947 StorageUtl_ReadDWord(
2948 headerBigBlock,
2949 OFFSET_ROOTSTARTBLOCK,
2950 &This->rootStartBlock);
2952 StorageUtl_ReadDWord(
2953 headerBigBlock,
2954 OFFSET_SBDEPOTSTART,
2955 &This->smallBlockDepotStart);
2957 StorageUtl_ReadDWord(
2958 headerBigBlock,
2959 OFFSET_EXTBBDEPOTSTART,
2960 &This->extBigBlockDepotStart);
2962 StorageUtl_ReadDWord(
2963 headerBigBlock,
2964 OFFSET_EXTBBDEPOTCOUNT,
2965 &This->extBigBlockDepotCount);
2967 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2969 StorageUtl_ReadDWord(
2970 headerBigBlock,
2971 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2972 &(This->bigBlockDepotStart[index]));
2976 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2978 if ((1 << 2) == 4)
2980 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2981 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2983 else
2985 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2986 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2990 * Right now, the code is making some assumptions about the size of the
2991 * blocks, just make sure they are what we're expecting.
2993 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2994 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2996 WARN("Broken OLE storage file\n");
2997 hr = STG_E_INVALIDHEADER;
2999 else
3000 hr = S_OK;
3003 * Release the block.
3005 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3008 return hr;
3011 /******************************************************************************
3012 * Storage32Impl_SaveFileHeader
3014 * This method will save to the file the header, i.e. big block -1.
3016 void StorageImpl_SaveFileHeader(
3017 StorageImpl* This)
3019 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3020 int index;
3021 BOOL success;
3024 * Get a pointer to the big block of data containing the header.
3026 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3029 * If the block read failed, the file is probably new.
3031 if (!success)
3034 * Initialize for all unknown fields.
3036 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3039 * Initialize the magic number.
3041 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3044 * And a bunch of things we don't know what they mean
3046 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3047 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3048 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3049 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3053 * Write the information to the header.
3055 StorageUtl_WriteWord(
3056 headerBigBlock,
3057 OFFSET_BIGBLOCKSIZEBITS,
3058 This->bigBlockSizeBits);
3060 StorageUtl_WriteWord(
3061 headerBigBlock,
3062 OFFSET_SMALLBLOCKSIZEBITS,
3063 This->smallBlockSizeBits);
3065 StorageUtl_WriteDWord(
3066 headerBigBlock,
3067 OFFSET_BBDEPOTCOUNT,
3068 This->bigBlockDepotCount);
3070 StorageUtl_WriteDWord(
3071 headerBigBlock,
3072 OFFSET_ROOTSTARTBLOCK,
3073 This->rootStartBlock);
3075 StorageUtl_WriteDWord(
3076 headerBigBlock,
3077 OFFSET_SBDEPOTSTART,
3078 This->smallBlockDepotStart);
3080 StorageUtl_WriteDWord(
3081 headerBigBlock,
3082 OFFSET_SBDEPOTCOUNT,
3083 This->smallBlockDepotChain ?
3084 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3086 StorageUtl_WriteDWord(
3087 headerBigBlock,
3088 OFFSET_EXTBBDEPOTSTART,
3089 This->extBigBlockDepotStart);
3091 StorageUtl_WriteDWord(
3092 headerBigBlock,
3093 OFFSET_EXTBBDEPOTCOUNT,
3094 This->extBigBlockDepotCount);
3096 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3098 StorageUtl_WriteDWord(
3099 headerBigBlock,
3100 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3101 (This->bigBlockDepotStart[index]));
3105 * Write the big block back to the file.
3107 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3110 /******************************************************************************
3111 * Storage32Impl_ReadProperty
3113 * This method will read the specified property from the property chain.
3115 BOOL StorageImpl_ReadProperty(
3116 StorageImpl* This,
3117 ULONG index,
3118 StgProperty* buffer)
3120 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3121 ULARGE_INTEGER offsetInPropSet;
3122 BOOL readSuccessful;
3123 ULONG bytesRead;
3125 offsetInPropSet.s.HighPart = 0;
3126 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3128 readSuccessful = BlockChainStream_ReadAt(
3129 This->rootBlockChain,
3130 offsetInPropSet,
3131 PROPSET_BLOCK_SIZE,
3132 currentProperty,
3133 &bytesRead);
3135 if (readSuccessful)
3137 /* replace the name of root entry (often "Root Entry") by the file name */
3138 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3139 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3141 memset(buffer->name, 0, sizeof(buffer->name));
3142 memcpy(
3143 buffer->name,
3144 propName,
3145 PROPERTY_NAME_BUFFER_LEN );
3146 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3148 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3150 StorageUtl_ReadWord(
3151 currentProperty,
3152 OFFSET_PS_NAMELENGTH,
3153 &buffer->sizeOfNameString);
3155 StorageUtl_ReadDWord(
3156 currentProperty,
3157 OFFSET_PS_PREVIOUSPROP,
3158 &buffer->previousProperty);
3160 StorageUtl_ReadDWord(
3161 currentProperty,
3162 OFFSET_PS_NEXTPROP,
3163 &buffer->nextProperty);
3165 StorageUtl_ReadDWord(
3166 currentProperty,
3167 OFFSET_PS_DIRPROP,
3168 &buffer->dirProperty);
3170 StorageUtl_ReadGUID(
3171 currentProperty,
3172 OFFSET_PS_GUID,
3173 &buffer->propertyUniqueID);
3175 StorageUtl_ReadDWord(
3176 currentProperty,
3177 OFFSET_PS_TSS1,
3178 &buffer->timeStampS1);
3180 StorageUtl_ReadDWord(
3181 currentProperty,
3182 OFFSET_PS_TSD1,
3183 &buffer->timeStampD1);
3185 StorageUtl_ReadDWord(
3186 currentProperty,
3187 OFFSET_PS_TSS2,
3188 &buffer->timeStampS2);
3190 StorageUtl_ReadDWord(
3191 currentProperty,
3192 OFFSET_PS_TSD2,
3193 &buffer->timeStampD2);
3195 StorageUtl_ReadDWord(
3196 currentProperty,
3197 OFFSET_PS_STARTBLOCK,
3198 &buffer->startingBlock);
3200 StorageUtl_ReadDWord(
3201 currentProperty,
3202 OFFSET_PS_SIZE,
3203 &buffer->size.s.LowPart);
3205 buffer->size.s.HighPart = 0;
3208 return readSuccessful;
3211 /*********************************************************************
3212 * Write the specified property into the property chain
3214 BOOL StorageImpl_WriteProperty(
3215 StorageImpl* This,
3216 ULONG index,
3217 StgProperty* buffer)
3219 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3220 ULARGE_INTEGER offsetInPropSet;
3221 BOOL writeSuccessful;
3222 ULONG bytesWritten;
3224 offsetInPropSet.s.HighPart = 0;
3225 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3227 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3229 memcpy(
3230 currentProperty + OFFSET_PS_NAME,
3231 buffer->name,
3232 PROPERTY_NAME_BUFFER_LEN );
3234 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3236 StorageUtl_WriteWord(
3237 currentProperty,
3238 OFFSET_PS_NAMELENGTH,
3239 buffer->sizeOfNameString);
3241 StorageUtl_WriteDWord(
3242 currentProperty,
3243 OFFSET_PS_PREVIOUSPROP,
3244 buffer->previousProperty);
3246 StorageUtl_WriteDWord(
3247 currentProperty,
3248 OFFSET_PS_NEXTPROP,
3249 buffer->nextProperty);
3251 StorageUtl_WriteDWord(
3252 currentProperty,
3253 OFFSET_PS_DIRPROP,
3254 buffer->dirProperty);
3256 StorageUtl_WriteGUID(
3257 currentProperty,
3258 OFFSET_PS_GUID,
3259 &buffer->propertyUniqueID);
3261 StorageUtl_WriteDWord(
3262 currentProperty,
3263 OFFSET_PS_TSS1,
3264 buffer->timeStampS1);
3266 StorageUtl_WriteDWord(
3267 currentProperty,
3268 OFFSET_PS_TSD1,
3269 buffer->timeStampD1);
3271 StorageUtl_WriteDWord(
3272 currentProperty,
3273 OFFSET_PS_TSS2,
3274 buffer->timeStampS2);
3276 StorageUtl_WriteDWord(
3277 currentProperty,
3278 OFFSET_PS_TSD2,
3279 buffer->timeStampD2);
3281 StorageUtl_WriteDWord(
3282 currentProperty,
3283 OFFSET_PS_STARTBLOCK,
3284 buffer->startingBlock);
3286 StorageUtl_WriteDWord(
3287 currentProperty,
3288 OFFSET_PS_SIZE,
3289 buffer->size.s.LowPart);
3291 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3292 offsetInPropSet,
3293 PROPSET_BLOCK_SIZE,
3294 currentProperty,
3295 &bytesWritten);
3296 return writeSuccessful;
3299 BOOL StorageImpl_ReadBigBlock(
3300 StorageImpl* This,
3301 ULONG blockIndex,
3302 void* buffer)
3304 void* bigBlockBuffer;
3306 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3308 if (bigBlockBuffer!=0)
3310 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3312 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3314 return TRUE;
3317 return FALSE;
3320 BOOL StorageImpl_WriteBigBlock(
3321 StorageImpl* This,
3322 ULONG blockIndex,
3323 void* buffer)
3325 void* bigBlockBuffer;
3327 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3329 if (bigBlockBuffer!=0)
3331 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3333 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3335 return TRUE;
3338 return FALSE;
3341 void* StorageImpl_GetROBigBlock(
3342 StorageImpl* This,
3343 ULONG blockIndex)
3345 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3348 void* StorageImpl_GetBigBlock(
3349 StorageImpl* This,
3350 ULONG blockIndex)
3352 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3355 void StorageImpl_ReleaseBigBlock(
3356 StorageImpl* This,
3357 void* pBigBlock)
3359 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3362 /******************************************************************************
3363 * Storage32Impl_SmallBlocksToBigBlocks
3365 * This method will convert a small block chain to a big block chain.
3366 * The small block chain will be destroyed.
3368 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3369 StorageImpl* This,
3370 SmallBlockChainStream** ppsbChain)
3372 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3373 ULARGE_INTEGER size, offset;
3374 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3375 ULONG propertyIndex;
3376 BOOL successRead, successWrite;
3377 StgProperty chainProperty;
3378 BYTE *buffer;
3379 BlockChainStream *bbTempChain = NULL;
3380 BlockChainStream *bigBlockChain = NULL;
3383 * Create a temporary big block chain that doesn't have
3384 * an associated property. This temporary chain will be
3385 * used to copy data from small blocks to big blocks.
3387 bbTempChain = BlockChainStream_Construct(This,
3388 &bbHeadOfChain,
3389 PROPERTY_NULL);
3390 if(!bbTempChain) return NULL;
3392 * Grow the big block chain.
3394 size = SmallBlockChainStream_GetSize(*ppsbChain);
3395 BlockChainStream_SetSize(bbTempChain, size);
3398 * Copy the contents of the small block chain to the big block chain
3399 * by small block size increments.
3401 offset.s.LowPart = 0;
3402 offset.s.HighPart = 0;
3403 cbTotalRead = 0;
3404 cbTotalWritten = 0;
3406 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3409 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3410 offset,
3411 DEF_SMALL_BLOCK_SIZE,
3412 buffer,
3413 &cbRead);
3414 cbTotalRead += cbRead;
3416 successWrite = BlockChainStream_WriteAt(bbTempChain,
3417 offset,
3418 cbRead,
3419 buffer,
3420 &cbWritten);
3421 cbTotalWritten += cbWritten;
3423 offset.s.LowPart += This->smallBlockSize;
3425 } while (successRead && successWrite);
3426 HeapFree(GetProcessHeap(),0,buffer);
3428 assert(cbTotalRead == cbTotalWritten);
3431 * Destroy the small block chain.
3433 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3434 size.s.HighPart = 0;
3435 size.s.LowPart = 0;
3436 SmallBlockChainStream_SetSize(*ppsbChain, size);
3437 SmallBlockChainStream_Destroy(*ppsbChain);
3438 *ppsbChain = 0;
3441 * Change the property information. This chain is now a big block chain
3442 * and it doesn't reside in the small blocks chain anymore.
3444 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3446 chainProperty.startingBlock = bbHeadOfChain;
3448 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3451 * Destroy the temporary propertyless big block chain.
3452 * Create a new big block chain associated with this property.
3454 BlockChainStream_Destroy(bbTempChain);
3455 bigBlockChain = BlockChainStream_Construct(This,
3456 NULL,
3457 propertyIndex);
3459 return bigBlockChain;
3462 /******************************************************************************
3463 ** Storage32InternalImpl implementation
3466 StorageInternalImpl* StorageInternalImpl_Construct(
3467 StorageImpl* ancestorStorage,
3468 ULONG rootPropertyIndex)
3470 StorageInternalImpl* newStorage;
3473 * Allocate space for the new storage object
3475 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3477 if (newStorage!=0)
3479 memset(newStorage, 0, sizeof(StorageInternalImpl));
3482 * Initialize the virtual function table.
3484 ICOM_VTBL(newStorage) = &Storage32InternalImpl_Vtbl;
3485 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3488 * Keep the ancestor storage pointer and nail a reference to it.
3490 newStorage->ancestorStorage = ancestorStorage;
3491 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3494 * Keep the index of the root property set for this storage,
3496 newStorage->rootPropertySetIndex = rootPropertyIndex;
3498 return newStorage;
3501 return 0;
3504 void StorageInternalImpl_Destroy(
3505 StorageInternalImpl* This)
3507 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3508 HeapFree(GetProcessHeap(), 0, This);
3511 /******************************************************************************
3513 ** Storage32InternalImpl_Commit
3515 ** The non-root storages cannot be opened in transacted mode thus this function
3516 ** does nothing.
3518 HRESULT WINAPI StorageInternalImpl_Commit(
3519 IStorage* iface,
3520 DWORD grfCommitFlags) /* [in] */
3522 return S_OK;
3525 /******************************************************************************
3527 ** Storage32InternalImpl_Revert
3529 ** The non-root storages cannot be opened in transacted mode thus this function
3530 ** does nothing.
3532 HRESULT WINAPI StorageInternalImpl_Revert(
3533 IStorage* iface)
3535 return S_OK;
3538 /******************************************************************************
3539 ** IEnumSTATSTGImpl implementation
3542 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3543 StorageImpl* parentStorage,
3544 ULONG firstPropertyNode)
3546 IEnumSTATSTGImpl* newEnumeration;
3548 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3550 if (newEnumeration!=0)
3553 * Set-up the virtual function table and reference count.
3555 ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3556 newEnumeration->ref = 0;
3559 * We want to nail-down the reference to the storage in case the
3560 * enumeration out-lives the storage in the client application.
3562 newEnumeration->parentStorage = parentStorage;
3563 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3565 newEnumeration->firstPropertyNode = firstPropertyNode;
3568 * Initialize the search stack
3570 newEnumeration->stackSize = 0;
3571 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3572 newEnumeration->stackToVisit =
3573 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3576 * Make sure the current node of the iterator is the first one.
3578 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3581 return newEnumeration;
3584 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3586 IStorage_Release((IStorage*)This->parentStorage);
3587 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3588 HeapFree(GetProcessHeap(), 0, This);
3591 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3592 IEnumSTATSTG* iface,
3593 REFIID riid,
3594 void** ppvObject)
3596 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3599 * Perform a sanity check on the parameters.
3601 if (ppvObject==0)
3602 return E_INVALIDARG;
3605 * Initialize the return parameter.
3607 *ppvObject = 0;
3610 * Compare the riid with the interface IDs implemented by this object.
3612 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3614 *ppvObject = (IEnumSTATSTG*)This;
3616 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3618 *ppvObject = (IEnumSTATSTG*)This;
3622 * Check that we obtained an interface.
3624 if ((*ppvObject)==0)
3625 return E_NOINTERFACE;
3628 * Query Interface always increases the reference count by one when it is
3629 * successful
3631 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3633 return S_OK;
3636 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3637 IEnumSTATSTG* iface)
3639 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3641 This->ref++;
3642 return This->ref;
3645 ULONG WINAPI IEnumSTATSTGImpl_Release(
3646 IEnumSTATSTG* iface)
3648 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3650 ULONG newRef;
3652 This->ref--;
3653 newRef = This->ref;
3656 * If the reference count goes down to 0, perform suicide.
3658 if (newRef==0)
3660 IEnumSTATSTGImpl_Destroy(This);
3663 return newRef;
3666 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3667 IEnumSTATSTG* iface,
3668 ULONG celt,
3669 STATSTG* rgelt,
3670 ULONG* pceltFetched)
3672 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3674 StgProperty currentProperty;
3675 STATSTG* currentReturnStruct = rgelt;
3676 ULONG objectFetched = 0;
3677 ULONG currentSearchNode;
3680 * Perform a sanity check on the parameters.
3682 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3683 return E_INVALIDARG;
3686 * To avoid the special case, get another pointer to a ULONG value if
3687 * the caller didn't supply one.
3689 if (pceltFetched==0)
3690 pceltFetched = &objectFetched;
3693 * Start the iteration, we will iterate until we hit the end of the
3694 * linked list or until we hit the number of items to iterate through
3696 *pceltFetched = 0;
3699 * Start with the node at the top of the stack.
3701 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3703 while ( ( *pceltFetched < celt) &&
3704 ( currentSearchNode!=PROPERTY_NULL) )
3707 * Remove the top node from the stack
3709 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3712 * Read the property from the storage.
3714 StorageImpl_ReadProperty(This->parentStorage,
3715 currentSearchNode,
3716 &currentProperty);
3719 * Copy the information to the return buffer.
3721 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3722 &currentProperty,
3723 STATFLAG_DEFAULT);
3726 * Step to the next item in the iteration
3728 (*pceltFetched)++;
3729 currentReturnStruct++;
3732 * Push the next search node in the search stack.
3734 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3737 * continue the iteration.
3739 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3742 if (*pceltFetched == celt)
3743 return S_OK;
3745 return S_FALSE;
3749 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3750 IEnumSTATSTG* iface,
3751 ULONG celt)
3753 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3755 StgProperty currentProperty;
3756 ULONG objectFetched = 0;
3757 ULONG currentSearchNode;
3760 * Start with the node at the top of the stack.
3762 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3764 while ( (objectFetched < celt) &&
3765 (currentSearchNode!=PROPERTY_NULL) )
3768 * Remove the top node from the stack
3770 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3773 * Read the property from the storage.
3775 StorageImpl_ReadProperty(This->parentStorage,
3776 currentSearchNode,
3777 &currentProperty);
3780 * Step to the next item in the iteration
3782 objectFetched++;
3785 * Push the next search node in the search stack.
3787 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3790 * continue the iteration.
3792 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3795 if (objectFetched == celt)
3796 return S_OK;
3798 return S_FALSE;
3801 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3802 IEnumSTATSTG* iface)
3804 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3806 StgProperty rootProperty;
3807 BOOL readSuccessful;
3810 * Re-initialize the search stack to an empty stack
3812 This->stackSize = 0;
3815 * Read the root property from the storage.
3817 readSuccessful = StorageImpl_ReadProperty(
3818 This->parentStorage,
3819 This->firstPropertyNode,
3820 &rootProperty);
3822 if (readSuccessful)
3824 assert(rootProperty.sizeOfNameString!=0);
3827 * Push the search node in the search stack.
3829 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3832 return S_OK;
3835 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3836 IEnumSTATSTG* iface,
3837 IEnumSTATSTG** ppenum)
3839 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3841 IEnumSTATSTGImpl* newClone;
3844 * Perform a sanity check on the parameters.
3846 if (ppenum==0)
3847 return E_INVALIDARG;
3849 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3850 This->firstPropertyNode);
3854 * The new clone enumeration must point to the same current node as
3855 * the ole one.
3857 newClone->stackSize = This->stackSize ;
3858 newClone->stackMaxSize = This->stackMaxSize ;
3859 newClone->stackToVisit =
3860 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3862 memcpy(
3863 newClone->stackToVisit,
3864 This->stackToVisit,
3865 sizeof(ULONG) * newClone->stackSize);
3867 *ppenum = (IEnumSTATSTG*)newClone;
3870 * Don't forget to nail down a reference to the clone before
3871 * returning it.
3873 IEnumSTATSTGImpl_AddRef(*ppenum);
3875 return S_OK;
3878 INT IEnumSTATSTGImpl_FindParentProperty(
3879 IEnumSTATSTGImpl *This,
3880 ULONG childProperty,
3881 StgProperty *currentProperty,
3882 ULONG *thisNodeId)
3884 ULONG currentSearchNode;
3885 ULONG foundNode;
3888 * To avoid the special case, get another pointer to a ULONG value if
3889 * the caller didn't supply one.
3891 if (thisNodeId==0)
3892 thisNodeId = &foundNode;
3895 * Start with the node at the top of the stack.
3897 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3900 while (currentSearchNode!=PROPERTY_NULL)
3903 * Store the current node in the returned parameters
3905 *thisNodeId = currentSearchNode;
3908 * Remove the top node from the stack
3910 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3913 * Read the property from the storage.
3915 StorageImpl_ReadProperty(
3916 This->parentStorage,
3917 currentSearchNode,
3918 currentProperty);
3920 if (currentProperty->previousProperty == childProperty)
3921 return PROPERTY_RELATION_PREVIOUS;
3923 else if (currentProperty->nextProperty == childProperty)
3924 return PROPERTY_RELATION_NEXT;
3926 else if (currentProperty->dirProperty == childProperty)
3927 return PROPERTY_RELATION_DIR;
3930 * Push the next search node in the search stack.
3932 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3935 * continue the iteration.
3937 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3940 return PROPERTY_NULL;
3943 ULONG IEnumSTATSTGImpl_FindProperty(
3944 IEnumSTATSTGImpl* This,
3945 const OLECHAR* lpszPropName,
3946 StgProperty* currentProperty)
3948 ULONG currentSearchNode;
3951 * Start with the node at the top of the stack.
3953 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3955 while (currentSearchNode!=PROPERTY_NULL)
3958 * Remove the top node from the stack
3960 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3963 * Read the property from the storage.
3965 StorageImpl_ReadProperty(This->parentStorage,
3966 currentSearchNode,
3967 currentProperty);
3969 if ( propertyNameCmp(
3970 (OLECHAR*)currentProperty->name,
3971 (OLECHAR*)lpszPropName) == 0)
3972 return currentSearchNode;
3975 * Push the next search node in the search stack.
3977 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3980 * continue the iteration.
3982 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3985 return PROPERTY_NULL;
3988 void IEnumSTATSTGImpl_PushSearchNode(
3989 IEnumSTATSTGImpl* This,
3990 ULONG nodeToPush)
3992 StgProperty rootProperty;
3993 BOOL readSuccessful;
3996 * First, make sure we're not trying to push an unexisting node.
3998 if (nodeToPush==PROPERTY_NULL)
3999 return;
4002 * First push the node to the stack
4004 if (This->stackSize == This->stackMaxSize)
4006 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4008 This->stackToVisit = HeapReAlloc(
4009 GetProcessHeap(),
4011 This->stackToVisit,
4012 sizeof(ULONG) * This->stackMaxSize);
4015 This->stackToVisit[This->stackSize] = nodeToPush;
4016 This->stackSize++;
4019 * Read the root property from the storage.
4021 readSuccessful = StorageImpl_ReadProperty(
4022 This->parentStorage,
4023 nodeToPush,
4024 &rootProperty);
4026 if (readSuccessful)
4028 assert(rootProperty.sizeOfNameString!=0);
4031 * Push the previous search node in the search stack.
4033 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4037 ULONG IEnumSTATSTGImpl_PopSearchNode(
4038 IEnumSTATSTGImpl* This,
4039 BOOL remove)
4041 ULONG topNode;
4043 if (This->stackSize == 0)
4044 return PROPERTY_NULL;
4046 topNode = This->stackToVisit[This->stackSize-1];
4048 if (remove)
4049 This->stackSize--;
4051 return topNode;
4054 /******************************************************************************
4055 ** StorageUtl implementation
4058 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4060 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4063 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4065 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4068 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4070 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4073 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4075 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4078 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4080 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4081 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4082 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4084 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4087 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4089 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4090 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4091 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4093 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4096 void StorageUtl_CopyPropertyToSTATSTG(
4097 STATSTG* destination,
4098 StgProperty* source,
4099 int statFlags)
4102 * The copy of the string occurs only when the flag is not set
4104 if ((statFlags & STATFLAG_NONAME) != 0)
4106 destination->pwcsName = 0;
4108 else
4110 destination->pwcsName =
4111 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4113 strcpyW((LPWSTR)destination->pwcsName, source->name);
4116 switch (source->propertyType)
4118 case PROPTYPE_STORAGE:
4119 case PROPTYPE_ROOT:
4120 destination->type = STGTY_STORAGE;
4121 break;
4122 case PROPTYPE_STREAM:
4123 destination->type = STGTY_STREAM;
4124 break;
4125 default:
4126 destination->type = STGTY_STREAM;
4127 break;
4130 destination->cbSize = source->size;
4132 currentReturnStruct->mtime = {0}; TODO
4133 currentReturnStruct->ctime = {0};
4134 currentReturnStruct->atime = {0};
4136 destination->grfMode = 0;
4137 destination->grfLocksSupported = 0;
4138 destination->clsid = source->propertyUniqueID;
4139 destination->grfStateBits = 0;
4140 destination->reserved = 0;
4143 /******************************************************************************
4144 ** BlockChainStream implementation
4147 BlockChainStream* BlockChainStream_Construct(
4148 StorageImpl* parentStorage,
4149 ULONG* headOfStreamPlaceHolder,
4150 ULONG propertyIndex)
4152 BlockChainStream* newStream;
4153 ULONG blockIndex;
4155 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4157 newStream->parentStorage = parentStorage;
4158 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4159 newStream->ownerPropertyIndex = propertyIndex;
4160 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4161 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4162 newStream->numBlocks = 0;
4164 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4166 while (blockIndex != BLOCK_END_OF_CHAIN)
4168 newStream->numBlocks++;
4169 newStream->tailIndex = blockIndex;
4171 if(FAILED(StorageImpl_GetNextBlockInChain(
4172 parentStorage,
4173 blockIndex,
4174 &blockIndex)))
4176 HeapFree(GetProcessHeap(), 0, newStream);
4177 return NULL;
4181 return newStream;
4184 void BlockChainStream_Destroy(BlockChainStream* This)
4186 HeapFree(GetProcessHeap(), 0, This);
4189 /******************************************************************************
4190 * BlockChainStream_GetHeadOfChain
4192 * Returns the head of this stream chain.
4193 * Some special chains don't have properties, their heads are kept in
4194 * This->headOfStreamPlaceHolder.
4197 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4199 StgProperty chainProperty;
4200 BOOL readSuccessful;
4202 if (This->headOfStreamPlaceHolder != 0)
4203 return *(This->headOfStreamPlaceHolder);
4205 if (This->ownerPropertyIndex != PROPERTY_NULL)
4207 readSuccessful = StorageImpl_ReadProperty(
4208 This->parentStorage,
4209 This->ownerPropertyIndex,
4210 &chainProperty);
4212 if (readSuccessful)
4214 return chainProperty.startingBlock;
4218 return BLOCK_END_OF_CHAIN;
4221 /******************************************************************************
4222 * BlockChainStream_GetCount
4224 * Returns the number of blocks that comprises this chain.
4225 * This is not the size of the stream as the last block may not be full!
4228 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4230 ULONG blockIndex;
4231 ULONG count = 0;
4233 blockIndex = BlockChainStream_GetHeadOfChain(This);
4235 while (blockIndex != BLOCK_END_OF_CHAIN)
4237 count++;
4239 if(FAILED(StorageImpl_GetNextBlockInChain(
4240 This->parentStorage,
4241 blockIndex,
4242 &blockIndex)))
4243 return 0;
4246 return count;
4249 /******************************************************************************
4250 * BlockChainStream_ReadAt
4252 * Reads a specified number of bytes from this chain at the specified offset.
4253 * bytesRead may be NULL.
4254 * Failure will be returned if the specified number of bytes has not been read.
4256 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4257 ULARGE_INTEGER offset,
4258 ULONG size,
4259 void* buffer,
4260 ULONG* bytesRead)
4262 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4263 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4264 ULONG bytesToReadInBuffer;
4265 ULONG blockIndex;
4266 BYTE* bufferWalker;
4267 BYTE* bigBlockBuffer;
4270 * Find the first block in the stream that contains part of the buffer.
4272 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4273 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4274 (blockNoInSequence < This->lastBlockNoInSequence) )
4276 blockIndex = BlockChainStream_GetHeadOfChain(This);
4277 This->lastBlockNoInSequence = blockNoInSequence;
4279 else
4281 ULONG temp = blockNoInSequence;
4283 blockIndex = This->lastBlockNoInSequenceIndex;
4284 blockNoInSequence -= This->lastBlockNoInSequence;
4285 This->lastBlockNoInSequence = temp;
4288 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4290 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4291 return FALSE;
4292 blockNoInSequence--;
4295 This->lastBlockNoInSequenceIndex = blockIndex;
4298 * Start reading the buffer.
4300 *bytesRead = 0;
4301 bufferWalker = buffer;
4303 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4306 * Calculate how many bytes we can copy from this big block.
4308 bytesToReadInBuffer =
4309 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4312 * Copy those bytes to the buffer
4314 bigBlockBuffer =
4315 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4317 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4319 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4322 * Step to the next big block.
4324 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4325 return FALSE;
4327 bufferWalker += bytesToReadInBuffer;
4328 size -= bytesToReadInBuffer;
4329 *bytesRead += bytesToReadInBuffer;
4330 offsetInBlock = 0; /* There is no offset on the next block */
4334 return (size == 0);
4337 /******************************************************************************
4338 * BlockChainStream_WriteAt
4340 * Writes the specified number of bytes to this chain at the specified offset.
4341 * bytesWritten may be NULL.
4342 * Will fail if not all specified number of bytes have been written.
4344 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4345 ULARGE_INTEGER offset,
4346 ULONG size,
4347 const void* buffer,
4348 ULONG* bytesWritten)
4350 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4351 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4352 ULONG bytesToWrite;
4353 ULONG blockIndex;
4354 BYTE* bufferWalker;
4355 BYTE* bigBlockBuffer;
4358 * Find the first block in the stream that contains part of the buffer.
4360 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4361 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4362 (blockNoInSequence < This->lastBlockNoInSequence) )
4364 blockIndex = BlockChainStream_GetHeadOfChain(This);
4365 This->lastBlockNoInSequence = blockNoInSequence;
4367 else
4369 ULONG temp = blockNoInSequence;
4371 blockIndex = This->lastBlockNoInSequenceIndex;
4372 blockNoInSequence -= This->lastBlockNoInSequence;
4373 This->lastBlockNoInSequence = temp;
4376 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4378 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4379 &blockIndex)))
4380 return FALSE;
4381 blockNoInSequence--;
4384 This->lastBlockNoInSequenceIndex = blockIndex;
4387 * Here, I'm casting away the constness on the buffer variable
4388 * This is OK since we don't intend to modify that buffer.
4390 *bytesWritten = 0;
4391 bufferWalker = (BYTE*)buffer;
4393 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4396 * Calculate how many bytes we can copy from this big block.
4398 bytesToWrite =
4399 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4402 * Copy those bytes to the buffer
4404 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4406 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4408 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4411 * Step to the next big block.
4413 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4414 &blockIndex)))
4415 return FALSE;
4416 bufferWalker += bytesToWrite;
4417 size -= bytesToWrite;
4418 *bytesWritten += bytesToWrite;
4419 offsetInBlock = 0; /* There is no offset on the next block */
4422 return (size == 0);
4425 /******************************************************************************
4426 * BlockChainStream_Shrink
4428 * Shrinks this chain in the big block depot.
4430 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4431 ULARGE_INTEGER newSize)
4433 ULONG blockIndex, extraBlock;
4434 ULONG numBlocks;
4435 ULONG count = 1;
4438 * Reset the last accessed block cache.
4440 This->lastBlockNoInSequence = 0xFFFFFFFF;
4441 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4444 * Figure out how many blocks are needed to contain the new size
4446 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4448 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4449 numBlocks++;
4451 blockIndex = BlockChainStream_GetHeadOfChain(This);
4454 * Go to the new end of chain
4456 while (count < numBlocks)
4458 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4459 &blockIndex)))
4460 return FALSE;
4461 count++;
4464 /* Get the next block before marking the new end */
4465 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4466 &extraBlock)))
4467 return FALSE;
4469 /* Mark the new end of chain */
4470 StorageImpl_SetNextBlockInChain(
4471 This->parentStorage,
4472 blockIndex,
4473 BLOCK_END_OF_CHAIN);
4475 This->tailIndex = blockIndex;
4476 This->numBlocks = numBlocks;
4479 * Mark the extra blocks as free
4481 while (extraBlock != BLOCK_END_OF_CHAIN)
4483 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4484 &blockIndex)))
4485 return FALSE;
4486 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4487 extraBlock = blockIndex;
4490 return TRUE;
4493 /******************************************************************************
4494 * BlockChainStream_Enlarge
4496 * Grows this chain in the big block depot.
4498 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4499 ULARGE_INTEGER newSize)
4501 ULONG blockIndex, currentBlock;
4502 ULONG newNumBlocks;
4503 ULONG oldNumBlocks = 0;
4505 blockIndex = BlockChainStream_GetHeadOfChain(This);
4508 * Empty chain. Create the head.
4510 if (blockIndex == BLOCK_END_OF_CHAIN)
4512 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4513 StorageImpl_SetNextBlockInChain(This->parentStorage,
4514 blockIndex,
4515 BLOCK_END_OF_CHAIN);
4517 if (This->headOfStreamPlaceHolder != 0)
4519 *(This->headOfStreamPlaceHolder) = blockIndex;
4521 else
4523 StgProperty chainProp;
4524 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4526 StorageImpl_ReadProperty(
4527 This->parentStorage,
4528 This->ownerPropertyIndex,
4529 &chainProp);
4531 chainProp.startingBlock = blockIndex;
4533 StorageImpl_WriteProperty(
4534 This->parentStorage,
4535 This->ownerPropertyIndex,
4536 &chainProp);
4539 This->tailIndex = blockIndex;
4540 This->numBlocks = 1;
4544 * Figure out how many blocks are needed to contain this stream
4546 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4548 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4549 newNumBlocks++;
4552 * Go to the current end of chain
4554 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4556 currentBlock = blockIndex;
4558 while (blockIndex != BLOCK_END_OF_CHAIN)
4560 This->numBlocks++;
4561 currentBlock = blockIndex;
4563 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4564 &blockIndex)))
4565 return FALSE;
4568 This->tailIndex = currentBlock;
4571 currentBlock = This->tailIndex;
4572 oldNumBlocks = This->numBlocks;
4575 * Add new blocks to the chain
4577 if (oldNumBlocks < newNumBlocks)
4579 while (oldNumBlocks < newNumBlocks)
4581 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4583 StorageImpl_SetNextBlockInChain(
4584 This->parentStorage,
4585 currentBlock,
4586 blockIndex);
4588 StorageImpl_SetNextBlockInChain(
4589 This->parentStorage,
4590 blockIndex,
4591 BLOCK_END_OF_CHAIN);
4593 currentBlock = blockIndex;
4594 oldNumBlocks++;
4597 This->tailIndex = blockIndex;
4598 This->numBlocks = newNumBlocks;
4601 return TRUE;
4604 /******************************************************************************
4605 * BlockChainStream_SetSize
4607 * Sets the size of this stream. The big block depot will be updated.
4608 * The file will grow if we grow the chain.
4610 * TODO: Free the actual blocks in the file when we shrink the chain.
4611 * Currently, the blocks are still in the file. So the file size
4612 * doesn't shrink even if we shrink streams.
4614 BOOL BlockChainStream_SetSize(
4615 BlockChainStream* This,
4616 ULARGE_INTEGER newSize)
4618 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4620 if (newSize.s.LowPart == size.s.LowPart)
4621 return TRUE;
4623 if (newSize.s.LowPart < size.s.LowPart)
4625 BlockChainStream_Shrink(This, newSize);
4627 else
4629 ULARGE_INTEGER fileSize =
4630 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4632 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4635 * Make sure the file stays a multiple of blocksize
4637 if ((diff % This->parentStorage->bigBlockSize) != 0)
4638 diff += (This->parentStorage->bigBlockSize -
4639 (diff % This->parentStorage->bigBlockSize) );
4641 fileSize.s.LowPart += diff;
4642 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4644 BlockChainStream_Enlarge(This, newSize);
4647 return TRUE;
4650 /******************************************************************************
4651 * BlockChainStream_GetSize
4653 * Returns the size of this chain.
4654 * Will return the block count if this chain doesn't have a property.
4656 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4658 StgProperty chainProperty;
4660 if(This->headOfStreamPlaceHolder == NULL)
4663 * This chain is a data stream read the property and return
4664 * the appropriate size
4666 StorageImpl_ReadProperty(
4667 This->parentStorage,
4668 This->ownerPropertyIndex,
4669 &chainProperty);
4671 return chainProperty.size;
4673 else
4676 * this chain is a chain that does not have a property, figure out the
4677 * size by making the product number of used blocks times the
4678 * size of them
4680 ULARGE_INTEGER result;
4681 result.s.HighPart = 0;
4683 result.s.LowPart =
4684 BlockChainStream_GetCount(This) *
4685 This->parentStorage->bigBlockSize;
4687 return result;
4691 /******************************************************************************
4692 ** SmallBlockChainStream implementation
4695 SmallBlockChainStream* SmallBlockChainStream_Construct(
4696 StorageImpl* parentStorage,
4697 ULONG propertyIndex)
4699 SmallBlockChainStream* newStream;
4701 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4703 newStream->parentStorage = parentStorage;
4704 newStream->ownerPropertyIndex = propertyIndex;
4706 return newStream;
4709 void SmallBlockChainStream_Destroy(
4710 SmallBlockChainStream* This)
4712 HeapFree(GetProcessHeap(), 0, This);
4715 /******************************************************************************
4716 * SmallBlockChainStream_GetHeadOfChain
4718 * Returns the head of this chain of small blocks.
4720 ULONG SmallBlockChainStream_GetHeadOfChain(
4721 SmallBlockChainStream* This)
4723 StgProperty chainProperty;
4724 BOOL readSuccessful;
4726 if (This->ownerPropertyIndex)
4728 readSuccessful = StorageImpl_ReadProperty(
4729 This->parentStorage,
4730 This->ownerPropertyIndex,
4731 &chainProperty);
4733 if (readSuccessful)
4735 return chainProperty.startingBlock;
4740 return BLOCK_END_OF_CHAIN;
4743 /******************************************************************************
4744 * SmallBlockChainStream_GetNextBlockInChain
4746 * Returns the index of the next small block in this chain.
4748 * Return Values:
4749 * - BLOCK_END_OF_CHAIN: end of this chain
4750 * - BLOCK_UNUSED: small block 'blockIndex' is free
4752 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4753 SmallBlockChainStream* This,
4754 ULONG blockIndex,
4755 ULONG* nextBlockInChain)
4757 ULARGE_INTEGER offsetOfBlockInDepot;
4758 DWORD buffer;
4759 ULONG bytesRead;
4760 BOOL success;
4762 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4764 offsetOfBlockInDepot.s.HighPart = 0;
4765 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4768 * Read those bytes in the buffer from the small block file.
4770 success = BlockChainStream_ReadAt(
4771 This->parentStorage->smallBlockDepotChain,
4772 offsetOfBlockInDepot,
4773 sizeof(DWORD),
4774 &buffer,
4775 &bytesRead);
4777 if (success)
4779 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4780 return S_OK;
4783 return STG_E_READFAULT;
4786 /******************************************************************************
4787 * SmallBlockChainStream_SetNextBlockInChain
4789 * Writes the index of the next block of the specified block in the small
4790 * block depot.
4791 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4792 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4794 void SmallBlockChainStream_SetNextBlockInChain(
4795 SmallBlockChainStream* This,
4796 ULONG blockIndex,
4797 ULONG nextBlock)
4799 ULARGE_INTEGER offsetOfBlockInDepot;
4800 DWORD buffer;
4801 ULONG bytesWritten;
4803 offsetOfBlockInDepot.s.HighPart = 0;
4804 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4806 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4809 * Read those bytes in the buffer from the small block file.
4811 BlockChainStream_WriteAt(
4812 This->parentStorage->smallBlockDepotChain,
4813 offsetOfBlockInDepot,
4814 sizeof(DWORD),
4815 &buffer,
4816 &bytesWritten);
4819 /******************************************************************************
4820 * SmallBlockChainStream_FreeBlock
4822 * Flag small block 'blockIndex' as free in the small block depot.
4824 void SmallBlockChainStream_FreeBlock(
4825 SmallBlockChainStream* This,
4826 ULONG blockIndex)
4828 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4831 /******************************************************************************
4832 * SmallBlockChainStream_GetNextFreeBlock
4834 * Returns the index of a free small block. The small block depot will be
4835 * enlarged if necessary. The small block chain will also be enlarged if
4836 * necessary.
4838 ULONG SmallBlockChainStream_GetNextFreeBlock(
4839 SmallBlockChainStream* This)
4841 ULARGE_INTEGER offsetOfBlockInDepot;
4842 DWORD buffer;
4843 ULONG bytesRead;
4844 ULONG blockIndex = 0;
4845 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4846 BOOL success = TRUE;
4847 ULONG smallBlocksPerBigBlock;
4849 offsetOfBlockInDepot.s.HighPart = 0;
4852 * Scan the small block depot for a free block
4854 while (nextBlockIndex != BLOCK_UNUSED)
4856 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4858 success = BlockChainStream_ReadAt(
4859 This->parentStorage->smallBlockDepotChain,
4860 offsetOfBlockInDepot,
4861 sizeof(DWORD),
4862 &buffer,
4863 &bytesRead);
4866 * If we run out of space for the small block depot, enlarge it
4868 if (success)
4870 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4872 if (nextBlockIndex != BLOCK_UNUSED)
4873 blockIndex++;
4875 else
4877 ULONG count =
4878 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4880 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4881 ULONG nextBlock, newsbdIndex;
4882 BYTE* smallBlockDepot;
4884 nextBlock = sbdIndex;
4885 while (nextBlock != BLOCK_END_OF_CHAIN)
4887 sbdIndex = nextBlock;
4888 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4891 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4892 if (sbdIndex != BLOCK_END_OF_CHAIN)
4893 StorageImpl_SetNextBlockInChain(
4894 This->parentStorage,
4895 sbdIndex,
4896 newsbdIndex);
4898 StorageImpl_SetNextBlockInChain(
4899 This->parentStorage,
4900 newsbdIndex,
4901 BLOCK_END_OF_CHAIN);
4904 * Initialize all the small blocks to free
4906 smallBlockDepot =
4907 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4909 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4910 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4912 if (count == 0)
4915 * We have just created the small block depot.
4917 StgProperty rootProp;
4918 ULONG sbStartIndex;
4921 * Save it in the header
4923 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4924 StorageImpl_SaveFileHeader(This->parentStorage);
4927 * And allocate the first big block that will contain small blocks
4929 sbStartIndex =
4930 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4932 StorageImpl_SetNextBlockInChain(
4933 This->parentStorage,
4934 sbStartIndex,
4935 BLOCK_END_OF_CHAIN);
4937 StorageImpl_ReadProperty(
4938 This->parentStorage,
4939 This->parentStorage->rootPropertySetIndex,
4940 &rootProp);
4942 rootProp.startingBlock = sbStartIndex;
4943 rootProp.size.s.HighPart = 0;
4944 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4946 StorageImpl_WriteProperty(
4947 This->parentStorage,
4948 This->parentStorage->rootPropertySetIndex,
4949 &rootProp);
4954 smallBlocksPerBigBlock =
4955 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4958 * Verify if we have to allocate big blocks to contain small blocks
4960 if (blockIndex % smallBlocksPerBigBlock == 0)
4962 StgProperty rootProp;
4963 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4965 StorageImpl_ReadProperty(
4966 This->parentStorage,
4967 This->parentStorage->rootPropertySetIndex,
4968 &rootProp);
4970 if (rootProp.size.s.LowPart <
4971 (blocksRequired * This->parentStorage->bigBlockSize))
4973 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4975 BlockChainStream_SetSize(
4976 This->parentStorage->smallBlockRootChain,
4977 rootProp.size);
4979 StorageImpl_WriteProperty(
4980 This->parentStorage,
4981 This->parentStorage->rootPropertySetIndex,
4982 &rootProp);
4986 return blockIndex;
4989 /******************************************************************************
4990 * SmallBlockChainStream_ReadAt
4992 * Reads a specified number of bytes from this chain at the specified offset.
4993 * bytesRead may be NULL.
4994 * Failure will be returned if the specified number of bytes has not been read.
4996 BOOL SmallBlockChainStream_ReadAt(
4997 SmallBlockChainStream* This,
4998 ULARGE_INTEGER offset,
4999 ULONG size,
5000 void* buffer,
5001 ULONG* bytesRead)
5003 ULARGE_INTEGER offsetInBigBlockFile;
5004 ULONG blockNoInSequence =
5005 offset.s.LowPart / This->parentStorage->smallBlockSize;
5007 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5008 ULONG bytesToReadInBuffer;
5009 ULONG blockIndex;
5010 ULONG bytesReadFromBigBlockFile;
5011 BYTE* bufferWalker;
5014 * This should never happen on a small block file.
5016 assert(offset.s.HighPart==0);
5019 * Find the first block in the stream that contains part of the buffer.
5021 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5023 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5025 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5026 &blockIndex)))
5027 return FALSE;
5028 blockNoInSequence--;
5032 * Start reading the buffer.
5034 *bytesRead = 0;
5035 bufferWalker = buffer;
5037 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5040 * Calculate how many bytes we can copy from this small block.
5042 bytesToReadInBuffer =
5043 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5046 * Calculate the offset of the small block in the small block file.
5048 offsetInBigBlockFile.s.HighPart = 0;
5049 offsetInBigBlockFile.s.LowPart =
5050 blockIndex * This->parentStorage->smallBlockSize;
5052 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5055 * Read those bytes in the buffer from the small block file.
5057 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5058 offsetInBigBlockFile,
5059 bytesToReadInBuffer,
5060 bufferWalker,
5061 &bytesReadFromBigBlockFile);
5063 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5066 * Step to the next big block.
5068 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5069 return FALSE;
5070 bufferWalker += bytesToReadInBuffer;
5071 size -= bytesToReadInBuffer;
5072 *bytesRead += bytesToReadInBuffer;
5073 offsetInBlock = 0; /* There is no offset on the next block */
5076 return (size == 0);
5079 /******************************************************************************
5080 * SmallBlockChainStream_WriteAt
5082 * Writes the specified number of bytes to this chain at the specified offset.
5083 * bytesWritten may be NULL.
5084 * Will fail if not all specified number of bytes have been written.
5086 BOOL SmallBlockChainStream_WriteAt(
5087 SmallBlockChainStream* This,
5088 ULARGE_INTEGER offset,
5089 ULONG size,
5090 const void* buffer,
5091 ULONG* bytesWritten)
5093 ULARGE_INTEGER offsetInBigBlockFile;
5094 ULONG blockNoInSequence =
5095 offset.s.LowPart / This->parentStorage->smallBlockSize;
5097 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5098 ULONG bytesToWriteInBuffer;
5099 ULONG blockIndex;
5100 ULONG bytesWrittenFromBigBlockFile;
5101 BYTE* bufferWalker;
5104 * This should never happen on a small block file.
5106 assert(offset.s.HighPart==0);
5109 * Find the first block in the stream that contains part of the buffer.
5111 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5113 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5115 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5116 return FALSE;
5117 blockNoInSequence--;
5121 * Start writing the buffer.
5123 * Here, I'm casting away the constness on the buffer variable
5124 * This is OK since we don't intend to modify that buffer.
5126 *bytesWritten = 0;
5127 bufferWalker = (BYTE*)buffer;
5128 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5131 * Calculate how many bytes we can copy to this small block.
5133 bytesToWriteInBuffer =
5134 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5137 * Calculate the offset of the small block in the small block file.
5139 offsetInBigBlockFile.s.HighPart = 0;
5140 offsetInBigBlockFile.s.LowPart =
5141 blockIndex * This->parentStorage->smallBlockSize;
5143 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5146 * Write those bytes in the buffer to the small block file.
5148 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5149 offsetInBigBlockFile,
5150 bytesToWriteInBuffer,
5151 bufferWalker,
5152 &bytesWrittenFromBigBlockFile);
5154 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5157 * Step to the next big block.
5159 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5160 &blockIndex)))
5161 return FALSE;
5162 bufferWalker += bytesToWriteInBuffer;
5163 size -= bytesToWriteInBuffer;
5164 *bytesWritten += bytesToWriteInBuffer;
5165 offsetInBlock = 0; /* There is no offset on the next block */
5168 return (size == 0);
5171 /******************************************************************************
5172 * SmallBlockChainStream_Shrink
5174 * Shrinks this chain in the small block depot.
5176 BOOL SmallBlockChainStream_Shrink(
5177 SmallBlockChainStream* This,
5178 ULARGE_INTEGER newSize)
5180 ULONG blockIndex, extraBlock;
5181 ULONG numBlocks;
5182 ULONG count = 0;
5184 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5186 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5187 numBlocks++;
5189 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5192 * Go to the new end of chain
5194 while (count < numBlocks)
5196 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5197 &blockIndex)))
5198 return FALSE;
5199 count++;
5203 * If the count is 0, we have a special case, the head of the chain was
5204 * just freed.
5206 if (count == 0)
5208 StgProperty chainProp;
5210 StorageImpl_ReadProperty(This->parentStorage,
5211 This->ownerPropertyIndex,
5212 &chainProp);
5214 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5216 StorageImpl_WriteProperty(This->parentStorage,
5217 This->ownerPropertyIndex,
5218 &chainProp);
5221 * We start freeing the chain at the head block.
5223 extraBlock = blockIndex;
5225 else
5227 /* Get the next block before marking the new end */
5228 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5229 &extraBlock)))
5230 return FALSE;
5232 /* Mark the new end of chain */
5233 SmallBlockChainStream_SetNextBlockInChain(
5234 This,
5235 blockIndex,
5236 BLOCK_END_OF_CHAIN);
5240 * Mark the extra blocks as free
5242 while (extraBlock != BLOCK_END_OF_CHAIN)
5244 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5245 &blockIndex)))
5246 return FALSE;
5247 SmallBlockChainStream_FreeBlock(This, extraBlock);
5248 extraBlock = blockIndex;
5251 return TRUE;
5254 /******************************************************************************
5255 * SmallBlockChainStream_Enlarge
5257 * Grows this chain in the small block depot.
5259 BOOL SmallBlockChainStream_Enlarge(
5260 SmallBlockChainStream* This,
5261 ULARGE_INTEGER newSize)
5263 ULONG blockIndex, currentBlock;
5264 ULONG newNumBlocks;
5265 ULONG oldNumBlocks = 0;
5267 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5270 * Empty chain
5272 if (blockIndex == BLOCK_END_OF_CHAIN)
5275 StgProperty chainProp;
5277 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5278 &chainProp);
5280 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5282 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5283 &chainProp);
5285 blockIndex = chainProp.startingBlock;
5286 SmallBlockChainStream_SetNextBlockInChain(
5287 This,
5288 blockIndex,
5289 BLOCK_END_OF_CHAIN);
5292 currentBlock = blockIndex;
5295 * Figure out how many blocks are needed to contain this stream
5297 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5299 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5300 newNumBlocks++;
5303 * Go to the current end of chain
5305 while (blockIndex != BLOCK_END_OF_CHAIN)
5307 oldNumBlocks++;
5308 currentBlock = blockIndex;
5309 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5310 return FALSE;
5314 * Add new blocks to the chain
5316 while (oldNumBlocks < newNumBlocks)
5318 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5319 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5321 SmallBlockChainStream_SetNextBlockInChain(
5322 This,
5323 blockIndex,
5324 BLOCK_END_OF_CHAIN);
5326 currentBlock = blockIndex;
5327 oldNumBlocks++;
5330 return TRUE;
5333 /******************************************************************************
5334 * SmallBlockChainStream_GetCount
5336 * Returns the number of blocks that comprises this chain.
5337 * This is not the size of this chain as the last block may not be full!
5339 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5341 ULONG blockIndex;
5342 ULONG count = 0;
5344 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5346 while (blockIndex != BLOCK_END_OF_CHAIN)
5348 count++;
5350 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5351 return 0;
5354 return count;
5357 /******************************************************************************
5358 * SmallBlockChainStream_SetSize
5360 * Sets the size of this stream.
5361 * The file will grow if we grow the chain.
5363 * TODO: Free the actual blocks in the file when we shrink the chain.
5364 * Currently, the blocks are still in the file. So the file size
5365 * doesn't shrink even if we shrink streams.
5367 BOOL SmallBlockChainStream_SetSize(
5368 SmallBlockChainStream* This,
5369 ULARGE_INTEGER newSize)
5371 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5373 if (newSize.s.LowPart == size.s.LowPart)
5374 return TRUE;
5376 if (newSize.s.LowPart < size.s.LowPart)
5378 SmallBlockChainStream_Shrink(This, newSize);
5380 else
5382 SmallBlockChainStream_Enlarge(This, newSize);
5385 return TRUE;
5388 /******************************************************************************
5389 * SmallBlockChainStream_GetSize
5391 * Returns the size of this chain.
5393 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5395 StgProperty chainProperty;
5397 StorageImpl_ReadProperty(
5398 This->parentStorage,
5399 This->ownerPropertyIndex,
5400 &chainProperty);
5402 return chainProperty.size;
5405 /******************************************************************************
5406 * StgCreateDocfile [OLE32.144]
5408 HRESULT WINAPI StgCreateDocfile(
5409 LPCOLESTR pwcsName,
5410 DWORD grfMode,
5411 DWORD reserved,
5412 IStorage **ppstgOpen)
5414 StorageImpl* newStorage = 0;
5415 HANDLE hFile = INVALID_HANDLE_VALUE;
5416 HRESULT hr = S_OK;
5417 DWORD shareMode;
5418 DWORD accessMode;
5419 DWORD creationMode;
5420 DWORD fileAttributes;
5421 WCHAR tempFileName[MAX_PATH];
5423 TRACE("(%s, %lx, %ld, %p)\n",
5424 debugstr_w(pwcsName), grfMode,
5425 reserved, ppstgOpen);
5428 * Validate the parameters
5430 if (ppstgOpen == 0)
5431 return STG_E_INVALIDPOINTER;
5434 * Validate the STGM flags
5436 if ( FAILED( validateSTGM(grfMode) ))
5437 return STG_E_INVALIDFLAG;
5440 * Generate a unique name.
5442 if (pwcsName == 0)
5444 WCHAR tempPath[MAX_PATH];
5445 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5447 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5448 return STG_E_INVALIDFLAG;
5449 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5450 return STG_E_INVALIDFLAG;
5452 memset(tempPath, 0, sizeof(tempPath));
5453 memset(tempFileName, 0, sizeof(tempFileName));
5455 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5456 tempPath[0] = '.';
5458 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5459 pwcsName = tempFileName;
5460 else
5461 return STG_E_INSUFFICIENTMEMORY;
5463 creationMode = TRUNCATE_EXISTING;
5465 else
5467 creationMode = GetCreationModeFromSTGM(grfMode);
5471 * Interpret the STGM value grfMode
5473 shareMode = GetShareModeFromSTGM(grfMode);
5474 accessMode = GetAccessModeFromSTGM(grfMode);
5476 if (grfMode & STGM_DELETEONRELEASE)
5477 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5478 else
5479 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5481 if (grfMode & STGM_TRANSACTED)
5482 FIXME("Transacted mode not implemented.\n");
5485 * Initialize the "out" parameter.
5487 *ppstgOpen = 0;
5489 hFile = CreateFileW(pwcsName,
5490 accessMode,
5491 shareMode,
5492 NULL,
5493 creationMode,
5494 fileAttributes,
5497 if (hFile == INVALID_HANDLE_VALUE)
5499 return E_FAIL;
5503 * Allocate and initialize the new IStorage32object.
5505 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5507 if (newStorage == 0)
5508 return STG_E_INSUFFICIENTMEMORY;
5510 hr = StorageImpl_Construct(
5511 newStorage,
5512 hFile,
5513 pwcsName,
5514 NULL,
5515 grfMode,
5516 TRUE,
5517 TRUE);
5519 if (FAILED(hr))
5521 HeapFree(GetProcessHeap(), 0, newStorage);
5522 return hr;
5526 * Get an "out" pointer for the caller.
5528 hr = StorageBaseImpl_QueryInterface(
5529 (IStorage*)newStorage,
5530 (REFIID)&IID_IStorage,
5531 (void**)ppstgOpen);
5533 return hr;
5536 /******************************************************************************
5537 * StgOpenStorage [OLE32.148]
5539 HRESULT WINAPI StgOpenStorage(
5540 const OLECHAR *pwcsName,
5541 IStorage *pstgPriority,
5542 DWORD grfMode,
5543 SNB snbExclude,
5544 DWORD reserved,
5545 IStorage **ppstgOpen)
5547 StorageImpl* newStorage = 0;
5548 HRESULT hr = S_OK;
5549 HANDLE hFile = 0;
5550 DWORD shareMode;
5551 DWORD accessMode;
5552 WCHAR fullname[MAX_PATH];
5553 DWORD length;
5555 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5556 debugstr_w(pwcsName), pstgPriority, grfMode,
5557 snbExclude, reserved, ppstgOpen);
5560 * Perform a sanity check
5562 if (( pwcsName == 0) || (ppstgOpen == 0) )
5564 hr = STG_E_INVALIDPOINTER;
5565 goto end;
5569 * Validate the STGM flags
5571 if ( FAILED( validateSTGM(grfMode) ))
5573 hr = STG_E_INVALIDFLAG;
5574 goto end;
5578 * Interpret the STGM value grfMode
5580 shareMode = GetShareModeFromSTGM(grfMode);
5581 accessMode = GetAccessModeFromSTGM(grfMode);
5584 * Initialize the "out" parameter.
5586 *ppstgOpen = 0;
5588 hFile = CreateFileW( pwcsName,
5589 accessMode,
5590 shareMode,
5591 NULL,
5592 OPEN_EXISTING,
5593 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5596 length = GetFileSize(hFile, NULL);
5598 if (hFile==INVALID_HANDLE_VALUE)
5600 DWORD last_error = GetLastError();
5602 hr = E_FAIL;
5604 switch (last_error)
5606 case ERROR_FILE_NOT_FOUND:
5607 hr = STG_E_FILENOTFOUND;
5608 break;
5610 case ERROR_PATH_NOT_FOUND:
5611 hr = STG_E_PATHNOTFOUND;
5612 break;
5614 case ERROR_ACCESS_DENIED:
5615 case ERROR_WRITE_PROTECT:
5616 hr = STG_E_ACCESSDENIED;
5617 break;
5619 case ERROR_SHARING_VIOLATION:
5620 hr = STG_E_SHAREVIOLATION;
5621 break;
5623 default:
5624 hr = E_FAIL;
5627 goto end;
5631 * Allocate and initialize the new IStorage32object.
5633 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5635 if (newStorage == 0)
5637 hr = STG_E_INSUFFICIENTMEMORY;
5638 goto end;
5641 /* if the file's length was zero, initialize the storage */
5642 hr = StorageImpl_Construct(
5643 newStorage,
5644 hFile,
5645 pwcsName,
5646 NULL,
5647 grfMode,
5648 TRUE,
5649 !length );
5651 if (FAILED(hr))
5653 HeapFree(GetProcessHeap(), 0, newStorage);
5655 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5657 if(hr == STG_E_INVALIDHEADER)
5658 hr = STG_E_FILEALREADYEXISTS;
5659 goto end;
5662 /* prepare the file name string given in lieu of the root property name */
5663 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5664 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5665 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5668 * Get an "out" pointer for the caller.
5670 hr = StorageBaseImpl_QueryInterface(
5671 (IStorage*)newStorage,
5672 (REFIID)&IID_IStorage,
5673 (void**)ppstgOpen);
5675 end:
5676 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5677 return hr;
5680 /******************************************************************************
5681 * StgCreateDocfileOnILockBytes [OLE32.145]
5683 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5684 ILockBytes *plkbyt,
5685 DWORD grfMode,
5686 DWORD reserved,
5687 IStorage** ppstgOpen)
5689 StorageImpl* newStorage = 0;
5690 HRESULT hr = S_OK;
5693 * Validate the parameters
5695 if ((ppstgOpen == 0) || (plkbyt == 0))
5696 return STG_E_INVALIDPOINTER;
5699 * Allocate and initialize the new IStorage object.
5701 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5703 if (newStorage == 0)
5704 return STG_E_INSUFFICIENTMEMORY;
5706 hr = StorageImpl_Construct(
5707 newStorage,
5710 plkbyt,
5711 grfMode,
5712 FALSE,
5713 TRUE);
5715 if (FAILED(hr))
5717 HeapFree(GetProcessHeap(), 0, newStorage);
5718 return hr;
5722 * Get an "out" pointer for the caller.
5724 hr = StorageBaseImpl_QueryInterface(
5725 (IStorage*)newStorage,
5726 (REFIID)&IID_IStorage,
5727 (void**)ppstgOpen);
5729 return hr;
5732 /******************************************************************************
5733 * StgOpenStorageOnILockBytes [OLE32.149]
5735 HRESULT WINAPI StgOpenStorageOnILockBytes(
5736 ILockBytes *plkbyt,
5737 IStorage *pstgPriority,
5738 DWORD grfMode,
5739 SNB snbExclude,
5740 DWORD reserved,
5741 IStorage **ppstgOpen)
5743 StorageImpl* newStorage = 0;
5744 HRESULT hr = S_OK;
5747 * Perform a sanity check
5749 if ((plkbyt == 0) || (ppstgOpen == 0))
5750 return STG_E_INVALIDPOINTER;
5753 * Validate the STGM flags
5755 if ( FAILED( validateSTGM(grfMode) ))
5756 return STG_E_INVALIDFLAG;
5759 * Initialize the "out" parameter.
5761 *ppstgOpen = 0;
5764 * Allocate and initialize the new IStorage object.
5766 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5768 if (newStorage == 0)
5769 return STG_E_INSUFFICIENTMEMORY;
5771 hr = StorageImpl_Construct(
5772 newStorage,
5775 plkbyt,
5776 grfMode,
5777 FALSE,
5778 FALSE);
5780 if (FAILED(hr))
5782 HeapFree(GetProcessHeap(), 0, newStorage);
5783 return hr;
5787 * Get an "out" pointer for the caller.
5789 hr = StorageBaseImpl_QueryInterface(
5790 (IStorage*)newStorage,
5791 (REFIID)&IID_IStorage,
5792 (void**)ppstgOpen);
5794 return hr;
5797 /******************************************************************************
5798 * StgSetTimes [ole32.150]
5799 * StgSetTimes [OLE32.150]
5803 HRESULT WINAPI StgSetTimes(OLECHAR *str, FILETIME *a, FILETIME *b, FILETIME *c )
5805 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5806 return S_OK;
5809 /******************************************************************************
5810 * StgIsStorageILockBytes [OLE32.147]
5812 * Determines if the ILockBytes contains a storage object.
5814 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5816 BYTE sig[8];
5817 ULARGE_INTEGER offset;
5819 offset.s.HighPart = 0;
5820 offset.s.LowPart = 0;
5822 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5824 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5825 return S_OK;
5827 return S_FALSE;
5830 /******************************************************************************
5831 * WriteClassStg [OLE32.158]
5833 * This method will store the specified CLSID in the specified storage object
5835 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5837 HRESULT hRes;
5839 assert(pStg != 0);
5841 hRes = IStorage_SetClass(pStg, rclsid);
5843 return hRes;
5846 /***********************************************************************
5847 * ReadClassStg (OLE32.134)
5849 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5851 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5853 STATSTG pstatstg;
5854 HRESULT hRes;
5856 TRACE("()\n");
5858 if(pclsid==NULL)
5859 return E_POINTER;
5861 * read a STATSTG structure (contains the clsid) from the storage
5863 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5865 if(SUCCEEDED(hRes))
5866 *pclsid=pstatstg.clsid;
5868 return hRes;
5871 /***********************************************************************
5872 * OleLoadFromStream (OLE32.113)
5874 * This function loads an object from stream
5876 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5878 CLSID clsid;
5879 HRESULT res;
5880 LPPERSISTSTREAM xstm;
5882 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5884 res=ReadClassStm(pStm,&clsid);
5885 if (!SUCCEEDED(res))
5886 return res;
5887 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5888 if (!SUCCEEDED(res))
5889 return res;
5890 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5891 if (!SUCCEEDED(res)) {
5892 IUnknown_Release((IUnknown*)*ppvObj);
5893 return res;
5895 res=IPersistStream_Load(xstm,pStm);
5896 IPersistStream_Release(xstm);
5897 /* FIXME: all refcounts ok at this point? I think they should be:
5898 * pStm : unchanged
5899 * ppvObj : 1
5900 * xstm : 0 (released)
5902 return res;
5905 /***********************************************************************
5906 * OleSaveToStream (OLE32.125)
5908 * This function saves an object with the IPersistStream interface on it
5909 * to the specified stream.
5911 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5914 CLSID clsid;
5915 HRESULT res;
5917 TRACE("(%p,%p)\n",pPStm,pStm);
5919 res=IPersistStream_GetClassID(pPStm,&clsid);
5921 if (SUCCEEDED(res)){
5923 res=WriteClassStm(pStm,&clsid);
5925 if (SUCCEEDED(res))
5927 res=IPersistStream_Save(pPStm,pStm,TRUE);
5930 TRACE("Finished Save\n");
5931 return res;
5934 /****************************************************************************
5935 * This method validate a STGM parameter that can contain the values below
5937 * STGM_DIRECT 0x00000000
5938 * STGM_TRANSACTED 0x00010000
5939 * STGM_SIMPLE 0x08000000
5941 * STGM_READ 0x00000000
5942 * STGM_WRITE 0x00000001
5943 * STGM_READWRITE 0x00000002
5945 * STGM_SHARE_DENY_NONE 0x00000040
5946 * STGM_SHARE_DENY_READ 0x00000030
5947 * STGM_SHARE_DENY_WRITE 0x00000020
5948 * STGM_SHARE_EXCLUSIVE 0x00000010
5950 * STGM_PRIORITY 0x00040000
5951 * STGM_DELETEONRELEASE 0x04000000
5953 * STGM_CREATE 0x00001000
5954 * STGM_CONVERT 0x00020000
5955 * STGM_FAILIFTHERE 0x00000000
5957 * STGM_NOSCRATCH 0x00100000
5958 * STGM_NOSNAPSHOT 0x00200000
5960 static HRESULT validateSTGM(DWORD stgm)
5962 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5963 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5964 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5966 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5967 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5968 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5970 BOOL bSTGM_SHARE_DENY_NONE =
5971 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5973 BOOL bSTGM_SHARE_DENY_READ =
5974 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5976 BOOL bSTGM_SHARE_DENY_WRITE =
5977 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5979 BOOL bSTGM_SHARE_EXCLUSIVE =
5980 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5982 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5983 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5985 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5986 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5989 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5991 if ( ! bSTGM_DIRECT )
5992 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5993 return E_FAIL;
5996 * STGM_WRITE | STGM_READWRITE | STGM_READ
5998 if ( ! bSTGM_READ )
5999 if( bSTGM_WRITE && bSTGM_READWRITE )
6000 return E_FAIL;
6003 * STGM_SHARE_DENY_NONE | others
6004 * (I assume here that DENY_READ implies DENY_WRITE)
6006 if ( bSTGM_SHARE_DENY_NONE )
6007 if ( bSTGM_SHARE_DENY_READ ||
6008 bSTGM_SHARE_DENY_WRITE ||
6009 bSTGM_SHARE_EXCLUSIVE)
6010 return E_FAIL;
6013 * STGM_CREATE | STGM_CONVERT
6014 * if both are false, STGM_FAILIFTHERE is set to TRUE
6016 if ( bSTGM_CREATE && bSTGM_CONVERT )
6017 return E_FAIL;
6020 * STGM_NOSCRATCH requires STGM_TRANSACTED
6022 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6023 return E_FAIL;
6026 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6027 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6029 if (bSTGM_NOSNAPSHOT)
6031 if ( ! ( bSTGM_TRANSACTED &&
6032 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6033 return E_FAIL;
6036 return S_OK;
6039 /****************************************************************************
6040 * GetShareModeFromSTGM
6042 * This method will return a share mode flag from a STGM value.
6043 * The STGM value is assumed valid.
6045 static DWORD GetShareModeFromSTGM(DWORD stgm)
6047 DWORD dwShareMode = 0;
6048 BOOL bSTGM_SHARE_DENY_NONE =
6049 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6051 BOOL bSTGM_SHARE_DENY_READ =
6052 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6054 BOOL bSTGM_SHARE_DENY_WRITE =
6055 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6057 BOOL bSTGM_SHARE_EXCLUSIVE =
6058 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6060 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6061 dwShareMode = 0;
6063 if (bSTGM_SHARE_DENY_NONE)
6064 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6066 if (bSTGM_SHARE_DENY_WRITE)
6067 dwShareMode = FILE_SHARE_READ;
6069 return dwShareMode;
6072 /****************************************************************************
6073 * GetAccessModeFromSTGM
6075 * This method will return an access mode flag from a STGM value.
6076 * The STGM value is assumed valid.
6078 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6080 DWORD dwDesiredAccess = GENERIC_READ;
6081 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6082 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6083 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6085 if (bSTGM_READ)
6086 dwDesiredAccess = GENERIC_READ;
6088 if (bSTGM_WRITE)
6089 dwDesiredAccess |= GENERIC_WRITE;
6091 if (bSTGM_READWRITE)
6092 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6094 return dwDesiredAccess;
6097 /****************************************************************************
6098 * GetCreationModeFromSTGM
6100 * This method will return a creation mode flag from a STGM value.
6101 * The STGM value is assumed valid.
6103 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6105 if ( stgm & STGM_CREATE)
6106 return CREATE_ALWAYS;
6107 if (stgm & STGM_CONVERT) {
6108 FIXME("STGM_CONVERT not implemented!\n");
6109 return CREATE_NEW;
6111 /* All other cases */
6112 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6113 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6114 return CREATE_NEW;
6118 /*************************************************************************
6119 * OLECONVERT_LoadOLE10 [Internal]
6121 * Loads the OLE10 STREAM to memory
6123 * PARAMS
6124 * pOleStream [I] The OLESTREAM
6125 * pData [I] Data Structure for the OLESTREAM Data
6127 * RETURNS
6128 * Success: S_OK
6129 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6130 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6132 * NOTES
6133 * This function is used by OleConvertOLESTREAMToIStorage only.
6135 * Memory allocated for pData must be freed by the caller
6137 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6139 DWORD dwSize;
6140 HRESULT hRes = S_OK;
6141 int nTryCnt=0;
6142 int max_try = 6;
6144 pData->pData = NULL;
6145 pData->pstrOleObjFileName = (CHAR *) NULL;
6147 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6149 /* Get the OleID */
6150 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6151 if(dwSize != sizeof(pData->dwOleID))
6153 hRes = CONVERT10_E_OLESTREAM_GET;
6155 else if(pData->dwOleID != OLESTREAM_ID)
6157 hRes = CONVERT10_E_OLESTREAM_FMT;
6159 else
6161 hRes = S_OK;
6162 break;
6166 if(hRes == S_OK)
6168 /* Get the TypeID...more info needed for this field */
6169 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6170 if(dwSize != sizeof(pData->dwTypeID))
6172 hRes = CONVERT10_E_OLESTREAM_GET;
6175 if(hRes == S_OK)
6177 if(pData->dwTypeID != 0)
6179 /* Get the lenght of the OleTypeName */
6180 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6181 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6183 hRes = CONVERT10_E_OLESTREAM_GET;
6186 if(hRes == S_OK)
6188 if(pData->dwOleTypeNameLength > 0)
6190 /* Get the OleTypeName */
6191 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6192 if(dwSize != pData->dwOleTypeNameLength)
6194 hRes = CONVERT10_E_OLESTREAM_GET;
6198 if(bStrem1)
6200 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6201 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6203 hRes = CONVERT10_E_OLESTREAM_GET;
6205 if(hRes == S_OK)
6207 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6208 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6209 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6210 if(pData->pstrOleObjFileName)
6212 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6213 if(dwSize != pData->dwOleObjFileNameLength)
6215 hRes = CONVERT10_E_OLESTREAM_GET;
6218 else
6219 hRes = CONVERT10_E_OLESTREAM_GET;
6222 else
6224 /* Get the Width of the Metafile */
6225 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6226 if(dwSize != sizeof(pData->dwMetaFileWidth))
6228 hRes = CONVERT10_E_OLESTREAM_GET;
6230 if(hRes == S_OK)
6232 /* Get the Height of the Metafile */
6233 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6234 if(dwSize != sizeof(pData->dwMetaFileHeight))
6236 hRes = CONVERT10_E_OLESTREAM_GET;
6240 if(hRes == S_OK)
6242 /* Get the Lenght of the Data */
6243 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6244 if(dwSize != sizeof(pData->dwDataLength))
6246 hRes = CONVERT10_E_OLESTREAM_GET;
6250 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6252 if(!bStrem1) /* if it is a second OLE stream data */
6254 pData->dwDataLength -= 8;
6255 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6256 if(dwSize != sizeof(pData->strUnknown))
6258 hRes = CONVERT10_E_OLESTREAM_GET;
6262 if(hRes == S_OK)
6264 if(pData->dwDataLength > 0)
6266 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6268 /* Get Data (ex. IStorage, Metafile, or BMP) */
6269 if(pData->pData)
6271 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6272 if(dwSize != pData->dwDataLength)
6274 hRes = CONVERT10_E_OLESTREAM_GET;
6277 else
6279 hRes = CONVERT10_E_OLESTREAM_GET;
6285 return hRes;
6288 /*************************************************************************
6289 * OLECONVERT_SaveOLE10 [Internal]
6291 * Saves the OLE10 STREAM From memory
6293 * PARAMS
6294 * pData [I] Data Structure for the OLESTREAM Data
6295 * pOleStream [I] The OLESTREAM to save
6297 * RETURNS
6298 * Success: S_OK
6299 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6301 * NOTES
6302 * This function is used by OleConvertIStorageToOLESTREAM only.
6305 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6307 DWORD dwSize;
6308 HRESULT hRes = S_OK;
6311 /* Set the OleID */
6312 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6313 if(dwSize != sizeof(pData->dwOleID))
6315 hRes = CONVERT10_E_OLESTREAM_PUT;
6318 if(hRes == S_OK)
6320 /* Set the TypeID */
6321 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6322 if(dwSize != sizeof(pData->dwTypeID))
6324 hRes = CONVERT10_E_OLESTREAM_PUT;
6328 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6330 /* Set the Lenght of the OleTypeName */
6331 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6332 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6334 hRes = CONVERT10_E_OLESTREAM_PUT;
6337 if(hRes == S_OK)
6339 if(pData->dwOleTypeNameLength > 0)
6341 /* Set the OleTypeName */
6342 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6343 if(dwSize != pData->dwOleTypeNameLength)
6345 hRes = CONVERT10_E_OLESTREAM_PUT;
6350 if(hRes == S_OK)
6352 /* Set the width of the Metafile */
6353 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6354 if(dwSize != sizeof(pData->dwMetaFileWidth))
6356 hRes = CONVERT10_E_OLESTREAM_PUT;
6360 if(hRes == S_OK)
6362 /* Set the height of the Metafile */
6363 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6364 if(dwSize != sizeof(pData->dwMetaFileHeight))
6366 hRes = CONVERT10_E_OLESTREAM_PUT;
6370 if(hRes == S_OK)
6372 /* Set the lenght of the Data */
6373 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6374 if(dwSize != sizeof(pData->dwDataLength))
6376 hRes = CONVERT10_E_OLESTREAM_PUT;
6380 if(hRes == S_OK)
6382 if(pData->dwDataLength > 0)
6384 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6385 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6386 if(dwSize != pData->dwDataLength)
6388 hRes = CONVERT10_E_OLESTREAM_PUT;
6393 return hRes;
6396 /*************************************************************************
6397 * OLECONVERT_GetOLE20FromOLE10[Internal]
6399 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6400 * opens it, and copies the content to the dest IStorage for
6401 * OleConvertOLESTREAMToIStorage
6404 * PARAMS
6405 * pDestStorage [I] The IStorage to copy the data to
6406 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6407 * nBufferLength [I] The size of the buffer
6409 * RETURNS
6410 * Nothing
6412 * NOTES
6416 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6418 HRESULT hRes;
6419 HANDLE hFile;
6420 IStorage *pTempStorage;
6421 DWORD dwNumOfBytesWritten;
6422 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6423 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6425 /* Create a temp File */
6426 GetTempPathW(MAX_PATH, wstrTempDir);
6427 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6428 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6430 if(hFile != INVALID_HANDLE_VALUE)
6432 /* Write IStorage Data to File */
6433 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6434 CloseHandle(hFile);
6436 /* Open and copy temp storage to the Dest Storage */
6437 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6438 if(hRes == S_OK)
6440 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6441 StorageBaseImpl_Release(pTempStorage);
6443 DeleteFileW(wstrTempFile);
6448 /*************************************************************************
6449 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6451 * Saves the OLE10 STREAM From memory
6453 * PARAMS
6454 * pStorage [I] The Src IStorage to copy
6455 * pData [I] The Dest Memory to write to.
6457 * RETURNS
6458 * The size in bytes allocated for pData
6460 * NOTES
6461 * Memory allocated for pData must be freed by the caller
6463 * Used by OleConvertIStorageToOLESTREAM only.
6466 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6468 HANDLE hFile;
6469 HRESULT hRes;
6470 DWORD nDataLength = 0;
6471 IStorage *pTempStorage;
6472 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6473 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6475 *pData = NULL;
6477 /* Create temp Storage */
6478 GetTempPathW(MAX_PATH, wstrTempDir);
6479 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6480 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6482 if(hRes == S_OK)
6484 /* Copy Src Storage to the Temp Storage */
6485 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6486 StorageBaseImpl_Release(pTempStorage);
6488 /* Open Temp Storage as a file and copy to memory */
6489 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6490 if(hFile != INVALID_HANDLE_VALUE)
6492 nDataLength = GetFileSize(hFile, NULL);
6493 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6494 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6495 CloseHandle(hFile);
6497 DeleteFileW(wstrTempFile);
6499 return nDataLength;
6502 /*************************************************************************
6503 * OLECONVERT_CreateOleStream [Internal]
6505 * Creates the "\001OLE" stream in the IStorage if neccessary.
6507 * PARAMS
6508 * pStorage [I] Dest storage to create the stream in
6510 * RETURNS
6511 * Nothing
6513 * NOTES
6514 * This function is used by OleConvertOLESTREAMToIStorage only.
6516 * This stream is still unknown, MS Word seems to have extra data
6517 * but since the data is stored in the OLESTREAM there should be
6518 * no need to recreate the stream. If the stream is manually
6519 * deleted it will create it with this default data.
6522 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6524 HRESULT hRes;
6525 IStream *pStream;
6526 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6527 BYTE pOleStreamHeader [] =
6529 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6530 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6531 0x00, 0x00, 0x00, 0x00
6534 /* Create stream if not present */
6535 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6536 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6538 if(hRes == S_OK)
6540 /* Write default Data */
6541 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6542 IStream_Release(pStream);
6547 /*************************************************************************
6548 * OLECONVERT_CreateCompObjStream [Internal]
6550 * Creates a "\001CompObj" is the destination IStorage if necessary.
6552 * PARAMS
6553 * pStorage [I] The dest IStorage to create the CompObj Stream
6554 * if necessary.
6555 * strOleTypeName [I] The ProgID
6557 * RETURNS
6558 * Success: S_OK
6559 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6561 * NOTES
6562 * This function is used by OleConvertOLESTREAMToIStorage only.
6564 * The stream data is stored in the OLESTREAM and there should be
6565 * no need to recreate the stream. If the stream is manually
6566 * deleted it will attempt to create it by querying the registry.
6570 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6572 IStream *pStream;
6573 HRESULT hStorageRes, hRes = S_OK;
6574 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6575 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6577 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6578 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6580 /* Initialize the CompObj structure */
6581 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6582 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6583 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6586 /* Create a CompObj stream if it doesn't exist */
6587 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6588 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6589 if(hStorageRes == S_OK)
6591 /* copy the OleTypeName to the compobj struct */
6592 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6593 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6595 /* copy the OleTypeName to the compobj struct */
6596 /* Note: in the test made, these were Identical */
6597 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6598 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6600 /* Get the CLSID */
6601 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6603 if(hRes == S_OK)
6605 HKEY hKey;
6606 LONG hErr;
6607 /* Get the CLSID Default Name from the Registry */
6608 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6609 if(hErr == ERROR_SUCCESS)
6611 char strTemp[OLESTREAM_MAX_STR_LEN];
6612 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6613 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6614 if(hErr == ERROR_SUCCESS)
6616 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6618 RegCloseKey(hKey);
6622 /* Write CompObj Structure to stream */
6623 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6625 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6627 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6628 if(IStorageCompObj.dwCLSIDNameLength > 0)
6630 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6632 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6633 if(IStorageCompObj.dwOleTypeNameLength > 0)
6635 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6637 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6638 if(IStorageCompObj.dwProgIDNameLength > 0)
6640 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6642 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6643 IStream_Release(pStream);
6645 return hRes;
6649 /*************************************************************************
6650 * OLECONVERT_CreateOlePresStream[Internal]
6652 * Creates the "\002OlePres000" Stream with the Metafile data
6654 * PARAMS
6655 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6656 * dwExtentX [I] Width of the Metafile
6657 * dwExtentY [I] Height of the Metafile
6658 * pData [I] Metafile data
6659 * dwDataLength [I] Size of the Metafile data
6661 * RETURNS
6662 * Success: S_OK
6663 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6665 * NOTES
6666 * This function is used by OleConvertOLESTREAMToIStorage only.
6669 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6671 HRESULT hRes;
6672 IStream *pStream;
6673 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6674 BYTE pOlePresStreamHeader [] =
6676 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6677 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6678 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6679 0x00, 0x00, 0x00, 0x00
6682 BYTE pOlePresStreamHeaderEmpty [] =
6684 0x00, 0x00, 0x00, 0x00,
6685 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6686 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6687 0x00, 0x00, 0x00, 0x00
6690 /* Create the OlePres000 Stream */
6691 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6692 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6694 if(hRes == S_OK)
6696 DWORD nHeaderSize;
6697 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6699 memset(&OlePres, 0, sizeof(OlePres));
6700 /* Do we have any metafile data to save */
6701 if(dwDataLength > 0)
6703 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6704 nHeaderSize = sizeof(pOlePresStreamHeader);
6706 else
6708 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6709 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6711 /* Set width and height of the metafile */
6712 OlePres.dwExtentX = dwExtentX;
6713 OlePres.dwExtentY = -dwExtentY;
6715 /* Set Data and Lenght */
6716 if(dwDataLength > sizeof(METAFILEPICT16))
6718 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6719 OlePres.pData = &(pData[8]);
6721 /* Save OlePres000 Data to Stream */
6722 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6723 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6724 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6725 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6726 if(OlePres.dwSize > 0)
6728 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6730 IStream_Release(pStream);
6734 /*************************************************************************
6735 * OLECONVERT_CreateOle10NativeStream [Internal]
6737 * Creates the "\001Ole10Native" Stream (should contain a BMP)
6739 * PARAMS
6740 * pStorage [I] Dest storage to create the stream in
6741 * pData [I] Ole10 Native Data (ex. bmp)
6742 * dwDataLength [I] Size of the Ole10 Native Data
6744 * RETURNS
6745 * Nothing
6747 * NOTES
6748 * This function is used by OleConvertOLESTREAMToIStorage only.
6750 * Might need to verify the data and return appropriate error message
6753 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6755 HRESULT hRes;
6756 IStream *pStream;
6757 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6759 /* Create the Ole10Native Stream */
6760 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6761 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6763 if(hRes == S_OK)
6765 /* Write info to stream */
6766 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6767 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6768 IStream_Release(pStream);
6773 /*************************************************************************
6774 * OLECONVERT_GetOLE10ProgID [Internal]
6776 * Finds the ProgID (or OleTypeID) from the IStorage
6778 * PARAMS
6779 * pStorage [I] The Src IStorage to get the ProgID
6780 * strProgID [I] the ProgID string to get
6781 * dwSize [I] the size of the string
6783 * RETURNS
6784 * Success: S_OK
6785 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6787 * NOTES
6788 * This function is used by OleConvertIStorageToOLESTREAM only.
6792 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6794 HRESULT hRes;
6795 IStream *pStream;
6796 LARGE_INTEGER iSeekPos;
6797 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6798 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6800 /* Open the CompObj Stream */
6801 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6802 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6803 if(hRes == S_OK)
6806 /*Get the OleType from the CompObj Stream */
6807 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6808 iSeekPos.s.HighPart = 0;
6810 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6811 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6812 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6813 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6814 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6815 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6816 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6818 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6819 if(*dwSize > 0)
6821 IStream_Read(pStream, strProgID, *dwSize, NULL);
6823 IStream_Release(pStream);
6825 else
6827 STATSTG stat;
6828 LPOLESTR wstrProgID;
6830 /* Get the OleType from the registry */
6831 REFCLSID clsid = &(stat.clsid);
6832 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6833 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6834 if(hRes == S_OK)
6836 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6840 return hRes;
6843 /*************************************************************************
6844 * OLECONVERT_GetOle10PresData [Internal]
6846 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6848 * PARAMS
6849 * pStorage [I] Src IStroage
6850 * pOleStream [I] Dest OleStream Mem Struct
6852 * RETURNS
6853 * Nothing
6855 * NOTES
6856 * This function is used by OleConvertIStorageToOLESTREAM only.
6858 * Memory allocated for pData must be freed by the caller
6862 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6865 HRESULT hRes;
6866 IStream *pStream;
6867 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6869 /* Initialize Default data for OLESTREAM */
6870 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6871 pOleStreamData[0].dwTypeID = 2;
6872 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6873 pOleStreamData[1].dwTypeID = 0;
6874 pOleStreamData[0].dwMetaFileWidth = 0;
6875 pOleStreamData[0].dwMetaFileHeight = 0;
6876 pOleStreamData[0].pData = NULL;
6877 pOleStreamData[1].pData = NULL;
6879 /* Open Ole10Native Stream */
6880 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6881 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6882 if(hRes == S_OK)
6885 /* Read Size and Data */
6886 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6887 if(pOleStreamData->dwDataLength > 0)
6889 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6890 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6892 IStream_Release(pStream);
6898 /*************************************************************************
6899 * OLECONVERT_GetOle20PresData[Internal]
6901 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6903 * PARAMS
6904 * pStorage [I] Src IStroage
6905 * pOleStreamData [I] Dest OleStream Mem Struct
6907 * RETURNS
6908 * Nothing
6910 * NOTES
6911 * This function is used by OleConvertIStorageToOLESTREAM only.
6913 * Memory allocated for pData must be freed by the caller
6915 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6917 HRESULT hRes;
6918 IStream *pStream;
6919 OLECONVERT_ISTORAGE_OLEPRES olePress;
6920 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6922 /* Initialize Default data for OLESTREAM */
6923 pOleStreamData[0].dwOleID = OLESTREAM_ID;
6924 pOleStreamData[0].dwTypeID = 2;
6925 pOleStreamData[0].dwMetaFileWidth = 0;
6926 pOleStreamData[0].dwMetaFileHeight = 0;
6927 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6928 pOleStreamData[1].dwOleID = OLESTREAM_ID;
6929 pOleStreamData[1].dwTypeID = 0;
6930 pOleStreamData[1].dwOleTypeNameLength = 0;
6931 pOleStreamData[1].strOleTypeName[0] = 0;
6932 pOleStreamData[1].dwMetaFileWidth = 0;
6933 pOleStreamData[1].dwMetaFileHeight = 0;
6934 pOleStreamData[1].pData = NULL;
6935 pOleStreamData[1].dwDataLength = 0;
6938 /* Open OlePress000 stream */
6939 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
6940 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6941 if(hRes == S_OK)
6943 LARGE_INTEGER iSeekPos;
6944 METAFILEPICT16 MetaFilePict;
6945 char strMetafilePictName[] = "METAFILEPICT";
6947 /* Set the TypeID for a Metafile */
6948 pOleStreamData[1].dwTypeID = 5;
6950 /* Set the OleTypeName to Metafile */
6951 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6952 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6954 iSeekPos.s.HighPart = 0;
6955 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6957 /* Get Presentation Data */
6958 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6959 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6960 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6961 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6963 /*Set width and Height */
6964 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6965 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6966 if(olePress.dwSize > 0)
6968 /* Set Length */
6969 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
6971 /* Set MetaFilePict struct */
6972 MetaFilePict.mm = 8;
6973 MetaFilePict.xExt = olePress.dwExtentX;
6974 MetaFilePict.yExt = olePress.dwExtentY;
6975 MetaFilePict.hMF = 0;
6977 /* Get Metafile Data */
6978 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6979 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6980 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6982 IStream_Release(pStream);
6986 /*************************************************************************
6987 * OleConvertOLESTREAMToIStorage [OLE32.87]
6989 * Read info on MSDN
6991 * TODO
6992 * DVTARGETDEVICE paramenter is not handled
6993 * Still unsure of some mem fields for OLE 10 Stream
6994 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6995 * and "\001OLE" streams
6998 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6999 LPOLESTREAM pOleStream,
7000 LPSTORAGE pstg,
7001 const DVTARGETDEVICE* ptd)
7003 int i;
7004 HRESULT hRes=S_OK;
7005 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7007 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7009 if(ptd != NULL)
7011 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7014 if(pstg == NULL || pOleStream == NULL)
7016 hRes = E_INVALIDARG;
7019 if(hRes == S_OK)
7021 /* Load the OLESTREAM to Memory */
7022 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7025 if(hRes == S_OK)
7027 /* Load the OLESTREAM to Memory (part 2)*/
7028 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7031 if(hRes == S_OK)
7034 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7036 /* Do we have the IStorage Data in the OLESTREAM */
7037 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7039 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7040 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7042 else
7044 /* It must be an original OLE 1.0 source */
7045 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7048 else
7050 /* It must be an original OLE 1.0 source */
7051 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7054 /* Create CompObj Stream if necessary */
7055 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7056 if(hRes == S_OK)
7058 /*Create the Ole Stream if necessary */
7059 OLECONVERT_CreateOleStream(pstg);
7064 /* Free allocated memory */
7065 for(i=0; i < 2; i++)
7067 if(pOleStreamData[i].pData != NULL)
7069 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7071 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7073 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7074 pOleStreamData[i].pstrOleObjFileName = NULL;
7077 return hRes;
7080 /*************************************************************************
7081 * OleConvertIStorageToOLESTREAM [OLE32.85]
7083 * Read info on MSDN
7085 * Read info on MSDN
7087 * TODO
7088 * Still unsure of some mem fields for OLE 10 Stream
7089 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7090 * and "\001OLE" streams.
7093 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7094 LPSTORAGE pstg,
7095 LPOLESTREAM pOleStream)
7097 int i;
7098 HRESULT hRes = S_OK;
7099 IStream *pStream;
7100 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7101 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7104 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7106 if(pstg == NULL || pOleStream == NULL)
7108 hRes = E_INVALIDARG;
7110 if(hRes == S_OK)
7112 /* Get the ProgID */
7113 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7114 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7116 if(hRes == S_OK)
7118 /* Was it originally Ole10 */
7119 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7120 if(hRes == S_OK)
7122 IStream_Release(pStream);
7123 /* Get Presentation Data for Ole10Native */
7124 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7126 else
7128 /* Get Presentation Data (OLE20) */
7129 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7132 /* Save OLESTREAM */
7133 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7134 if(hRes == S_OK)
7136 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7141 /* Free allocated memory */
7142 for(i=0; i < 2; i++)
7144 if(pOleStreamData[i].pData != NULL)
7146 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7150 return hRes;
7153 /***********************************************************************
7154 * GetConvertStg (OLE32.68)
7156 HRESULT WINAPI GetConvertStg(LPGUID guid) {
7157 FIXME("(%s), unimplemented stub!\n",debugstr_guid(guid));
7158 return E_FAIL;
7161 /******************************************************************************
7162 * StgIsStorageFile [OLE32.146]
7164 HRESULT WINAPI
7165 StgIsStorageFile(LPCOLESTR fn)
7167 HANDLE hf;
7168 BYTE magic[8];
7169 DWORD bytes_read;
7171 TRACE("(\'%s\')\n", debugstr_w(fn));
7172 hf = CreateFileW(fn, GENERIC_READ,
7173 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7174 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7176 if (hf == INVALID_HANDLE_VALUE)
7177 return STG_E_FILENOTFOUND;
7179 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7181 WARN(" unable to read file\n");
7182 CloseHandle(hf);
7183 return S_FALSE;
7186 CloseHandle(hf);
7188 if (bytes_read != 8) {
7189 WARN(" too short\n");
7190 return S_FALSE;
7193 if (!memcmp(magic,STORAGE_magic,8)) {
7194 WARN(" -> YES\n");
7195 return S_OK;
7198 WARN(" -> Invalid header.\n");
7199 return S_FALSE;