Don't define COBJMACROS in objbase.h.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob6ec67d2ec53e20160d91df0090d6e5beffeab73e
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 <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
33 #define COBJMACROS
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winuser.h"
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
44 #include "storage32.h"
45 #include "ole2.h" /* For Write/ReadClassStm */
47 #include "winreg.h"
48 #include "wine/wingdi16.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(storage);
52 #define FILE_BEGIN 0
55 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
56 #define OLESTREAM_ID 0x501
57 #define OLESTREAM_MAX_STR_LEN 255
59 static const char rootPropertyName[] = "Root Entry";
62 /* OLESTREAM memory structure to use for Get and Put Routines */
63 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
64 typedef struct
66 DWORD dwOleID;
67 DWORD dwTypeID;
68 DWORD dwOleTypeNameLength;
69 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
70 CHAR *pstrOleObjFileName;
71 DWORD dwOleObjFileNameLength;
72 DWORD dwMetaFileWidth;
73 DWORD dwMetaFileHeight;
74 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
75 DWORD dwDataLength;
76 BYTE *pData;
77 }OLECONVERT_OLESTREAM_DATA;
79 /* CompObj Stream structure */
80 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
81 typedef struct
83 BYTE byUnknown1[12];
84 CLSID clsid;
85 DWORD dwCLSIDNameLength;
86 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
87 DWORD dwOleTypeNameLength;
88 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
89 DWORD dwProgIDNameLength;
90 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
91 BYTE byUnknown2[16];
92 }OLECONVERT_ISTORAGE_COMPOBJ;
95 /* Ole Presention Stream structure */
96 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
97 typedef struct
99 BYTE byUnknown1[28];
100 DWORD dwExtentX;
101 DWORD dwExtentY;
102 DWORD dwSize;
103 BYTE *pData;
104 }OLECONVERT_ISTORAGE_OLEPRES;
108 /***********************************************************************
109 * Forward declaration of internal functions used by the method DestroyElement
111 static HRESULT deleteStorageProperty(
112 StorageImpl *parentStorage,
113 ULONG foundPropertyIndexToDelete,
114 StgProperty propertyToDelete);
116 static HRESULT deleteStreamProperty(
117 StorageImpl *parentStorage,
118 ULONG foundPropertyIndexToDelete,
119 StgProperty propertyToDelete);
121 static HRESULT findPlaceholder(
122 StorageImpl *storage,
123 ULONG propertyIndexToStore,
124 ULONG storagePropertyIndex,
125 INT typeOfRelation);
127 static HRESULT adjustPropertyChain(
128 StorageImpl *This,
129 StgProperty propertyToDelete,
130 StgProperty parentProperty,
131 ULONG parentPropertyId,
132 INT typeOfRelation);
134 /***********************************************************************
135 * Declaration of the functions used to manipulate StgProperty
138 static ULONG getFreeProperty(
139 StorageImpl *storage);
141 static void updatePropertyChain(
142 StorageImpl *storage,
143 ULONG newPropertyIndex,
144 StgProperty newProperty);
146 static LONG propertyNameCmp(
147 OLECHAR *newProperty,
148 OLECHAR *currentProperty);
151 /***********************************************************************
152 * Declaration of miscellaneous functions...
154 static HRESULT validateSTGM(DWORD stgmValue);
156 static DWORD GetShareModeFromSTGM(DWORD stgm);
157 static DWORD GetAccessModeFromSTGM(DWORD stgm);
158 static DWORD GetCreationModeFromSTGM(DWORD stgm);
161 * Virtual function table for the IStorage32Impl class.
163 static IStorageVtbl Storage32Impl_Vtbl =
165 StorageBaseImpl_QueryInterface,
166 StorageBaseImpl_AddRef,
167 StorageBaseImpl_Release,
168 StorageBaseImpl_CreateStream,
169 StorageBaseImpl_OpenStream,
170 StorageImpl_CreateStorage,
171 StorageBaseImpl_OpenStorage,
172 StorageImpl_CopyTo,
173 StorageImpl_MoveElementTo,
174 StorageImpl_Commit,
175 StorageImpl_Revert,
176 StorageBaseImpl_EnumElements,
177 StorageImpl_DestroyElement,
178 StorageBaseImpl_RenameElement,
179 StorageImpl_SetElementTimes,
180 StorageBaseImpl_SetClass,
181 StorageImpl_SetStateBits,
182 StorageImpl_Stat
186 * Virtual function table for the Storage32InternalImpl class.
188 static IStorageVtbl Storage32InternalImpl_Vtbl =
190 StorageBaseImpl_QueryInterface,
191 StorageBaseImpl_AddRef,
192 StorageBaseImpl_Release,
193 StorageBaseImpl_CreateStream,
194 StorageBaseImpl_OpenStream,
195 StorageImpl_CreateStorage,
196 StorageBaseImpl_OpenStorage,
197 StorageImpl_CopyTo,
198 StorageImpl_MoveElementTo,
199 StorageInternalImpl_Commit,
200 StorageInternalImpl_Revert,
201 StorageBaseImpl_EnumElements,
202 StorageImpl_DestroyElement,
203 StorageBaseImpl_RenameElement,
204 StorageImpl_SetElementTimes,
205 StorageBaseImpl_SetClass,
206 StorageImpl_SetStateBits,
207 StorageBaseImpl_Stat
211 * Virtual function table for the IEnumSTATSTGImpl class.
213 static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
215 IEnumSTATSTGImpl_QueryInterface,
216 IEnumSTATSTGImpl_AddRef,
217 IEnumSTATSTGImpl_Release,
218 IEnumSTATSTGImpl_Next,
219 IEnumSTATSTGImpl_Skip,
220 IEnumSTATSTGImpl_Reset,
221 IEnumSTATSTGImpl_Clone
228 /************************************************************************
229 ** Storage32BaseImpl implementatiion
232 /************************************************************************
233 * Storage32BaseImpl_QueryInterface (IUnknown)
235 * This method implements the common QueryInterface for all IStorage32
236 * implementations contained in this file.
238 * See Windows documentation for more details on IUnknown methods.
240 HRESULT WINAPI StorageBaseImpl_QueryInterface(
241 IStorage* iface,
242 REFIID riid,
243 void** ppvObject)
245 StorageBaseImpl *This = (StorageBaseImpl *)iface;
247 * Perform a sanity check on the parameters.
249 if ( (This==0) || (ppvObject==0) )
250 return E_INVALIDARG;
253 * Initialize the return parameter.
255 *ppvObject = 0;
258 * Compare the riid with the interface IDs implemented by this object.
260 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
262 *ppvObject = (IStorage*)This;
264 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
266 *ppvObject = (IStorage*)This;
270 * Check that we obtained an interface.
272 if ((*ppvObject)==0)
273 return E_NOINTERFACE;
276 * Query Interface always increases the reference count by one when it is
277 * successful
279 StorageBaseImpl_AddRef(iface);
281 return S_OK;
284 /************************************************************************
285 * Storage32BaseImpl_AddRef (IUnknown)
287 * This method implements the common AddRef for all IStorage32
288 * implementations contained in this file.
290 * See Windows documentation for more details on IUnknown methods.
292 ULONG WINAPI StorageBaseImpl_AddRef(
293 IStorage* iface)
295 StorageBaseImpl *This = (StorageBaseImpl *)iface;
296 return InterlockedIncrement(&This->ref);
299 /************************************************************************
300 * Storage32BaseImpl_Release (IUnknown)
302 * This method implements the common Release for all IStorage32
303 * implementations contained in this file.
305 * See Windows documentation for more details on IUnknown methods.
307 ULONG WINAPI StorageBaseImpl_Release(
308 IStorage* iface)
310 StorageBaseImpl *This = (StorageBaseImpl *)iface;
312 * Decrease the reference count on this object.
314 ULONG ref = InterlockedDecrement(&This->ref);
317 * If the reference count goes down to 0, perform suicide.
319 if (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);
329 return ref;
332 /************************************************************************
333 * Storage32BaseImpl_OpenStream (IStorage)
335 * This method will open the specified stream object from the current storage.
337 * See Windows documentation for more details on IStorage methods.
339 HRESULT WINAPI StorageBaseImpl_OpenStream(
340 IStorage* iface,
341 const OLECHAR* pwcsName, /* [string][in] */
342 void* reserved1, /* [unique][in] */
343 DWORD grfMode, /* [in] */
344 DWORD reserved2, /* [in] */
345 IStream** ppstm) /* [out] */
347 StorageBaseImpl *This = (StorageBaseImpl *)iface;
348 IEnumSTATSTGImpl* propertyEnumeration;
349 StgStreamImpl* newStream;
350 StgProperty currentProperty;
351 ULONG foundPropertyIndex;
352 HRESULT res = STG_E_UNKNOWN;
354 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
355 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
358 * Perform a sanity check on the parameters.
360 if ( (pwcsName==NULL) || (ppstm==0) )
362 res = E_INVALIDARG;
363 goto end;
367 * Initialize the out parameter
369 *ppstm = NULL;
372 * Validate the STGM flags
374 if ( FAILED( validateSTGM(grfMode) ))
376 res = STG_E_INVALIDFLAG;
377 goto end;
381 * As documented.
383 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
384 (grfMode & STGM_DELETEONRELEASE) ||
385 (grfMode & STGM_TRANSACTED) )
387 res = STG_E_INVALIDFUNCTION;
388 goto end;
392 * Create a property enumeration to search the properties
394 propertyEnumeration = IEnumSTATSTGImpl_Construct(
395 This->ancestorStorage,
396 This->rootPropertySetIndex);
399 * Search the enumeration for the property with the given name
401 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
402 propertyEnumeration,
403 pwcsName,
404 &currentProperty);
407 * Delete the property enumeration since we don't need it anymore
409 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
412 * If it was found, construct the stream object and return a pointer to it.
414 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
415 (currentProperty.propertyType==PROPTYPE_STREAM) )
417 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
419 if (newStream!=0)
421 newStream->grfMode = grfMode;
422 *ppstm = (IStream*)newStream;
425 * Since we are returning a pointer to the interface, we have to
426 * nail down the reference.
428 StgStreamImpl_AddRef(*ppstm);
430 res = S_OK;
431 goto end;
434 res = E_OUTOFMEMORY;
435 goto end;
438 res = STG_E_FILENOTFOUND;
440 end:
441 if (res == S_OK)
442 TRACE("<-- IStream %p\n", *ppstm);
443 TRACE("<-- %08lx\n", res);
444 return res;
447 /************************************************************************
448 * Storage32BaseImpl_OpenStorage (IStorage)
450 * This method will open a new storage object from the current storage.
452 * See Windows documentation for more details on IStorage methods.
454 HRESULT WINAPI StorageBaseImpl_OpenStorage(
455 IStorage* iface,
456 const OLECHAR* pwcsName, /* [string][unique][in] */
457 IStorage* pstgPriority, /* [unique][in] */
458 DWORD grfMode, /* [in] */
459 SNB snbExclude, /* [unique][in] */
460 DWORD reserved, /* [in] */
461 IStorage** ppstg) /* [out] */
463 StorageBaseImpl *This = (StorageBaseImpl *)iface;
464 StorageInternalImpl* newStorage;
465 IEnumSTATSTGImpl* propertyEnumeration;
466 StgProperty currentProperty;
467 ULONG foundPropertyIndex;
468 HRESULT res = STG_E_UNKNOWN;
470 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
471 iface, debugstr_w(pwcsName), pstgPriority,
472 grfMode, snbExclude, reserved, ppstg);
475 * Perform a sanity check on the parameters.
477 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
479 res = E_INVALIDARG;
480 goto end;
483 /* as documented */
484 if (snbExclude != NULL)
486 res = STG_E_INVALIDPARAMETER;
487 goto end;
491 * Validate the STGM flags
493 if ( FAILED( validateSTGM(grfMode) ))
495 res = STG_E_INVALIDFLAG;
496 goto end;
500 * As documented.
502 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
503 (grfMode & STGM_DELETEONRELEASE) ||
504 (grfMode & STGM_PRIORITY) )
506 res = STG_E_INVALIDFUNCTION;
507 goto end;
511 * Initialize the out parameter
513 *ppstg = NULL;
516 * Create a property enumeration to search the properties
518 propertyEnumeration = IEnumSTATSTGImpl_Construct(
519 This->ancestorStorage,
520 This->rootPropertySetIndex);
523 * Search the enumeration for the property with the given name
525 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
526 propertyEnumeration,
527 pwcsName,
528 &currentProperty);
531 * Delete the property enumeration since we don't need it anymore
533 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
536 * If it was found, construct the stream object and return a pointer to it.
538 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
539 (currentProperty.propertyType==PROPTYPE_STORAGE) )
542 * Construct a new Storage object
544 newStorage = StorageInternalImpl_Construct(
545 This->ancestorStorage,
546 foundPropertyIndex);
548 if (newStorage != 0)
550 *ppstg = (IStorage*)newStorage;
553 * Since we are returning a pointer to the interface,
554 * we have to nail down the reference.
556 StorageBaseImpl_AddRef(*ppstg);
558 res = S_OK;
559 goto end;
562 res = STG_E_INSUFFICIENTMEMORY;
563 goto end;
566 res = STG_E_FILENOTFOUND;
568 end:
569 TRACE("<-- %08lx\n", res);
570 return res;
573 /************************************************************************
574 * Storage32BaseImpl_EnumElements (IStorage)
576 * This method will create an enumerator object that can be used to
577 * retrieve informatino about all the properties in the storage object.
579 * See Windows documentation for more details on IStorage methods.
581 HRESULT WINAPI StorageBaseImpl_EnumElements(
582 IStorage* iface,
583 DWORD reserved1, /* [in] */
584 void* reserved2, /* [size_is][unique][in] */
585 DWORD reserved3, /* [in] */
586 IEnumSTATSTG** ppenum) /* [out] */
588 StorageBaseImpl *This = (StorageBaseImpl *)iface;
589 IEnumSTATSTGImpl* newEnum;
591 TRACE("(%p, %ld, %p, %ld, %p)\n",
592 iface, reserved1, reserved2, reserved3, ppenum);
595 * Perform a sanity check on the parameters.
597 if ( (This==0) || (ppenum==0))
598 return E_INVALIDARG;
601 * Construct the enumerator.
603 newEnum = IEnumSTATSTGImpl_Construct(
604 This->ancestorStorage,
605 This->rootPropertySetIndex);
607 if (newEnum!=0)
609 *ppenum = (IEnumSTATSTG*)newEnum;
612 * Don't forget to nail down a reference to the new object before
613 * returning it.
615 IEnumSTATSTGImpl_AddRef(*ppenum);
617 return S_OK;
620 return E_OUTOFMEMORY;
623 /************************************************************************
624 * Storage32BaseImpl_Stat (IStorage)
626 * This method will retrieve information about this storage object.
628 * See Windows documentation for more details on IStorage methods.
630 HRESULT WINAPI StorageBaseImpl_Stat(
631 IStorage* iface,
632 STATSTG* pstatstg, /* [out] */
633 DWORD grfStatFlag) /* [in] */
635 StorageBaseImpl *This = (StorageBaseImpl *)iface;
636 StgProperty curProperty;
637 BOOL readSuccessful;
638 HRESULT res = STG_E_UNKNOWN;
640 TRACE("(%p, %p, %lx)\n",
641 iface, pstatstg, grfStatFlag);
644 * Perform a sanity check on the parameters.
646 if ( (This==0) || (pstatstg==0))
648 res = E_INVALIDARG;
649 goto end;
653 * Read the information from the property.
655 readSuccessful = StorageImpl_ReadProperty(
656 This->ancestorStorage,
657 This->rootPropertySetIndex,
658 &curProperty);
660 if (readSuccessful)
662 StorageUtl_CopyPropertyToSTATSTG(
663 pstatstg,
664 &curProperty,
665 grfStatFlag);
667 res = S_OK;
668 goto end;
671 res = E_FAIL;
673 end:
674 if (res == S_OK)
676 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
678 TRACE("<-- %08lx\n", res);
679 return res;
682 /************************************************************************
683 * Storage32BaseImpl_RenameElement (IStorage)
685 * This method will rename the specified element.
687 * See Windows documentation for more details on IStorage methods.
689 * Implementation notes: The method used to rename consists of creating a clone
690 * of the deleted StgProperty object setting it with the new name and to
691 * perform a DestroyElement of the old StgProperty.
693 HRESULT WINAPI StorageBaseImpl_RenameElement(
694 IStorage* iface,
695 const OLECHAR* pwcsOldName, /* [in] */
696 const OLECHAR* pwcsNewName) /* [in] */
698 StorageBaseImpl *This = (StorageBaseImpl *)iface;
699 IEnumSTATSTGImpl* propertyEnumeration;
700 StgProperty currentProperty;
701 ULONG foundPropertyIndex;
703 TRACE("(%p, %s, %s)\n",
704 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
707 * Create a property enumeration to search the properties
709 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
710 This->rootPropertySetIndex);
713 * Search the enumeration for the new property name
715 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
716 pwcsNewName,
717 &currentProperty);
719 if (foundPropertyIndex != PROPERTY_NULL)
722 * There is already a property with the new name
724 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
725 return STG_E_FILEALREADYEXISTS;
728 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
731 * Search the enumeration for the old property name
733 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
734 pwcsOldName,
735 &currentProperty);
738 * Delete the property enumeration since we don't need it anymore
740 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
742 if (foundPropertyIndex != PROPERTY_NULL)
744 StgProperty renamedProperty;
745 ULONG renamedPropertyIndex;
748 * Setup a new property for the renamed property
750 renamedProperty.sizeOfNameString =
751 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
753 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
754 return STG_E_INVALIDNAME;
756 strcpyW(renamedProperty.name, pwcsNewName);
758 renamedProperty.propertyType = currentProperty.propertyType;
759 renamedProperty.startingBlock = currentProperty.startingBlock;
760 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
761 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
763 renamedProperty.previousProperty = PROPERTY_NULL;
764 renamedProperty.nextProperty = PROPERTY_NULL;
767 * Bring the dirProperty link in case it is a storage and in which
768 * case the renamed storage elements don't require to be reorganized.
770 renamedProperty.dirProperty = currentProperty.dirProperty;
772 /* call CoFileTime to get the current time
773 renamedProperty.timeStampS1
774 renamedProperty.timeStampD1
775 renamedProperty.timeStampS2
776 renamedProperty.timeStampD2
777 renamedProperty.propertyUniqueID
781 * Obtain a free property in the property chain
783 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
786 * Save the new property into the new property spot
788 StorageImpl_WriteProperty(
789 This->ancestorStorage,
790 renamedPropertyIndex,
791 &renamedProperty);
794 * Find a spot in the property chain for our newly created property.
796 updatePropertyChain(
797 (StorageImpl*)This,
798 renamedPropertyIndex,
799 renamedProperty);
802 * At this point the renamed property has been inserted in the tree,
803 * now, before to Destroy the old property we must zeroed it's dirProperty
804 * otherwise the DestroyProperty below will zap it all and we do not want
805 * this to happen.
806 * Also, we fake that the old property is a storage so the DestroyProperty
807 * will not do a SetSize(0) on the stream data.
809 * This means that we need to tweek the StgProperty if it is a stream or a
810 * non empty storage.
812 StorageImpl_ReadProperty(This->ancestorStorage,
813 foundPropertyIndex,
814 &currentProperty);
816 currentProperty.dirProperty = PROPERTY_NULL;
817 currentProperty.propertyType = PROPTYPE_STORAGE;
818 StorageImpl_WriteProperty(
819 This->ancestorStorage,
820 foundPropertyIndex,
821 &currentProperty);
824 * Invoke Destroy to get rid of the ole property and automatically redo
825 * the linking of it's previous and next members...
827 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
830 else
833 * There is no property with the old name
835 return STG_E_FILENOTFOUND;
838 return S_OK;
841 /************************************************************************
842 * Storage32BaseImpl_CreateStream (IStorage)
844 * This method will create a stream object within this storage
846 * See Windows documentation for more details on IStorage methods.
848 HRESULT WINAPI StorageBaseImpl_CreateStream(
849 IStorage* iface,
850 const OLECHAR* pwcsName, /* [string][in] */
851 DWORD grfMode, /* [in] */
852 DWORD reserved1, /* [in] */
853 DWORD reserved2, /* [in] */
854 IStream** ppstm) /* [out] */
856 StorageBaseImpl *This = (StorageBaseImpl *)iface;
857 IEnumSTATSTGImpl* propertyEnumeration;
858 StgStreamImpl* newStream;
859 StgProperty currentProperty, newStreamProperty;
860 ULONG foundPropertyIndex, newPropertyIndex;
862 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
863 iface, debugstr_w(pwcsName), grfMode,
864 reserved1, reserved2, ppstm);
867 * Validate parameters
869 if (ppstm == 0)
870 return STG_E_INVALIDPOINTER;
872 if (pwcsName == 0)
873 return STG_E_INVALIDNAME;
876 * Validate the STGM flags
878 if ( FAILED( validateSTGM(grfMode) ))
879 return STG_E_INVALIDFLAG;
882 * As documented.
884 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
885 (grfMode & STGM_DELETEONRELEASE) ||
886 (grfMode & STGM_TRANSACTED) )
887 return STG_E_INVALIDFUNCTION;
890 * Initialize the out parameter
892 *ppstm = 0;
895 * Create a property enumeration to search the properties
897 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
898 This->rootPropertySetIndex);
900 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
901 pwcsName,
902 &currentProperty);
904 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
906 if (foundPropertyIndex != PROPERTY_NULL)
909 * An element with this name already exists
911 if (grfMode & STGM_CREATE)
913 IStorage_DestroyElement(iface, pwcsName);
915 else
916 return STG_E_FILEALREADYEXISTS;
920 * memset the empty property
922 memset(&newStreamProperty, 0, sizeof(StgProperty));
924 newStreamProperty.sizeOfNameString =
925 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
927 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
928 return STG_E_INVALIDNAME;
930 strcpyW(newStreamProperty.name, pwcsName);
932 newStreamProperty.propertyType = PROPTYPE_STREAM;
933 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
934 newStreamProperty.size.u.LowPart = 0;
935 newStreamProperty.size.u.HighPart = 0;
937 newStreamProperty.previousProperty = PROPERTY_NULL;
938 newStreamProperty.nextProperty = PROPERTY_NULL;
939 newStreamProperty.dirProperty = PROPERTY_NULL;
941 /* call CoFileTime to get the current time
942 newStreamProperty.timeStampS1
943 newStreamProperty.timeStampD1
944 newStreamProperty.timeStampS2
945 newStreamProperty.timeStampD2
948 /* newStreamProperty.propertyUniqueID */
951 * Get a free property or create a new one
953 newPropertyIndex = getFreeProperty(This->ancestorStorage);
956 * Save the new property into the new property spot
958 StorageImpl_WriteProperty(
959 This->ancestorStorage,
960 newPropertyIndex,
961 &newStreamProperty);
964 * Find a spot in the property chain for our newly created property.
966 updatePropertyChain(
967 (StorageImpl*)This,
968 newPropertyIndex,
969 newStreamProperty);
972 * Open the stream to return it.
974 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
976 if (newStream != 0)
978 *ppstm = (IStream*)newStream;
981 * Since we are returning a pointer to the interface, we have to nail down
982 * the reference.
984 StgStreamImpl_AddRef(*ppstm);
986 else
988 return STG_E_INSUFFICIENTMEMORY;
991 return S_OK;
994 /************************************************************************
995 * Storage32BaseImpl_SetClass (IStorage)
997 * This method will write the specified CLSID in the property of this
998 * storage.
1000 * See Windows documentation for more details on IStorage methods.
1002 HRESULT WINAPI StorageBaseImpl_SetClass(
1003 IStorage* iface,
1004 REFCLSID clsid) /* [in] */
1006 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1007 HRESULT hRes = E_FAIL;
1008 StgProperty curProperty;
1009 BOOL success;
1011 TRACE("(%p, %p)\n", iface, clsid);
1013 success = StorageImpl_ReadProperty(This->ancestorStorage,
1014 This->rootPropertySetIndex,
1015 &curProperty);
1016 if (success)
1018 curProperty.propertyUniqueID = *clsid;
1020 success = StorageImpl_WriteProperty(This->ancestorStorage,
1021 This->rootPropertySetIndex,
1022 &curProperty);
1023 if (success)
1024 hRes = S_OK;
1027 return hRes;
1030 /************************************************************************
1031 ** Storage32Impl implementation
1034 /************************************************************************
1035 * Storage32Impl_CreateStorage (IStorage)
1037 * This method will create the storage object within the provided storage.
1039 * See Windows documentation for more details on IStorage methods.
1041 HRESULT WINAPI StorageImpl_CreateStorage(
1042 IStorage* iface,
1043 const OLECHAR *pwcsName, /* [string][in] */
1044 DWORD grfMode, /* [in] */
1045 DWORD reserved1, /* [in] */
1046 DWORD reserved2, /* [in] */
1047 IStorage **ppstg) /* [out] */
1049 StorageImpl* const This=(StorageImpl*)iface;
1051 IEnumSTATSTGImpl *propertyEnumeration;
1052 StgProperty currentProperty;
1053 StgProperty newProperty;
1054 ULONG foundPropertyIndex;
1055 ULONG newPropertyIndex;
1056 HRESULT hr;
1058 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1059 iface, debugstr_w(pwcsName), grfMode,
1060 reserved1, reserved2, ppstg);
1063 * Validate parameters
1065 if (ppstg == 0)
1066 return STG_E_INVALIDPOINTER;
1068 if (pwcsName == 0)
1069 return STG_E_INVALIDNAME;
1072 * Validate the STGM flags
1074 if ( FAILED( validateSTGM(grfMode) ) ||
1075 (grfMode & STGM_DELETEONRELEASE) )
1076 return STG_E_INVALIDFLAG;
1079 * Initialize the out parameter
1081 *ppstg = 0;
1084 * Create a property enumeration and search the properties
1086 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1087 This->rootPropertySetIndex);
1089 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1090 pwcsName,
1091 &currentProperty);
1092 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1094 if (foundPropertyIndex != PROPERTY_NULL)
1097 * An element with this name already exists
1099 if (grfMode & STGM_CREATE)
1100 IStorage_DestroyElement(iface, pwcsName);
1101 else
1102 return STG_E_FILEALREADYEXISTS;
1106 * memset the empty property
1108 memset(&newProperty, 0, sizeof(StgProperty));
1110 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1112 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1113 return STG_E_INVALIDNAME;
1115 strcpyW(newProperty.name, pwcsName);
1117 newProperty.propertyType = PROPTYPE_STORAGE;
1118 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1119 newProperty.size.u.LowPart = 0;
1120 newProperty.size.u.HighPart = 0;
1122 newProperty.previousProperty = PROPERTY_NULL;
1123 newProperty.nextProperty = PROPERTY_NULL;
1124 newProperty.dirProperty = PROPERTY_NULL;
1126 /* call CoFileTime to get the current time
1127 newProperty.timeStampS1
1128 newProperty.timeStampD1
1129 newProperty.timeStampS2
1130 newProperty.timeStampD2
1133 /* newStorageProperty.propertyUniqueID */
1136 * Obtain a free property in the property chain
1138 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1141 * Save the new property into the new property spot
1143 StorageImpl_WriteProperty(
1144 This->ancestorStorage,
1145 newPropertyIndex,
1146 &newProperty);
1149 * Find a spot in the property chain for our newly created property.
1151 updatePropertyChain(
1152 This,
1153 newPropertyIndex,
1154 newProperty);
1157 * Open it to get a pointer to return.
1159 hr = IStorage_OpenStorage(
1160 iface,
1161 (OLECHAR*)pwcsName,
1163 grfMode,
1166 ppstg);
1168 if( (hr != S_OK) || (*ppstg == NULL))
1170 return hr;
1174 return S_OK;
1178 /***************************************************************************
1180 * Internal Method
1182 * Get a free property or create a new one.
1184 static ULONG getFreeProperty(
1185 StorageImpl *storage)
1187 ULONG currentPropertyIndex = 0;
1188 ULONG newPropertyIndex = PROPERTY_NULL;
1189 BOOL readSuccessful = TRUE;
1190 StgProperty currentProperty;
1195 * Start by reading the root property
1197 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1198 currentPropertyIndex,
1199 &currentProperty);
1200 if (readSuccessful)
1202 if (currentProperty.sizeOfNameString == 0)
1205 * The property existis and is available, we found it.
1207 newPropertyIndex = currentPropertyIndex;
1210 else
1213 * We exhausted the property list, we will create more space below
1215 newPropertyIndex = currentPropertyIndex;
1217 currentPropertyIndex++;
1219 } while (newPropertyIndex == PROPERTY_NULL);
1222 * grow the property chain
1224 if (! readSuccessful)
1226 StgProperty emptyProperty;
1227 ULARGE_INTEGER newSize;
1228 ULONG propertyIndex;
1229 ULONG lastProperty = 0;
1230 ULONG blockCount = 0;
1233 * obtain the new count of property blocks
1235 blockCount = BlockChainStream_GetCount(
1236 storage->ancestorStorage->rootBlockChain)+1;
1239 * initialize the size used by the property stream
1241 newSize.u.HighPart = 0;
1242 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1245 * add a property block to the property chain
1247 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1250 * memset the empty property in order to initialize the unused newly
1251 * created property
1253 memset(&emptyProperty, 0, sizeof(StgProperty));
1256 * initialize them
1258 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1260 for(
1261 propertyIndex = newPropertyIndex;
1262 propertyIndex < lastProperty;
1263 propertyIndex++)
1265 StorageImpl_WriteProperty(
1266 storage->ancestorStorage,
1267 propertyIndex,
1268 &emptyProperty);
1272 return newPropertyIndex;
1275 /****************************************************************************
1277 * Internal Method
1279 * Case insensitive comparaison of StgProperty.name by first considering
1280 * their size.
1282 * Returns <0 when newPrpoerty < currentProperty
1283 * >0 when newPrpoerty > currentProperty
1284 * 0 when newPrpoerty == currentProperty
1286 static LONG propertyNameCmp(
1287 OLECHAR *newProperty,
1288 OLECHAR *currentProperty)
1290 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1292 if (diff == 0)
1295 * We compare the string themselves only when they are of the same length
1297 diff = lstrcmpiW( newProperty, currentProperty);
1300 return diff;
1303 /****************************************************************************
1305 * Internal Method
1307 * Properly link this new element in the property chain.
1309 static void updatePropertyChain(
1310 StorageImpl *storage,
1311 ULONG newPropertyIndex,
1312 StgProperty newProperty)
1314 StgProperty currentProperty;
1317 * Read the root property
1319 StorageImpl_ReadProperty(storage->ancestorStorage,
1320 storage->rootPropertySetIndex,
1321 &currentProperty);
1323 if (currentProperty.dirProperty != PROPERTY_NULL)
1326 * The root storage contains some element, therefore, start the research
1327 * for the appropriate location.
1329 BOOL found = 0;
1330 ULONG current, next, previous, currentPropertyId;
1333 * Keep the StgProperty sequence number of the storage first property
1335 currentPropertyId = currentProperty.dirProperty;
1338 * Read
1340 StorageImpl_ReadProperty(storage->ancestorStorage,
1341 currentProperty.dirProperty,
1342 &currentProperty);
1344 previous = currentProperty.previousProperty;
1345 next = currentProperty.nextProperty;
1346 current = currentPropertyId;
1348 while (found == 0)
1350 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1352 if (diff < 0)
1354 if (previous != PROPERTY_NULL)
1356 StorageImpl_ReadProperty(storage->ancestorStorage,
1357 previous,
1358 &currentProperty);
1359 current = previous;
1361 else
1363 currentProperty.previousProperty = newPropertyIndex;
1364 StorageImpl_WriteProperty(storage->ancestorStorage,
1365 current,
1366 &currentProperty);
1367 found = 1;
1370 else if (diff > 0)
1372 if (next != PROPERTY_NULL)
1374 StorageImpl_ReadProperty(storage->ancestorStorage,
1375 next,
1376 &currentProperty);
1377 current = next;
1379 else
1381 currentProperty.nextProperty = newPropertyIndex;
1382 StorageImpl_WriteProperty(storage->ancestorStorage,
1383 current,
1384 &currentProperty);
1385 found = 1;
1388 else
1391 * Trying to insert an item with the same name in the
1392 * subtree structure.
1394 assert(FALSE);
1397 previous = currentProperty.previousProperty;
1398 next = currentProperty.nextProperty;
1401 else
1404 * The root storage is empty, link the new property to it's dir property
1406 currentProperty.dirProperty = newPropertyIndex;
1407 StorageImpl_WriteProperty(storage->ancestorStorage,
1408 storage->rootPropertySetIndex,
1409 &currentProperty);
1414 /*************************************************************************
1415 * CopyTo (IStorage)
1417 HRESULT WINAPI StorageImpl_CopyTo(
1418 IStorage* iface,
1419 DWORD ciidExclude, /* [in] */
1420 const IID* rgiidExclude, /* [size_is][unique][in] */
1421 SNB snbExclude, /* [unique][in] */
1422 IStorage* pstgDest) /* [unique][in] */
1424 IEnumSTATSTG *elements = 0;
1425 STATSTG curElement, strStat;
1426 HRESULT hr;
1427 IStorage *pstgTmp, *pstgChild;
1428 IStream *pstrTmp, *pstrChild;
1430 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1431 FIXME("Exclude option not implemented\n");
1433 TRACE("(%p, %ld, %p, %p, %p)\n",
1434 iface, ciidExclude, rgiidExclude,
1435 snbExclude, pstgDest);
1438 * Perform a sanity check
1440 if ( pstgDest == 0 )
1441 return STG_E_INVALIDPOINTER;
1444 * Enumerate the elements
1446 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1448 if ( hr != S_OK )
1449 return hr;
1452 * set the class ID
1454 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1455 IStorage_SetClass( pstgDest, &curElement.clsid );
1460 * Obtain the next element
1462 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1464 if ( hr == S_FALSE )
1466 hr = S_OK; /* done, every element has been copied */
1467 break;
1470 if (curElement.type == STGTY_STORAGE)
1473 * open child source storage
1475 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1476 STGM_READ|STGM_SHARE_EXCLUSIVE,
1477 NULL, 0, &pstgChild );
1479 if (hr != S_OK)
1480 break;
1483 * Check if destination storage is not a child of the source
1484 * storage, which will cause an infinite loop
1486 if (pstgChild == pstgDest)
1488 IEnumSTATSTG_Release(elements);
1490 return STG_E_ACCESSDENIED;
1494 * create a new storage in destination storage
1496 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1497 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1498 0, 0,
1499 &pstgTmp );
1501 * if it already exist, don't create a new one use this one
1503 if (hr == STG_E_FILEALREADYEXISTS)
1505 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1506 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1507 NULL, 0, &pstgTmp );
1510 if (hr != S_OK)
1511 break;
1515 * do the copy recursively
1517 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1518 snbExclude, pstgTmp );
1520 IStorage_Release( pstgTmp );
1521 IStorage_Release( pstgChild );
1523 else if (curElement.type == STGTY_STREAM)
1526 * create a new stream in destination storage. If the stream already
1527 * exist, it will be deleted and a new one will be created.
1529 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1530 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1531 0, 0, &pstrTmp );
1533 if (hr != S_OK)
1534 break;
1537 * open child stream storage
1539 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1540 STGM_READ|STGM_SHARE_EXCLUSIVE,
1541 0, &pstrChild );
1543 if (hr != S_OK)
1544 break;
1547 * Get the size of the source stream
1549 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1552 * Set the size of the destination stream.
1554 IStream_SetSize(pstrTmp, strStat.cbSize);
1557 * do the copy
1559 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1560 NULL, NULL );
1562 IStream_Release( pstrTmp );
1563 IStream_Release( pstrChild );
1565 else
1567 WARN("unknown element type: %ld\n", curElement.type);
1570 } while (hr == S_OK);
1573 * Clean-up
1575 IEnumSTATSTG_Release(elements);
1577 return hr;
1580 /*************************************************************************
1581 * MoveElementTo (IStorage)
1583 HRESULT WINAPI StorageImpl_MoveElementTo(
1584 IStorage* iface,
1585 const OLECHAR *pwcsName, /* [string][in] */
1586 IStorage *pstgDest, /* [unique][in] */
1587 const OLECHAR *pwcsNewName,/* [string][in] */
1588 DWORD grfFlags) /* [in] */
1590 FIXME("not implemented!\n");
1591 return E_NOTIMPL;
1594 /*************************************************************************
1595 * Commit (IStorage)
1597 HRESULT WINAPI StorageImpl_Commit(
1598 IStorage* iface,
1599 DWORD grfCommitFlags)/* [in] */
1601 FIXME("(%ld): stub!\n", grfCommitFlags);
1602 return S_OK;
1605 /*************************************************************************
1606 * Revert (IStorage)
1608 HRESULT WINAPI StorageImpl_Revert(
1609 IStorage* iface)
1611 FIXME("not implemented!\n");
1612 return E_NOTIMPL;
1615 /*************************************************************************
1616 * DestroyElement (IStorage)
1618 * Stategy: This implementation is build this way for simplicity not for speed.
1619 * I always delete the top most element of the enumeration and adjust
1620 * the deleted element pointer all the time. This takes longer to
1621 * do but allow to reinvoke DestroyElement whenever we encounter a
1622 * storage object. The optimisation reside in the usage of another
1623 * enumeration stategy that would give all the leaves of a storage
1624 * first. (postfix order)
1626 HRESULT WINAPI StorageImpl_DestroyElement(
1627 IStorage* iface,
1628 const OLECHAR *pwcsName)/* [string][in] */
1630 StorageImpl* const This=(StorageImpl*)iface;
1632 IEnumSTATSTGImpl* propertyEnumeration;
1633 HRESULT hr = S_OK;
1634 BOOL res;
1635 StgProperty propertyToDelete;
1636 StgProperty parentProperty;
1637 ULONG foundPropertyIndexToDelete;
1638 ULONG typeOfRelation;
1639 ULONG parentPropertyId;
1641 TRACE("(%p, %s)\n",
1642 iface, debugstr_w(pwcsName));
1645 * Perform a sanity check on the parameters.
1647 if (pwcsName==NULL)
1648 return STG_E_INVALIDPOINTER;
1651 * Create a property enumeration to search the property with the given name
1653 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1654 This->ancestorStorage,
1655 This->rootPropertySetIndex);
1657 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1658 propertyEnumeration,
1659 pwcsName,
1660 &propertyToDelete);
1662 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1664 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1666 return STG_E_FILENOTFOUND;
1670 * Find the parent property of the property to delete (the one that
1671 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1672 * the parent is This. Otherwise, the parent is one of it's sibling...
1676 * First, read This's StgProperty..
1678 res = StorageImpl_ReadProperty(
1679 This->ancestorStorage,
1680 This->rootPropertySetIndex,
1681 &parentProperty);
1683 assert(res==TRUE);
1686 * Second, check to see if by any chance the actual storage (This) is not
1687 * the parent of the property to delete... We never know...
1689 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1692 * Set data as it would have been done in the else part...
1694 typeOfRelation = PROPERTY_RELATION_DIR;
1695 parentPropertyId = This->rootPropertySetIndex;
1697 else
1700 * Create a property enumeration to search the parent properties, and
1701 * delete it once done.
1703 IEnumSTATSTGImpl* propertyEnumeration2;
1705 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1706 This->ancestorStorage,
1707 This->rootPropertySetIndex);
1709 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1710 propertyEnumeration2,
1711 foundPropertyIndexToDelete,
1712 &parentProperty,
1713 &parentPropertyId);
1715 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1718 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1720 hr = deleteStorageProperty(
1721 This,
1722 foundPropertyIndexToDelete,
1723 propertyToDelete);
1725 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1727 hr = deleteStreamProperty(
1728 This,
1729 foundPropertyIndexToDelete,
1730 propertyToDelete);
1733 if (hr!=S_OK)
1734 return hr;
1737 * Adjust the property chain
1739 hr = adjustPropertyChain(
1740 This,
1741 propertyToDelete,
1742 parentProperty,
1743 parentPropertyId,
1744 typeOfRelation);
1746 return hr;
1750 /************************************************************************
1751 * StorageImpl_Stat (IStorage)
1753 * This method will retrieve information about this storage object.
1755 * See Windows documentation for more details on IStorage methods.
1757 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1758 STATSTG* pstatstg, /* [out] */
1759 DWORD grfStatFlag) /* [in] */
1761 StorageImpl* const This = (StorageImpl*)iface;
1762 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1764 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1766 CoTaskMemFree(pstatstg->pwcsName);
1767 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1768 strcpyW(pstatstg->pwcsName, This->pwcsName);
1771 return result;
1776 /*********************************************************************
1778 * Internal Method
1780 * Perform the deletion of a complete storage node
1783 static HRESULT deleteStorageProperty(
1784 StorageImpl *parentStorage,
1785 ULONG indexOfPropertyToDelete,
1786 StgProperty propertyToDelete)
1788 IEnumSTATSTG *elements = 0;
1789 IStorage *childStorage = 0;
1790 STATSTG currentElement;
1791 HRESULT hr;
1792 HRESULT destroyHr = S_OK;
1795 * Open the storage and enumerate it
1797 hr = StorageBaseImpl_OpenStorage(
1798 (IStorage*)parentStorage,
1799 propertyToDelete.name,
1801 STGM_SHARE_EXCLUSIVE,
1804 &childStorage);
1806 if (hr != S_OK)
1808 return hr;
1812 * Enumerate the elements
1814 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1819 * Obtain the next element
1821 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1822 if (hr==S_OK)
1824 destroyHr = StorageImpl_DestroyElement(
1825 (IStorage*)childStorage,
1826 (OLECHAR*)currentElement.pwcsName);
1828 CoTaskMemFree(currentElement.pwcsName);
1832 * We need to Reset the enumeration every time because we delete elements
1833 * and the enumeration could be invalid
1835 IEnumSTATSTG_Reset(elements);
1837 } while ((hr == S_OK) && (destroyHr == S_OK));
1840 * Invalidate the property by zeroing it's name member.
1842 propertyToDelete.sizeOfNameString = 0;
1844 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1845 indexOfPropertyToDelete,
1846 &propertyToDelete);
1848 IStorage_Release(childStorage);
1849 IEnumSTATSTG_Release(elements);
1851 return destroyHr;
1854 /*********************************************************************
1856 * Internal Method
1858 * Perform the deletion of a stream node
1861 static HRESULT deleteStreamProperty(
1862 StorageImpl *parentStorage,
1863 ULONG indexOfPropertyToDelete,
1864 StgProperty propertyToDelete)
1866 IStream *pis;
1867 HRESULT hr;
1868 ULARGE_INTEGER size;
1870 size.u.HighPart = 0;
1871 size.u.LowPart = 0;
1873 hr = StorageBaseImpl_OpenStream(
1874 (IStorage*)parentStorage,
1875 (OLECHAR*)propertyToDelete.name,
1876 NULL,
1877 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1879 &pis);
1881 if (hr!=S_OK)
1883 return(hr);
1887 * Zap the stream
1889 hr = IStream_SetSize(pis, size);
1891 if(hr != S_OK)
1893 return hr;
1897 * Release the stream object.
1899 IStream_Release(pis);
1902 * Invalidate the property by zeroing it's name member.
1904 propertyToDelete.sizeOfNameString = 0;
1907 * Here we should re-read the property so we get the updated pointer
1908 * but since we are here to zap it, I don't do it...
1910 StorageImpl_WriteProperty(
1911 parentStorage->ancestorStorage,
1912 indexOfPropertyToDelete,
1913 &propertyToDelete);
1915 return S_OK;
1918 /*********************************************************************
1920 * Internal Method
1922 * Finds a placeholder for the StgProperty within the Storage
1925 static HRESULT findPlaceholder(
1926 StorageImpl *storage,
1927 ULONG propertyIndexToStore,
1928 ULONG storePropertyIndex,
1929 INT typeOfRelation)
1931 StgProperty storeProperty;
1932 HRESULT hr = S_OK;
1933 BOOL res = TRUE;
1936 * Read the storage property
1938 res = StorageImpl_ReadProperty(
1939 storage->ancestorStorage,
1940 storePropertyIndex,
1941 &storeProperty);
1943 if(! res)
1945 return E_FAIL;
1948 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1950 if (storeProperty.previousProperty != PROPERTY_NULL)
1952 return findPlaceholder(
1953 storage,
1954 propertyIndexToStore,
1955 storeProperty.previousProperty,
1956 typeOfRelation);
1958 else
1960 storeProperty.previousProperty = propertyIndexToStore;
1963 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1965 if (storeProperty.nextProperty != PROPERTY_NULL)
1967 return findPlaceholder(
1968 storage,
1969 propertyIndexToStore,
1970 storeProperty.nextProperty,
1971 typeOfRelation);
1973 else
1975 storeProperty.nextProperty = propertyIndexToStore;
1978 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1980 if (storeProperty.dirProperty != PROPERTY_NULL)
1982 return findPlaceholder(
1983 storage,
1984 propertyIndexToStore,
1985 storeProperty.dirProperty,
1986 typeOfRelation);
1988 else
1990 storeProperty.dirProperty = propertyIndexToStore;
1994 hr = StorageImpl_WriteProperty(
1995 storage->ancestorStorage,
1996 storePropertyIndex,
1997 &storeProperty);
1999 if(! hr)
2001 return E_FAIL;
2004 return S_OK;
2007 /*************************************************************************
2009 * Internal Method
2011 * This method takes the previous and the next property link of a property
2012 * to be deleted and find them a place in the Storage.
2014 static HRESULT adjustPropertyChain(
2015 StorageImpl *This,
2016 StgProperty propertyToDelete,
2017 StgProperty parentProperty,
2018 ULONG parentPropertyId,
2019 INT typeOfRelation)
2021 ULONG newLinkProperty = PROPERTY_NULL;
2022 BOOL needToFindAPlaceholder = FALSE;
2023 ULONG storeNode = PROPERTY_NULL;
2024 ULONG toStoreNode = PROPERTY_NULL;
2025 INT relationType = 0;
2026 HRESULT hr = S_OK;
2027 BOOL res = TRUE;
2029 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2031 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2034 * Set the parent previous to the property to delete previous
2036 newLinkProperty = propertyToDelete.previousProperty;
2038 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2041 * We also need to find a storage for the other link, setup variables
2042 * to do this at the end...
2044 needToFindAPlaceholder = TRUE;
2045 storeNode = propertyToDelete.previousProperty;
2046 toStoreNode = propertyToDelete.nextProperty;
2047 relationType = PROPERTY_RELATION_NEXT;
2050 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2053 * Set the parent previous to the property to delete next
2055 newLinkProperty = propertyToDelete.nextProperty;
2059 * Link it for real...
2061 parentProperty.previousProperty = newLinkProperty;
2064 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2066 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2069 * Set the parent next to the property to delete next previous
2071 newLinkProperty = propertyToDelete.previousProperty;
2073 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2076 * We also need to find a storage for the other link, setup variables
2077 * to do this at the end...
2079 needToFindAPlaceholder = TRUE;
2080 storeNode = propertyToDelete.previousProperty;
2081 toStoreNode = propertyToDelete.nextProperty;
2082 relationType = PROPERTY_RELATION_NEXT;
2085 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2088 * Set the parent next to the property to delete next
2090 newLinkProperty = propertyToDelete.nextProperty;
2094 * Link it for real...
2096 parentProperty.nextProperty = newLinkProperty;
2098 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2100 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2103 * Set the parent dir to the property to delete previous
2105 newLinkProperty = propertyToDelete.previousProperty;
2107 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2110 * We also need to find a storage for the other link, setup variables
2111 * to do this at the end...
2113 needToFindAPlaceholder = TRUE;
2114 storeNode = propertyToDelete.previousProperty;
2115 toStoreNode = propertyToDelete.nextProperty;
2116 relationType = PROPERTY_RELATION_NEXT;
2119 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2122 * Set the parent dir to the property to delete next
2124 newLinkProperty = propertyToDelete.nextProperty;
2128 * Link it for real...
2130 parentProperty.dirProperty = newLinkProperty;
2134 * Write back the parent property
2136 res = StorageImpl_WriteProperty(
2137 This->ancestorStorage,
2138 parentPropertyId,
2139 &parentProperty);
2140 if(! res)
2142 return E_FAIL;
2146 * If a placeholder is required for the other link, then, find one and
2147 * get out of here...
2149 if (needToFindAPlaceholder)
2151 hr = findPlaceholder(
2152 This,
2153 toStoreNode,
2154 storeNode,
2155 relationType);
2158 return hr;
2162 /******************************************************************************
2163 * SetElementTimes (IStorage)
2165 HRESULT WINAPI StorageImpl_SetElementTimes(
2166 IStorage* iface,
2167 const OLECHAR *pwcsName,/* [string][in] */
2168 const FILETIME *pctime, /* [in] */
2169 const FILETIME *patime, /* [in] */
2170 const FILETIME *pmtime) /* [in] */
2172 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2173 return S_OK;
2176 /******************************************************************************
2177 * SetStateBits (IStorage)
2179 HRESULT WINAPI StorageImpl_SetStateBits(
2180 IStorage* iface,
2181 DWORD grfStateBits,/* [in] */
2182 DWORD grfMask) /* [in] */
2184 FIXME("not implemented!\n");
2185 return E_NOTIMPL;
2188 HRESULT StorageImpl_Construct(
2189 StorageImpl* This,
2190 HANDLE hFile,
2191 LPCOLESTR pwcsName,
2192 ILockBytes* pLkbyt,
2193 DWORD openFlags,
2194 BOOL fileBased,
2195 BOOL fileCreate)
2197 HRESULT hr = S_OK;
2198 StgProperty currentProperty;
2199 BOOL readSuccessful;
2200 ULONG currentPropertyIndex;
2202 if ( FAILED( validateSTGM(openFlags) ))
2203 return STG_E_INVALIDFLAG;
2205 memset(This, 0, sizeof(StorageImpl));
2208 * Initialize the virtual function table.
2210 This->lpVtbl = &Storage32Impl_Vtbl;
2211 This->v_destructor = &StorageImpl_Destroy;
2214 * This is the top-level storage so initialize the ancestor pointer
2215 * to this.
2217 This->ancestorStorage = This;
2220 * Initialize the physical support of the storage.
2222 This->hFile = hFile;
2225 * Store copy of file path.
2227 if(pwcsName) {
2228 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2229 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2230 if (!This->pwcsName)
2231 return STG_E_INSUFFICIENTMEMORY;
2232 strcpyW(This->pwcsName, pwcsName);
2236 * Initialize the big block cache.
2238 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2239 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2240 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2241 pLkbyt,
2242 openFlags,
2243 This->bigBlockSize,
2244 fileBased);
2246 if (This->bigBlockFile == 0)
2247 return E_FAIL;
2249 if (fileCreate)
2251 ULARGE_INTEGER size;
2252 BYTE* bigBlockBuffer;
2255 * Initialize all header variables:
2256 * - The big block depot consists of one block and it is at block 0
2257 * - The properties start at block 1
2258 * - There is no small block depot
2260 memset( This->bigBlockDepotStart,
2261 BLOCK_UNUSED,
2262 sizeof(This->bigBlockDepotStart));
2264 This->bigBlockDepotCount = 1;
2265 This->bigBlockDepotStart[0] = 0;
2266 This->rootStartBlock = 1;
2267 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2268 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2269 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2270 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2271 This->extBigBlockDepotCount = 0;
2273 StorageImpl_SaveFileHeader(This);
2276 * Add one block for the big block depot and one block for the properties
2278 size.u.HighPart = 0;
2279 size.u.LowPart = This->bigBlockSize * 3;
2280 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2283 * Initialize the big block depot
2285 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2286 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2287 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2288 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2289 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2291 else
2294 * Load the header for the file.
2296 hr = StorageImpl_LoadFileHeader(This);
2298 if (FAILED(hr))
2300 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2302 return hr;
2307 * There is no block depot cached yet.
2309 This->indexBlockDepotCached = 0xFFFFFFFF;
2312 * Start searching for free blocks with block 0.
2314 This->prevFreeBlock = 0;
2317 * Create the block chain abstractions.
2319 if(!(This->rootBlockChain =
2320 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2321 return STG_E_READFAULT;
2323 if(!(This->smallBlockDepotChain =
2324 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2325 PROPERTY_NULL)))
2326 return STG_E_READFAULT;
2329 * Write the root property
2331 if (fileCreate)
2333 StgProperty rootProp;
2335 * Initialize the property chain
2337 memset(&rootProp, 0, sizeof(rootProp));
2338 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2339 sizeof(rootProp.name)/sizeof(WCHAR) );
2340 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2341 rootProp.propertyType = PROPTYPE_ROOT;
2342 rootProp.previousProperty = PROPERTY_NULL;
2343 rootProp.nextProperty = PROPERTY_NULL;
2344 rootProp.dirProperty = PROPERTY_NULL;
2345 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2346 rootProp.size.u.HighPart = 0;
2347 rootProp.size.u.LowPart = 0;
2349 StorageImpl_WriteProperty(This, 0, &rootProp);
2353 * Find the ID of the root in the property sets.
2355 currentPropertyIndex = 0;
2359 readSuccessful = StorageImpl_ReadProperty(
2360 This,
2361 currentPropertyIndex,
2362 &currentProperty);
2364 if (readSuccessful)
2366 if ( (currentProperty.sizeOfNameString != 0 ) &&
2367 (currentProperty.propertyType == PROPTYPE_ROOT) )
2369 This->rootPropertySetIndex = currentPropertyIndex;
2373 currentPropertyIndex++;
2375 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2377 if (!readSuccessful)
2379 /* TODO CLEANUP */
2380 return STG_E_READFAULT;
2384 * Create the block chain abstraction for the small block root chain.
2386 if(!(This->smallBlockRootChain =
2387 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2388 return STG_E_READFAULT;
2390 return hr;
2393 void StorageImpl_Destroy(
2394 StorageImpl* This)
2396 TRACE("(%p)\n", This);
2398 if(This->pwcsName)
2399 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2401 BlockChainStream_Destroy(This->smallBlockRootChain);
2402 BlockChainStream_Destroy(This->rootBlockChain);
2403 BlockChainStream_Destroy(This->smallBlockDepotChain);
2405 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2406 return;
2409 /******************************************************************************
2410 * Storage32Impl_GetNextFreeBigBlock
2412 * Returns the index of the next free big block.
2413 * If the big block depot is filled, this method will enlarge it.
2416 ULONG StorageImpl_GetNextFreeBigBlock(
2417 StorageImpl* This)
2419 ULONG depotBlockIndexPos;
2420 void *depotBuffer;
2421 ULONG depotBlockOffset;
2422 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2423 ULONG nextBlockIndex = BLOCK_SPECIAL;
2424 int depotIndex = 0;
2425 ULONG freeBlock = BLOCK_UNUSED;
2427 depotIndex = This->prevFreeBlock / blocksPerDepot;
2428 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2431 * Scan the entire big block depot until we find a block marked free
2433 while (nextBlockIndex != BLOCK_UNUSED)
2435 if (depotIndex < COUNT_BBDEPOTINHEADER)
2437 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2440 * Grow the primary depot.
2442 if (depotBlockIndexPos == BLOCK_UNUSED)
2444 depotBlockIndexPos = depotIndex*blocksPerDepot;
2447 * Add a block depot.
2449 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2450 This->bigBlockDepotCount++;
2451 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2454 * Flag it as a block depot.
2456 StorageImpl_SetNextBlockInChain(This,
2457 depotBlockIndexPos,
2458 BLOCK_SPECIAL);
2460 /* Save new header information.
2462 StorageImpl_SaveFileHeader(This);
2465 else
2467 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2469 if (depotBlockIndexPos == BLOCK_UNUSED)
2472 * Grow the extended depot.
2474 ULONG extIndex = BLOCK_UNUSED;
2475 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2476 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2478 if (extBlockOffset == 0)
2480 /* We need an extended block.
2482 extIndex = Storage32Impl_AddExtBlockDepot(This);
2483 This->extBigBlockDepotCount++;
2484 depotBlockIndexPos = extIndex + 1;
2486 else
2487 depotBlockIndexPos = depotIndex * blocksPerDepot;
2490 * Add a block depot and mark it in the extended block.
2492 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2493 This->bigBlockDepotCount++;
2494 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2496 /* Flag the block depot.
2498 StorageImpl_SetNextBlockInChain(This,
2499 depotBlockIndexPos,
2500 BLOCK_SPECIAL);
2502 /* If necessary, flag the extended depot block.
2504 if (extIndex != BLOCK_UNUSED)
2505 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2507 /* Save header information.
2509 StorageImpl_SaveFileHeader(This);
2513 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2515 if (depotBuffer != 0)
2517 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2518 ( nextBlockIndex != BLOCK_UNUSED))
2520 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2522 if (nextBlockIndex == BLOCK_UNUSED)
2524 freeBlock = (depotIndex * blocksPerDepot) +
2525 (depotBlockOffset/sizeof(ULONG));
2528 depotBlockOffset += sizeof(ULONG);
2531 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2534 depotIndex++;
2535 depotBlockOffset = 0;
2538 This->prevFreeBlock = freeBlock;
2540 return freeBlock;
2543 /******************************************************************************
2544 * Storage32Impl_AddBlockDepot
2546 * This will create a depot block, essentially it is a block initialized
2547 * to BLOCK_UNUSEDs.
2549 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2551 BYTE* blockBuffer;
2553 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2556 * Initialize blocks as free
2558 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2560 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2563 /******************************************************************************
2564 * Storage32Impl_GetExtDepotBlock
2566 * Returns the index of the block that corresponds to the specified depot
2567 * index. This method is only for depot indexes equal or greater than
2568 * COUNT_BBDEPOTINHEADER.
2570 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2572 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2573 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2574 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2575 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2576 ULONG blockIndex = BLOCK_UNUSED;
2577 ULONG extBlockIndex = This->extBigBlockDepotStart;
2579 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2581 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2582 return BLOCK_UNUSED;
2584 while (extBlockCount > 0)
2586 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2587 extBlockCount--;
2590 if (extBlockIndex != BLOCK_UNUSED)
2592 BYTE* depotBuffer;
2594 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2596 if (depotBuffer != 0)
2598 StorageUtl_ReadDWord(depotBuffer,
2599 extBlockOffset * sizeof(ULONG),
2600 &blockIndex);
2602 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2606 return blockIndex;
2609 /******************************************************************************
2610 * Storage32Impl_SetExtDepotBlock
2612 * Associates the specified block index to the specified depot index.
2613 * This method is only for depot indexes equal or greater than
2614 * COUNT_BBDEPOTINHEADER.
2616 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2617 ULONG depotIndex,
2618 ULONG blockIndex)
2620 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2621 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2622 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2623 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2624 ULONG extBlockIndex = This->extBigBlockDepotStart;
2626 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2628 while (extBlockCount > 0)
2630 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2631 extBlockCount--;
2634 if (extBlockIndex != BLOCK_UNUSED)
2636 BYTE* depotBuffer;
2638 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2640 if (depotBuffer != 0)
2642 StorageUtl_WriteDWord(depotBuffer,
2643 extBlockOffset * sizeof(ULONG),
2644 blockIndex);
2646 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2651 /******************************************************************************
2652 * Storage32Impl_AddExtBlockDepot
2654 * Creates an extended depot block.
2656 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2658 ULONG numExtBlocks = This->extBigBlockDepotCount;
2659 ULONG nextExtBlock = This->extBigBlockDepotStart;
2660 BYTE* depotBuffer = NULL;
2661 ULONG index = BLOCK_UNUSED;
2662 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2663 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2664 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2666 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2667 blocksPerDepotBlock;
2669 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2672 * The first extended block.
2674 This->extBigBlockDepotStart = index;
2676 else
2678 unsigned int i;
2680 * Follow the chain to the last one.
2682 for (i = 0; i < (numExtBlocks - 1); i++)
2684 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2688 * Add the new extended block to the chain.
2690 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2691 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2692 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2696 * Initialize this block.
2698 depotBuffer = StorageImpl_GetBigBlock(This, index);
2699 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2700 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2702 return index;
2705 /******************************************************************************
2706 * Storage32Impl_FreeBigBlock
2708 * This method will flag the specified block as free in the big block depot.
2710 void StorageImpl_FreeBigBlock(
2711 StorageImpl* This,
2712 ULONG blockIndex)
2714 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2716 if (blockIndex < This->prevFreeBlock)
2717 This->prevFreeBlock = blockIndex;
2720 /************************************************************************
2721 * Storage32Impl_GetNextBlockInChain
2723 * This method will retrieve the block index of the next big block in
2724 * in the chain.
2726 * Params: This - Pointer to the Storage object.
2727 * blockIndex - Index of the block to retrieve the chain
2728 * for.
2729 * nextBlockIndex - receives the return value.
2731 * Returns: This method returns the index of the next block in the chain.
2732 * It will return the constants:
2733 * BLOCK_SPECIAL - If the block given was not part of a
2734 * chain.
2735 * BLOCK_END_OF_CHAIN - If the block given was the last in
2736 * a chain.
2737 * BLOCK_UNUSED - If the block given was not past of a chain
2738 * and is available.
2739 * BLOCK_EXTBBDEPOT - This block is part of the extended
2740 * big block depot.
2742 * See Windows documentation for more details on IStorage methods.
2744 HRESULT StorageImpl_GetNextBlockInChain(
2745 StorageImpl* This,
2746 ULONG blockIndex,
2747 ULONG* nextBlockIndex)
2749 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2750 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2751 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2752 void* depotBuffer;
2753 ULONG depotBlockIndexPos;
2754 int index;
2756 *nextBlockIndex = BLOCK_SPECIAL;
2758 if(depotBlockCount >= This->bigBlockDepotCount)
2760 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2761 This->bigBlockDepotCount);
2762 return STG_E_READFAULT;
2766 * Cache the currently accessed depot block.
2768 if (depotBlockCount != This->indexBlockDepotCached)
2770 This->indexBlockDepotCached = depotBlockCount;
2772 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2774 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2776 else
2779 * We have to look in the extended depot.
2781 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2784 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2786 if (!depotBuffer)
2787 return STG_E_READFAULT;
2789 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2791 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2792 This->blockDepotCached[index] = *nextBlockIndex;
2794 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2797 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2799 return S_OK;
2802 /******************************************************************************
2803 * Storage32Impl_GetNextExtendedBlock
2805 * Given an extended block this method will return the next extended block.
2807 * NOTES:
2808 * The last ULONG of an extended block is the block index of the next
2809 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2810 * depot.
2812 * Return values:
2813 * - The index of the next extended block
2814 * - BLOCK_UNUSED: there is no next extended block.
2815 * - Any other return values denotes failure.
2817 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2819 ULONG nextBlockIndex = BLOCK_SPECIAL;
2820 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2821 void* depotBuffer;
2823 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2825 if (depotBuffer!=0)
2827 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2829 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2832 return nextBlockIndex;
2835 /******************************************************************************
2836 * Storage32Impl_SetNextBlockInChain
2838 * This method will write the index of the specified block's next block
2839 * in the big block depot.
2841 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2842 * do the following
2844 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2845 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2846 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2849 void StorageImpl_SetNextBlockInChain(
2850 StorageImpl* This,
2851 ULONG blockIndex,
2852 ULONG nextBlock)
2854 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2855 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2856 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2857 ULONG depotBlockIndexPos;
2858 void* depotBuffer;
2860 assert(depotBlockCount < This->bigBlockDepotCount);
2861 assert(blockIndex != nextBlock);
2863 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2865 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2867 else
2870 * We have to look in the extended depot.
2872 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2875 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2877 if (depotBuffer!=0)
2879 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2880 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2884 * Update the cached block depot, if necessary.
2886 if (depotBlockCount == This->indexBlockDepotCached)
2888 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2892 /******************************************************************************
2893 * Storage32Impl_LoadFileHeader
2895 * This method will read in the file header, i.e. big block index -1.
2897 HRESULT StorageImpl_LoadFileHeader(
2898 StorageImpl* This)
2900 HRESULT hr = STG_E_FILENOTFOUND;
2901 void* headerBigBlock = NULL;
2902 int index;
2905 * Get a pointer to the big block of data containing the header.
2907 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2910 * Extract the information from the header.
2912 if (headerBigBlock!=0)
2915 * Check for the "magic number" signature and return an error if it is not
2916 * found.
2918 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2920 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2921 return STG_E_OLDFORMAT;
2924 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2926 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2927 return STG_E_INVALIDHEADER;
2930 StorageUtl_ReadWord(
2931 headerBigBlock,
2932 OFFSET_BIGBLOCKSIZEBITS,
2933 &This->bigBlockSizeBits);
2935 StorageUtl_ReadWord(
2936 headerBigBlock,
2937 OFFSET_SMALLBLOCKSIZEBITS,
2938 &This->smallBlockSizeBits);
2940 StorageUtl_ReadDWord(
2941 headerBigBlock,
2942 OFFSET_BBDEPOTCOUNT,
2943 &This->bigBlockDepotCount);
2945 StorageUtl_ReadDWord(
2946 headerBigBlock,
2947 OFFSET_ROOTSTARTBLOCK,
2948 &This->rootStartBlock);
2950 StorageUtl_ReadDWord(
2951 headerBigBlock,
2952 OFFSET_SBDEPOTSTART,
2953 &This->smallBlockDepotStart);
2955 StorageUtl_ReadDWord(
2956 headerBigBlock,
2957 OFFSET_EXTBBDEPOTSTART,
2958 &This->extBigBlockDepotStart);
2960 StorageUtl_ReadDWord(
2961 headerBigBlock,
2962 OFFSET_EXTBBDEPOTCOUNT,
2963 &This->extBigBlockDepotCount);
2965 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2967 StorageUtl_ReadDWord(
2968 headerBigBlock,
2969 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2970 &(This->bigBlockDepotStart[index]));
2974 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2976 if ((1 << 2) == 4)
2978 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2979 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2981 else
2983 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2984 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2988 * Right now, the code is making some assumptions about the size of the
2989 * blocks, just make sure they are what we're expecting.
2991 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2992 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2994 WARN("Broken OLE storage file\n");
2995 hr = STG_E_INVALIDHEADER;
2997 else
2998 hr = S_OK;
3001 * Release the block.
3003 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3006 return hr;
3009 /******************************************************************************
3010 * Storage32Impl_SaveFileHeader
3012 * This method will save to the file the header, i.e. big block -1.
3014 void StorageImpl_SaveFileHeader(
3015 StorageImpl* This)
3017 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3018 int index;
3019 BOOL success;
3022 * Get a pointer to the big block of data containing the header.
3024 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3027 * If the block read failed, the file is probably new.
3029 if (!success)
3032 * Initialize for all unknown fields.
3034 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3037 * Initialize the magic number.
3039 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3042 * And a bunch of things we don't know what they mean
3044 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3045 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3046 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3047 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3051 * Write the information to the header.
3053 StorageUtl_WriteWord(
3054 headerBigBlock,
3055 OFFSET_BIGBLOCKSIZEBITS,
3056 This->bigBlockSizeBits);
3058 StorageUtl_WriteWord(
3059 headerBigBlock,
3060 OFFSET_SMALLBLOCKSIZEBITS,
3061 This->smallBlockSizeBits);
3063 StorageUtl_WriteDWord(
3064 headerBigBlock,
3065 OFFSET_BBDEPOTCOUNT,
3066 This->bigBlockDepotCount);
3068 StorageUtl_WriteDWord(
3069 headerBigBlock,
3070 OFFSET_ROOTSTARTBLOCK,
3071 This->rootStartBlock);
3073 StorageUtl_WriteDWord(
3074 headerBigBlock,
3075 OFFSET_SBDEPOTSTART,
3076 This->smallBlockDepotStart);
3078 StorageUtl_WriteDWord(
3079 headerBigBlock,
3080 OFFSET_SBDEPOTCOUNT,
3081 This->smallBlockDepotChain ?
3082 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3084 StorageUtl_WriteDWord(
3085 headerBigBlock,
3086 OFFSET_EXTBBDEPOTSTART,
3087 This->extBigBlockDepotStart);
3089 StorageUtl_WriteDWord(
3090 headerBigBlock,
3091 OFFSET_EXTBBDEPOTCOUNT,
3092 This->extBigBlockDepotCount);
3094 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3096 StorageUtl_WriteDWord(
3097 headerBigBlock,
3098 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3099 (This->bigBlockDepotStart[index]));
3103 * Write the big block back to the file.
3105 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3108 /******************************************************************************
3109 * Storage32Impl_ReadProperty
3111 * This method will read the specified property from the property chain.
3113 BOOL StorageImpl_ReadProperty(
3114 StorageImpl* This,
3115 ULONG index,
3116 StgProperty* buffer)
3118 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3119 ULARGE_INTEGER offsetInPropSet;
3120 BOOL readSuccessful;
3121 ULONG bytesRead;
3123 offsetInPropSet.u.HighPart = 0;
3124 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3126 readSuccessful = BlockChainStream_ReadAt(
3127 This->rootBlockChain,
3128 offsetInPropSet,
3129 PROPSET_BLOCK_SIZE,
3130 currentProperty,
3131 &bytesRead);
3133 if (readSuccessful)
3135 /* replace the name of root entry (often "Root Entry") by the file name */
3136 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3137 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3139 memset(buffer->name, 0, sizeof(buffer->name));
3140 memcpy(
3141 buffer->name,
3142 propName,
3143 PROPERTY_NAME_BUFFER_LEN );
3144 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3146 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3148 StorageUtl_ReadWord(
3149 currentProperty,
3150 OFFSET_PS_NAMELENGTH,
3151 &buffer->sizeOfNameString);
3153 StorageUtl_ReadDWord(
3154 currentProperty,
3155 OFFSET_PS_PREVIOUSPROP,
3156 &buffer->previousProperty);
3158 StorageUtl_ReadDWord(
3159 currentProperty,
3160 OFFSET_PS_NEXTPROP,
3161 &buffer->nextProperty);
3163 StorageUtl_ReadDWord(
3164 currentProperty,
3165 OFFSET_PS_DIRPROP,
3166 &buffer->dirProperty);
3168 StorageUtl_ReadGUID(
3169 currentProperty,
3170 OFFSET_PS_GUID,
3171 &buffer->propertyUniqueID);
3173 StorageUtl_ReadDWord(
3174 currentProperty,
3175 OFFSET_PS_TSS1,
3176 &buffer->timeStampS1);
3178 StorageUtl_ReadDWord(
3179 currentProperty,
3180 OFFSET_PS_TSD1,
3181 &buffer->timeStampD1);
3183 StorageUtl_ReadDWord(
3184 currentProperty,
3185 OFFSET_PS_TSS2,
3186 &buffer->timeStampS2);
3188 StorageUtl_ReadDWord(
3189 currentProperty,
3190 OFFSET_PS_TSD2,
3191 &buffer->timeStampD2);
3193 StorageUtl_ReadDWord(
3194 currentProperty,
3195 OFFSET_PS_STARTBLOCK,
3196 &buffer->startingBlock);
3198 StorageUtl_ReadDWord(
3199 currentProperty,
3200 OFFSET_PS_SIZE,
3201 &buffer->size.u.LowPart);
3203 buffer->size.u.HighPart = 0;
3206 return readSuccessful;
3209 /*********************************************************************
3210 * Write the specified property into the property chain
3212 BOOL StorageImpl_WriteProperty(
3213 StorageImpl* This,
3214 ULONG index,
3215 StgProperty* buffer)
3217 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3218 ULARGE_INTEGER offsetInPropSet;
3219 BOOL writeSuccessful;
3220 ULONG bytesWritten;
3222 offsetInPropSet.u.HighPart = 0;
3223 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3225 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3227 memcpy(
3228 currentProperty + OFFSET_PS_NAME,
3229 buffer->name,
3230 PROPERTY_NAME_BUFFER_LEN );
3232 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3234 StorageUtl_WriteWord(
3235 currentProperty,
3236 OFFSET_PS_NAMELENGTH,
3237 buffer->sizeOfNameString);
3239 StorageUtl_WriteDWord(
3240 currentProperty,
3241 OFFSET_PS_PREVIOUSPROP,
3242 buffer->previousProperty);
3244 StorageUtl_WriteDWord(
3245 currentProperty,
3246 OFFSET_PS_NEXTPROP,
3247 buffer->nextProperty);
3249 StorageUtl_WriteDWord(
3250 currentProperty,
3251 OFFSET_PS_DIRPROP,
3252 buffer->dirProperty);
3254 StorageUtl_WriteGUID(
3255 currentProperty,
3256 OFFSET_PS_GUID,
3257 &buffer->propertyUniqueID);
3259 StorageUtl_WriteDWord(
3260 currentProperty,
3261 OFFSET_PS_TSS1,
3262 buffer->timeStampS1);
3264 StorageUtl_WriteDWord(
3265 currentProperty,
3266 OFFSET_PS_TSD1,
3267 buffer->timeStampD1);
3269 StorageUtl_WriteDWord(
3270 currentProperty,
3271 OFFSET_PS_TSS2,
3272 buffer->timeStampS2);
3274 StorageUtl_WriteDWord(
3275 currentProperty,
3276 OFFSET_PS_TSD2,
3277 buffer->timeStampD2);
3279 StorageUtl_WriteDWord(
3280 currentProperty,
3281 OFFSET_PS_STARTBLOCK,
3282 buffer->startingBlock);
3284 StorageUtl_WriteDWord(
3285 currentProperty,
3286 OFFSET_PS_SIZE,
3287 buffer->size.u.LowPart);
3289 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3290 offsetInPropSet,
3291 PROPSET_BLOCK_SIZE,
3292 currentProperty,
3293 &bytesWritten);
3294 return writeSuccessful;
3297 BOOL StorageImpl_ReadBigBlock(
3298 StorageImpl* This,
3299 ULONG blockIndex,
3300 void* buffer)
3302 void* bigBlockBuffer;
3304 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3306 if (bigBlockBuffer!=0)
3308 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3310 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3312 return TRUE;
3315 return FALSE;
3318 BOOL StorageImpl_WriteBigBlock(
3319 StorageImpl* This,
3320 ULONG blockIndex,
3321 void* buffer)
3323 void* bigBlockBuffer;
3325 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3327 if (bigBlockBuffer!=0)
3329 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3331 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3333 return TRUE;
3336 return FALSE;
3339 void* StorageImpl_GetROBigBlock(
3340 StorageImpl* This,
3341 ULONG blockIndex)
3343 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3346 void* StorageImpl_GetBigBlock(
3347 StorageImpl* This,
3348 ULONG blockIndex)
3350 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3353 void StorageImpl_ReleaseBigBlock(
3354 StorageImpl* This,
3355 void* pBigBlock)
3357 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3360 /******************************************************************************
3361 * Storage32Impl_SmallBlocksToBigBlocks
3363 * This method will convert a small block chain to a big block chain.
3364 * The small block chain will be destroyed.
3366 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3367 StorageImpl* This,
3368 SmallBlockChainStream** ppsbChain)
3370 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3371 ULARGE_INTEGER size, offset;
3372 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3373 ULONG propertyIndex;
3374 BOOL successRead, successWrite;
3375 StgProperty chainProperty;
3376 BYTE *buffer;
3377 BlockChainStream *bbTempChain = NULL;
3378 BlockChainStream *bigBlockChain = NULL;
3381 * Create a temporary big block chain that doesn't have
3382 * an associated property. This temporary chain will be
3383 * used to copy data from small blocks to big blocks.
3385 bbTempChain = BlockChainStream_Construct(This,
3386 &bbHeadOfChain,
3387 PROPERTY_NULL);
3388 if(!bbTempChain) return NULL;
3390 * Grow the big block chain.
3392 size = SmallBlockChainStream_GetSize(*ppsbChain);
3393 BlockChainStream_SetSize(bbTempChain, size);
3396 * Copy the contents of the small block chain to the big block chain
3397 * by small block size increments.
3399 offset.u.LowPart = 0;
3400 offset.u.HighPart = 0;
3401 cbTotalRead = 0;
3402 cbTotalWritten = 0;
3404 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3407 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3408 offset,
3409 DEF_SMALL_BLOCK_SIZE,
3410 buffer,
3411 &cbRead);
3412 cbTotalRead += cbRead;
3414 successWrite = BlockChainStream_WriteAt(bbTempChain,
3415 offset,
3416 cbRead,
3417 buffer,
3418 &cbWritten);
3419 cbTotalWritten += cbWritten;
3421 offset.u.LowPart += This->smallBlockSize;
3423 } while (successRead && successWrite);
3424 HeapFree(GetProcessHeap(),0,buffer);
3426 assert(cbTotalRead == cbTotalWritten);
3429 * Destroy the small block chain.
3431 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3432 size.u.HighPart = 0;
3433 size.u.LowPart = 0;
3434 SmallBlockChainStream_SetSize(*ppsbChain, size);
3435 SmallBlockChainStream_Destroy(*ppsbChain);
3436 *ppsbChain = 0;
3439 * Change the property information. This chain is now a big block chain
3440 * and it doesn't reside in the small blocks chain anymore.
3442 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3444 chainProperty.startingBlock = bbHeadOfChain;
3446 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3449 * Destroy the temporary propertyless big block chain.
3450 * Create a new big block chain associated with this property.
3452 BlockChainStream_Destroy(bbTempChain);
3453 bigBlockChain = BlockChainStream_Construct(This,
3454 NULL,
3455 propertyIndex);
3457 return bigBlockChain;
3460 /******************************************************************************
3461 ** Storage32InternalImpl implementation
3464 StorageInternalImpl* StorageInternalImpl_Construct(
3465 StorageImpl* ancestorStorage,
3466 ULONG rootPropertyIndex)
3468 StorageInternalImpl* newStorage;
3471 * Allocate space for the new storage object
3473 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3475 if (newStorage!=0)
3477 memset(newStorage, 0, sizeof(StorageInternalImpl));
3480 * Initialize the virtual function table.
3482 newStorage->lpVtbl = &Storage32InternalImpl_Vtbl;
3483 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3486 * Keep the ancestor storage pointer and nail a reference to it.
3488 newStorage->ancestorStorage = ancestorStorage;
3489 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3492 * Keep the index of the root property set for this storage,
3494 newStorage->rootPropertySetIndex = rootPropertyIndex;
3496 return newStorage;
3499 return 0;
3502 void StorageInternalImpl_Destroy(
3503 StorageInternalImpl* This)
3505 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3506 HeapFree(GetProcessHeap(), 0, This);
3509 /******************************************************************************
3511 ** Storage32InternalImpl_Commit
3513 ** The non-root storages cannot be opened in transacted mode thus this function
3514 ** does nothing.
3516 HRESULT WINAPI StorageInternalImpl_Commit(
3517 IStorage* iface,
3518 DWORD grfCommitFlags) /* [in] */
3520 return S_OK;
3523 /******************************************************************************
3525 ** Storage32InternalImpl_Revert
3527 ** The non-root storages cannot be opened in transacted mode thus this function
3528 ** does nothing.
3530 HRESULT WINAPI StorageInternalImpl_Revert(
3531 IStorage* iface)
3533 return S_OK;
3536 /******************************************************************************
3537 ** IEnumSTATSTGImpl implementation
3540 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3541 StorageImpl* parentStorage,
3542 ULONG firstPropertyNode)
3544 IEnumSTATSTGImpl* newEnumeration;
3546 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3548 if (newEnumeration!=0)
3551 * Set-up the virtual function table and reference count.
3553 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3554 newEnumeration->ref = 0;
3557 * We want to nail-down the reference to the storage in case the
3558 * enumeration out-lives the storage in the client application.
3560 newEnumeration->parentStorage = parentStorage;
3561 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3563 newEnumeration->firstPropertyNode = firstPropertyNode;
3566 * Initialize the search stack
3568 newEnumeration->stackSize = 0;
3569 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3570 newEnumeration->stackToVisit =
3571 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3574 * Make sure the current node of the iterator is the first one.
3576 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3579 return newEnumeration;
3582 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3584 IStorage_Release((IStorage*)This->parentStorage);
3585 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3586 HeapFree(GetProcessHeap(), 0, This);
3589 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3590 IEnumSTATSTG* iface,
3591 REFIID riid,
3592 void** ppvObject)
3594 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3597 * Perform a sanity check on the parameters.
3599 if (ppvObject==0)
3600 return E_INVALIDARG;
3603 * Initialize the return parameter.
3605 *ppvObject = 0;
3608 * Compare the riid with the interface IDs implemented by this object.
3610 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3612 *ppvObject = (IEnumSTATSTG*)This;
3614 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3616 *ppvObject = (IEnumSTATSTG*)This;
3620 * Check that we obtained an interface.
3622 if ((*ppvObject)==0)
3623 return E_NOINTERFACE;
3626 * Query Interface always increases the reference count by one when it is
3627 * successful
3629 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3631 return S_OK;
3634 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3635 IEnumSTATSTG* iface)
3637 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3638 return InterlockedIncrement(&This->ref);
3641 ULONG WINAPI IEnumSTATSTGImpl_Release(
3642 IEnumSTATSTG* iface)
3644 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3646 ULONG newRef;
3648 newRef = InterlockedDecrement(&This->ref);
3651 * If the reference count goes down to 0, perform suicide.
3653 if (newRef==0)
3655 IEnumSTATSTGImpl_Destroy(This);
3658 return newRef;
3661 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3662 IEnumSTATSTG* iface,
3663 ULONG celt,
3664 STATSTG* rgelt,
3665 ULONG* pceltFetched)
3667 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3669 StgProperty currentProperty;
3670 STATSTG* currentReturnStruct = rgelt;
3671 ULONG objectFetched = 0;
3672 ULONG currentSearchNode;
3675 * Perform a sanity check on the parameters.
3677 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3678 return E_INVALIDARG;
3681 * To avoid the special case, get another pointer to a ULONG value if
3682 * the caller didn't supply one.
3684 if (pceltFetched==0)
3685 pceltFetched = &objectFetched;
3688 * Start the iteration, we will iterate until we hit the end of the
3689 * linked list or until we hit the number of items to iterate through
3691 *pceltFetched = 0;
3694 * Start with the node at the top of the stack.
3696 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3698 while ( ( *pceltFetched < celt) &&
3699 ( currentSearchNode!=PROPERTY_NULL) )
3702 * Remove the top node from the stack
3704 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3707 * Read the property from the storage.
3709 StorageImpl_ReadProperty(This->parentStorage,
3710 currentSearchNode,
3711 &currentProperty);
3714 * Copy the information to the return buffer.
3716 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3717 &currentProperty,
3718 STATFLAG_DEFAULT);
3721 * Step to the next item in the iteration
3723 (*pceltFetched)++;
3724 currentReturnStruct++;
3727 * Push the next search node in the search stack.
3729 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3732 * continue the iteration.
3734 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3737 if (*pceltFetched == celt)
3738 return S_OK;
3740 return S_FALSE;
3744 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3745 IEnumSTATSTG* iface,
3746 ULONG celt)
3748 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3750 StgProperty currentProperty;
3751 ULONG objectFetched = 0;
3752 ULONG currentSearchNode;
3755 * Start with the node at the top of the stack.
3757 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3759 while ( (objectFetched < celt) &&
3760 (currentSearchNode!=PROPERTY_NULL) )
3763 * Remove the top node from the stack
3765 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3768 * Read the property from the storage.
3770 StorageImpl_ReadProperty(This->parentStorage,
3771 currentSearchNode,
3772 &currentProperty);
3775 * Step to the next item in the iteration
3777 objectFetched++;
3780 * Push the next search node in the search stack.
3782 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3785 * continue the iteration.
3787 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3790 if (objectFetched == celt)
3791 return S_OK;
3793 return S_FALSE;
3796 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3797 IEnumSTATSTG* iface)
3799 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3801 StgProperty rootProperty;
3802 BOOL readSuccessful;
3805 * Re-initialize the search stack to an empty stack
3807 This->stackSize = 0;
3810 * Read the root property from the storage.
3812 readSuccessful = StorageImpl_ReadProperty(
3813 This->parentStorage,
3814 This->firstPropertyNode,
3815 &rootProperty);
3817 if (readSuccessful)
3819 assert(rootProperty.sizeOfNameString!=0);
3822 * Push the search node in the search stack.
3824 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3827 return S_OK;
3830 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3831 IEnumSTATSTG* iface,
3832 IEnumSTATSTG** ppenum)
3834 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3836 IEnumSTATSTGImpl* newClone;
3839 * Perform a sanity check on the parameters.
3841 if (ppenum==0)
3842 return E_INVALIDARG;
3844 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3845 This->firstPropertyNode);
3849 * The new clone enumeration must point to the same current node as
3850 * the ole one.
3852 newClone->stackSize = This->stackSize ;
3853 newClone->stackMaxSize = This->stackMaxSize ;
3854 newClone->stackToVisit =
3855 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3857 memcpy(
3858 newClone->stackToVisit,
3859 This->stackToVisit,
3860 sizeof(ULONG) * newClone->stackSize);
3862 *ppenum = (IEnumSTATSTG*)newClone;
3865 * Don't forget to nail down a reference to the clone before
3866 * returning it.
3868 IEnumSTATSTGImpl_AddRef(*ppenum);
3870 return S_OK;
3873 INT IEnumSTATSTGImpl_FindParentProperty(
3874 IEnumSTATSTGImpl *This,
3875 ULONG childProperty,
3876 StgProperty *currentProperty,
3877 ULONG *thisNodeId)
3879 ULONG currentSearchNode;
3880 ULONG foundNode;
3883 * To avoid the special case, get another pointer to a ULONG value if
3884 * the caller didn't supply one.
3886 if (thisNodeId==0)
3887 thisNodeId = &foundNode;
3890 * Start with the node at the top of the stack.
3892 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3895 while (currentSearchNode!=PROPERTY_NULL)
3898 * Store the current node in the returned parameters
3900 *thisNodeId = currentSearchNode;
3903 * Remove the top node from the stack
3905 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3908 * Read the property from the storage.
3910 StorageImpl_ReadProperty(
3911 This->parentStorage,
3912 currentSearchNode,
3913 currentProperty);
3915 if (currentProperty->previousProperty == childProperty)
3916 return PROPERTY_RELATION_PREVIOUS;
3918 else if (currentProperty->nextProperty == childProperty)
3919 return PROPERTY_RELATION_NEXT;
3921 else if (currentProperty->dirProperty == childProperty)
3922 return PROPERTY_RELATION_DIR;
3925 * Push the next search node in the search stack.
3927 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3930 * continue the iteration.
3932 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3935 return PROPERTY_NULL;
3938 ULONG IEnumSTATSTGImpl_FindProperty(
3939 IEnumSTATSTGImpl* This,
3940 const OLECHAR* lpszPropName,
3941 StgProperty* currentProperty)
3943 ULONG currentSearchNode;
3946 * Start with the node at the top of the stack.
3948 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3950 while (currentSearchNode!=PROPERTY_NULL)
3953 * Remove the top node from the stack
3955 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3958 * Read the property from the storage.
3960 StorageImpl_ReadProperty(This->parentStorage,
3961 currentSearchNode,
3962 currentProperty);
3964 if ( propertyNameCmp(
3965 (OLECHAR*)currentProperty->name,
3966 (OLECHAR*)lpszPropName) == 0)
3967 return currentSearchNode;
3970 * Push the next search node in the search stack.
3972 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3975 * continue the iteration.
3977 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3980 return PROPERTY_NULL;
3983 void IEnumSTATSTGImpl_PushSearchNode(
3984 IEnumSTATSTGImpl* This,
3985 ULONG nodeToPush)
3987 StgProperty rootProperty;
3988 BOOL readSuccessful;
3991 * First, make sure we're not trying to push an unexisting node.
3993 if (nodeToPush==PROPERTY_NULL)
3994 return;
3997 * First push the node to the stack
3999 if (This->stackSize == This->stackMaxSize)
4001 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4003 This->stackToVisit = HeapReAlloc(
4004 GetProcessHeap(),
4006 This->stackToVisit,
4007 sizeof(ULONG) * This->stackMaxSize);
4010 This->stackToVisit[This->stackSize] = nodeToPush;
4011 This->stackSize++;
4014 * Read the root property from the storage.
4016 readSuccessful = StorageImpl_ReadProperty(
4017 This->parentStorage,
4018 nodeToPush,
4019 &rootProperty);
4021 if (readSuccessful)
4023 assert(rootProperty.sizeOfNameString!=0);
4026 * Push the previous search node in the search stack.
4028 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4032 ULONG IEnumSTATSTGImpl_PopSearchNode(
4033 IEnumSTATSTGImpl* This,
4034 BOOL remove)
4036 ULONG topNode;
4038 if (This->stackSize == 0)
4039 return PROPERTY_NULL;
4041 topNode = This->stackToVisit[This->stackSize-1];
4043 if (remove)
4044 This->stackSize--;
4046 return topNode;
4049 /******************************************************************************
4050 ** StorageUtl implementation
4053 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4055 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4058 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4060 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4063 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4065 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4068 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4070 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4073 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4075 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4076 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4077 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4079 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4082 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4084 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4085 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4086 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4088 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4091 void StorageUtl_CopyPropertyToSTATSTG(
4092 STATSTG* destination,
4093 StgProperty* source,
4094 int statFlags)
4097 * The copy of the string occurs only when the flag is not set
4099 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4100 (source->name == NULL) ||
4101 (source->name[0] == 0) )
4103 destination->pwcsName = 0;
4105 else
4107 destination->pwcsName =
4108 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4110 strcpyW((LPWSTR)destination->pwcsName, source->name);
4113 switch (source->propertyType)
4115 case PROPTYPE_STORAGE:
4116 case PROPTYPE_ROOT:
4117 destination->type = STGTY_STORAGE;
4118 break;
4119 case PROPTYPE_STREAM:
4120 destination->type = STGTY_STREAM;
4121 break;
4122 default:
4123 destination->type = STGTY_STREAM;
4124 break;
4127 destination->cbSize = source->size;
4129 currentReturnStruct->mtime = {0}; TODO
4130 currentReturnStruct->ctime = {0};
4131 currentReturnStruct->atime = {0};
4133 destination->grfMode = 0;
4134 destination->grfLocksSupported = 0;
4135 destination->clsid = source->propertyUniqueID;
4136 destination->grfStateBits = 0;
4137 destination->reserved = 0;
4140 /******************************************************************************
4141 ** BlockChainStream implementation
4144 BlockChainStream* BlockChainStream_Construct(
4145 StorageImpl* parentStorage,
4146 ULONG* headOfStreamPlaceHolder,
4147 ULONG propertyIndex)
4149 BlockChainStream* newStream;
4150 ULONG blockIndex;
4152 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4154 newStream->parentStorage = parentStorage;
4155 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4156 newStream->ownerPropertyIndex = propertyIndex;
4157 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4158 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4159 newStream->numBlocks = 0;
4161 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4163 while (blockIndex != BLOCK_END_OF_CHAIN)
4165 newStream->numBlocks++;
4166 newStream->tailIndex = blockIndex;
4168 if(FAILED(StorageImpl_GetNextBlockInChain(
4169 parentStorage,
4170 blockIndex,
4171 &blockIndex)))
4173 HeapFree(GetProcessHeap(), 0, newStream);
4174 return NULL;
4178 return newStream;
4181 void BlockChainStream_Destroy(BlockChainStream* This)
4183 HeapFree(GetProcessHeap(), 0, This);
4186 /******************************************************************************
4187 * BlockChainStream_GetHeadOfChain
4189 * Returns the head of this stream chain.
4190 * Some special chains don't have properties, their heads are kept in
4191 * This->headOfStreamPlaceHolder.
4194 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4196 StgProperty chainProperty;
4197 BOOL readSuccessful;
4199 if (This->headOfStreamPlaceHolder != 0)
4200 return *(This->headOfStreamPlaceHolder);
4202 if (This->ownerPropertyIndex != PROPERTY_NULL)
4204 readSuccessful = StorageImpl_ReadProperty(
4205 This->parentStorage,
4206 This->ownerPropertyIndex,
4207 &chainProperty);
4209 if (readSuccessful)
4211 return chainProperty.startingBlock;
4215 return BLOCK_END_OF_CHAIN;
4218 /******************************************************************************
4219 * BlockChainStream_GetCount
4221 * Returns the number of blocks that comprises this chain.
4222 * This is not the size of the stream as the last block may not be full!
4225 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4227 ULONG blockIndex;
4228 ULONG count = 0;
4230 blockIndex = BlockChainStream_GetHeadOfChain(This);
4232 while (blockIndex != BLOCK_END_OF_CHAIN)
4234 count++;
4236 if(FAILED(StorageImpl_GetNextBlockInChain(
4237 This->parentStorage,
4238 blockIndex,
4239 &blockIndex)))
4240 return 0;
4243 return count;
4246 /******************************************************************************
4247 * BlockChainStream_ReadAt
4249 * Reads a specified number of bytes from this chain at the specified offset.
4250 * bytesRead may be NULL.
4251 * Failure will be returned if the specified number of bytes has not been read.
4253 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4254 ULARGE_INTEGER offset,
4255 ULONG size,
4256 void* buffer,
4257 ULONG* bytesRead)
4259 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4260 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4261 ULONG bytesToReadInBuffer;
4262 ULONG blockIndex;
4263 BYTE* bufferWalker;
4264 BYTE* bigBlockBuffer;
4267 * Find the first block in the stream that contains part of the buffer.
4269 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4270 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4271 (blockNoInSequence < This->lastBlockNoInSequence) )
4273 blockIndex = BlockChainStream_GetHeadOfChain(This);
4274 This->lastBlockNoInSequence = blockNoInSequence;
4276 else
4278 ULONG temp = blockNoInSequence;
4280 blockIndex = This->lastBlockNoInSequenceIndex;
4281 blockNoInSequence -= This->lastBlockNoInSequence;
4282 This->lastBlockNoInSequence = temp;
4285 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4287 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4288 return FALSE;
4289 blockNoInSequence--;
4292 This->lastBlockNoInSequenceIndex = blockIndex;
4295 * Start reading the buffer.
4297 *bytesRead = 0;
4298 bufferWalker = buffer;
4300 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4303 * Calculate how many bytes we can copy from this big block.
4305 bytesToReadInBuffer =
4306 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4309 * Copy those bytes to the buffer
4311 bigBlockBuffer =
4312 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4314 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4316 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4319 * Step to the next big block.
4321 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4322 return FALSE;
4324 bufferWalker += bytesToReadInBuffer;
4325 size -= bytesToReadInBuffer;
4326 *bytesRead += bytesToReadInBuffer;
4327 offsetInBlock = 0; /* There is no offset on the next block */
4331 return (size == 0);
4334 /******************************************************************************
4335 * BlockChainStream_WriteAt
4337 * Writes the specified number of bytes to this chain at the specified offset.
4338 * bytesWritten may be NULL.
4339 * Will fail if not all specified number of bytes have been written.
4341 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4342 ULARGE_INTEGER offset,
4343 ULONG size,
4344 const void* buffer,
4345 ULONG* bytesWritten)
4347 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4348 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4349 ULONG bytesToWrite;
4350 ULONG blockIndex;
4351 BYTE* bufferWalker;
4352 BYTE* bigBlockBuffer;
4355 * Find the first block in the stream that contains part of the buffer.
4357 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4358 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4359 (blockNoInSequence < This->lastBlockNoInSequence) )
4361 blockIndex = BlockChainStream_GetHeadOfChain(This);
4362 This->lastBlockNoInSequence = blockNoInSequence;
4364 else
4366 ULONG temp = blockNoInSequence;
4368 blockIndex = This->lastBlockNoInSequenceIndex;
4369 blockNoInSequence -= This->lastBlockNoInSequence;
4370 This->lastBlockNoInSequence = temp;
4373 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4375 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4376 &blockIndex)))
4377 return FALSE;
4378 blockNoInSequence--;
4381 This->lastBlockNoInSequenceIndex = blockIndex;
4384 * Here, I'm casting away the constness on the buffer variable
4385 * This is OK since we don't intend to modify that buffer.
4387 *bytesWritten = 0;
4388 bufferWalker = (BYTE*)buffer;
4390 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4393 * Calculate how many bytes we can copy from this big block.
4395 bytesToWrite =
4396 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4399 * Copy those bytes to the buffer
4401 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4403 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4405 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4408 * Step to the next big block.
4410 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4411 &blockIndex)))
4412 return FALSE;
4413 bufferWalker += bytesToWrite;
4414 size -= bytesToWrite;
4415 *bytesWritten += bytesToWrite;
4416 offsetInBlock = 0; /* There is no offset on the next block */
4419 return (size == 0);
4422 /******************************************************************************
4423 * BlockChainStream_Shrink
4425 * Shrinks this chain in the big block depot.
4427 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4428 ULARGE_INTEGER newSize)
4430 ULONG blockIndex, extraBlock;
4431 ULONG numBlocks;
4432 ULONG count = 1;
4435 * Reset the last accessed block cache.
4437 This->lastBlockNoInSequence = 0xFFFFFFFF;
4438 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4441 * Figure out how many blocks are needed to contain the new size
4443 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4445 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4446 numBlocks++;
4448 blockIndex = BlockChainStream_GetHeadOfChain(This);
4451 * Go to the new end of chain
4453 while (count < numBlocks)
4455 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4456 &blockIndex)))
4457 return FALSE;
4458 count++;
4461 /* Get the next block before marking the new end */
4462 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4463 &extraBlock)))
4464 return FALSE;
4466 /* Mark the new end of chain */
4467 StorageImpl_SetNextBlockInChain(
4468 This->parentStorage,
4469 blockIndex,
4470 BLOCK_END_OF_CHAIN);
4472 This->tailIndex = blockIndex;
4473 This->numBlocks = numBlocks;
4476 * Mark the extra blocks as free
4478 while (extraBlock != BLOCK_END_OF_CHAIN)
4480 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4481 &blockIndex)))
4482 return FALSE;
4483 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4484 extraBlock = blockIndex;
4487 return TRUE;
4490 /******************************************************************************
4491 * BlockChainStream_Enlarge
4493 * Grows this chain in the big block depot.
4495 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4496 ULARGE_INTEGER newSize)
4498 ULONG blockIndex, currentBlock;
4499 ULONG newNumBlocks;
4500 ULONG oldNumBlocks = 0;
4502 blockIndex = BlockChainStream_GetHeadOfChain(This);
4505 * Empty chain. Create the head.
4507 if (blockIndex == BLOCK_END_OF_CHAIN)
4509 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4510 StorageImpl_SetNextBlockInChain(This->parentStorage,
4511 blockIndex,
4512 BLOCK_END_OF_CHAIN);
4514 if (This->headOfStreamPlaceHolder != 0)
4516 *(This->headOfStreamPlaceHolder) = blockIndex;
4518 else
4520 StgProperty chainProp;
4521 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4523 StorageImpl_ReadProperty(
4524 This->parentStorage,
4525 This->ownerPropertyIndex,
4526 &chainProp);
4528 chainProp.startingBlock = blockIndex;
4530 StorageImpl_WriteProperty(
4531 This->parentStorage,
4532 This->ownerPropertyIndex,
4533 &chainProp);
4536 This->tailIndex = blockIndex;
4537 This->numBlocks = 1;
4541 * Figure out how many blocks are needed to contain this stream
4543 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4545 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4546 newNumBlocks++;
4549 * Go to the current end of chain
4551 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4553 currentBlock = blockIndex;
4555 while (blockIndex != BLOCK_END_OF_CHAIN)
4557 This->numBlocks++;
4558 currentBlock = blockIndex;
4560 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4561 &blockIndex)))
4562 return FALSE;
4565 This->tailIndex = currentBlock;
4568 currentBlock = This->tailIndex;
4569 oldNumBlocks = This->numBlocks;
4572 * Add new blocks to the chain
4574 if (oldNumBlocks < newNumBlocks)
4576 while (oldNumBlocks < newNumBlocks)
4578 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4580 StorageImpl_SetNextBlockInChain(
4581 This->parentStorage,
4582 currentBlock,
4583 blockIndex);
4585 StorageImpl_SetNextBlockInChain(
4586 This->parentStorage,
4587 blockIndex,
4588 BLOCK_END_OF_CHAIN);
4590 currentBlock = blockIndex;
4591 oldNumBlocks++;
4594 This->tailIndex = blockIndex;
4595 This->numBlocks = newNumBlocks;
4598 return TRUE;
4601 /******************************************************************************
4602 * BlockChainStream_SetSize
4604 * Sets the size of this stream. The big block depot will be updated.
4605 * The file will grow if we grow the chain.
4607 * TODO: Free the actual blocks in the file when we shrink the chain.
4608 * Currently, the blocks are still in the file. So the file size
4609 * doesn't shrink even if we shrink streams.
4611 BOOL BlockChainStream_SetSize(
4612 BlockChainStream* This,
4613 ULARGE_INTEGER newSize)
4615 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4617 if (newSize.u.LowPart == size.u.LowPart)
4618 return TRUE;
4620 if (newSize.u.LowPart < size.u.LowPart)
4622 BlockChainStream_Shrink(This, newSize);
4624 else
4626 ULARGE_INTEGER fileSize =
4627 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4629 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4632 * Make sure the file stays a multiple of blocksize
4634 if ((diff % This->parentStorage->bigBlockSize) != 0)
4635 diff += (This->parentStorage->bigBlockSize -
4636 (diff % This->parentStorage->bigBlockSize) );
4638 fileSize.u.LowPart += diff;
4639 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4641 BlockChainStream_Enlarge(This, newSize);
4644 return TRUE;
4647 /******************************************************************************
4648 * BlockChainStream_GetSize
4650 * Returns the size of this chain.
4651 * Will return the block count if this chain doesn't have a property.
4653 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4655 StgProperty chainProperty;
4657 if(This->headOfStreamPlaceHolder == NULL)
4660 * This chain is a data stream read the property and return
4661 * the appropriate size
4663 StorageImpl_ReadProperty(
4664 This->parentStorage,
4665 This->ownerPropertyIndex,
4666 &chainProperty);
4668 return chainProperty.size;
4670 else
4673 * this chain is a chain that does not have a property, figure out the
4674 * size by making the product number of used blocks times the
4675 * size of them
4677 ULARGE_INTEGER result;
4678 result.u.HighPart = 0;
4680 result.u.LowPart =
4681 BlockChainStream_GetCount(This) *
4682 This->parentStorage->bigBlockSize;
4684 return result;
4688 /******************************************************************************
4689 ** SmallBlockChainStream implementation
4692 SmallBlockChainStream* SmallBlockChainStream_Construct(
4693 StorageImpl* parentStorage,
4694 ULONG propertyIndex)
4696 SmallBlockChainStream* newStream;
4698 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4700 newStream->parentStorage = parentStorage;
4701 newStream->ownerPropertyIndex = propertyIndex;
4703 return newStream;
4706 void SmallBlockChainStream_Destroy(
4707 SmallBlockChainStream* This)
4709 HeapFree(GetProcessHeap(), 0, This);
4712 /******************************************************************************
4713 * SmallBlockChainStream_GetHeadOfChain
4715 * Returns the head of this chain of small blocks.
4717 ULONG SmallBlockChainStream_GetHeadOfChain(
4718 SmallBlockChainStream* This)
4720 StgProperty chainProperty;
4721 BOOL readSuccessful;
4723 if (This->ownerPropertyIndex)
4725 readSuccessful = StorageImpl_ReadProperty(
4726 This->parentStorage,
4727 This->ownerPropertyIndex,
4728 &chainProperty);
4730 if (readSuccessful)
4732 return chainProperty.startingBlock;
4737 return BLOCK_END_OF_CHAIN;
4740 /******************************************************************************
4741 * SmallBlockChainStream_GetNextBlockInChain
4743 * Returns the index of the next small block in this chain.
4745 * Return Values:
4746 * - BLOCK_END_OF_CHAIN: end of this chain
4747 * - BLOCK_UNUSED: small block 'blockIndex' is free
4749 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4750 SmallBlockChainStream* This,
4751 ULONG blockIndex,
4752 ULONG* nextBlockInChain)
4754 ULARGE_INTEGER offsetOfBlockInDepot;
4755 DWORD buffer;
4756 ULONG bytesRead;
4757 BOOL success;
4759 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4761 offsetOfBlockInDepot.u.HighPart = 0;
4762 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4765 * Read those bytes in the buffer from the small block file.
4767 success = BlockChainStream_ReadAt(
4768 This->parentStorage->smallBlockDepotChain,
4769 offsetOfBlockInDepot,
4770 sizeof(DWORD),
4771 &buffer,
4772 &bytesRead);
4774 if (success)
4776 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4777 return S_OK;
4780 return STG_E_READFAULT;
4783 /******************************************************************************
4784 * SmallBlockChainStream_SetNextBlockInChain
4786 * Writes the index of the next block of the specified block in the small
4787 * block depot.
4788 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4789 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4791 void SmallBlockChainStream_SetNextBlockInChain(
4792 SmallBlockChainStream* This,
4793 ULONG blockIndex,
4794 ULONG nextBlock)
4796 ULARGE_INTEGER offsetOfBlockInDepot;
4797 DWORD buffer;
4798 ULONG bytesWritten;
4800 offsetOfBlockInDepot.u.HighPart = 0;
4801 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4803 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4806 * Read those bytes in the buffer from the small block file.
4808 BlockChainStream_WriteAt(
4809 This->parentStorage->smallBlockDepotChain,
4810 offsetOfBlockInDepot,
4811 sizeof(DWORD),
4812 &buffer,
4813 &bytesWritten);
4816 /******************************************************************************
4817 * SmallBlockChainStream_FreeBlock
4819 * Flag small block 'blockIndex' as free in the small block depot.
4821 void SmallBlockChainStream_FreeBlock(
4822 SmallBlockChainStream* This,
4823 ULONG blockIndex)
4825 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4828 /******************************************************************************
4829 * SmallBlockChainStream_GetNextFreeBlock
4831 * Returns the index of a free small block. The small block depot will be
4832 * enlarged if necessary. The small block chain will also be enlarged if
4833 * necessary.
4835 ULONG SmallBlockChainStream_GetNextFreeBlock(
4836 SmallBlockChainStream* This)
4838 ULARGE_INTEGER offsetOfBlockInDepot;
4839 DWORD buffer;
4840 ULONG bytesRead;
4841 ULONG blockIndex = 0;
4842 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4843 BOOL success = TRUE;
4844 ULONG smallBlocksPerBigBlock;
4846 offsetOfBlockInDepot.u.HighPart = 0;
4849 * Scan the small block depot for a free block
4851 while (nextBlockIndex != BLOCK_UNUSED)
4853 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4855 success = BlockChainStream_ReadAt(
4856 This->parentStorage->smallBlockDepotChain,
4857 offsetOfBlockInDepot,
4858 sizeof(DWORD),
4859 &buffer,
4860 &bytesRead);
4863 * If we run out of space for the small block depot, enlarge it
4865 if (success)
4867 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4869 if (nextBlockIndex != BLOCK_UNUSED)
4870 blockIndex++;
4872 else
4874 ULONG count =
4875 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4877 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4878 ULONG nextBlock, newsbdIndex;
4879 BYTE* smallBlockDepot;
4881 nextBlock = sbdIndex;
4882 while (nextBlock != BLOCK_END_OF_CHAIN)
4884 sbdIndex = nextBlock;
4885 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4888 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4889 if (sbdIndex != BLOCK_END_OF_CHAIN)
4890 StorageImpl_SetNextBlockInChain(
4891 This->parentStorage,
4892 sbdIndex,
4893 newsbdIndex);
4895 StorageImpl_SetNextBlockInChain(
4896 This->parentStorage,
4897 newsbdIndex,
4898 BLOCK_END_OF_CHAIN);
4901 * Initialize all the small blocks to free
4903 smallBlockDepot =
4904 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4906 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4907 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4909 if (count == 0)
4912 * We have just created the small block depot.
4914 StgProperty rootProp;
4915 ULONG sbStartIndex;
4918 * Save it in the header
4920 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4921 StorageImpl_SaveFileHeader(This->parentStorage);
4924 * And allocate the first big block that will contain small blocks
4926 sbStartIndex =
4927 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4929 StorageImpl_SetNextBlockInChain(
4930 This->parentStorage,
4931 sbStartIndex,
4932 BLOCK_END_OF_CHAIN);
4934 StorageImpl_ReadProperty(
4935 This->parentStorage,
4936 This->parentStorage->rootPropertySetIndex,
4937 &rootProp);
4939 rootProp.startingBlock = sbStartIndex;
4940 rootProp.size.u.HighPart = 0;
4941 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
4943 StorageImpl_WriteProperty(
4944 This->parentStorage,
4945 This->parentStorage->rootPropertySetIndex,
4946 &rootProp);
4951 smallBlocksPerBigBlock =
4952 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4955 * Verify if we have to allocate big blocks to contain small blocks
4957 if (blockIndex % smallBlocksPerBigBlock == 0)
4959 StgProperty rootProp;
4960 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4962 StorageImpl_ReadProperty(
4963 This->parentStorage,
4964 This->parentStorage->rootPropertySetIndex,
4965 &rootProp);
4967 if (rootProp.size.u.LowPart <
4968 (blocksRequired * This->parentStorage->bigBlockSize))
4970 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
4972 BlockChainStream_SetSize(
4973 This->parentStorage->smallBlockRootChain,
4974 rootProp.size);
4976 StorageImpl_WriteProperty(
4977 This->parentStorage,
4978 This->parentStorage->rootPropertySetIndex,
4979 &rootProp);
4983 return blockIndex;
4986 /******************************************************************************
4987 * SmallBlockChainStream_ReadAt
4989 * Reads a specified number of bytes from this chain at the specified offset.
4990 * bytesRead may be NULL.
4991 * Failure will be returned if the specified number of bytes has not been read.
4993 BOOL SmallBlockChainStream_ReadAt(
4994 SmallBlockChainStream* This,
4995 ULARGE_INTEGER offset,
4996 ULONG size,
4997 void* buffer,
4998 ULONG* bytesRead)
5000 ULARGE_INTEGER offsetInBigBlockFile;
5001 ULONG blockNoInSequence =
5002 offset.u.LowPart / This->parentStorage->smallBlockSize;
5004 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5005 ULONG bytesToReadInBuffer;
5006 ULONG blockIndex;
5007 ULONG bytesReadFromBigBlockFile;
5008 BYTE* bufferWalker;
5011 * This should never happen on a small block file.
5013 assert(offset.u.HighPart==0);
5016 * Find the first block in the stream that contains part of the buffer.
5018 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5020 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5022 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5023 &blockIndex)))
5024 return FALSE;
5025 blockNoInSequence--;
5029 * Start reading the buffer.
5031 *bytesRead = 0;
5032 bufferWalker = buffer;
5034 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5037 * Calculate how many bytes we can copy from this small block.
5039 bytesToReadInBuffer =
5040 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5043 * Calculate the offset of the small block in the small block file.
5045 offsetInBigBlockFile.u.HighPart = 0;
5046 offsetInBigBlockFile.u.LowPart =
5047 blockIndex * This->parentStorage->smallBlockSize;
5049 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5052 * Read those bytes in the buffer from the small block file.
5054 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5055 offsetInBigBlockFile,
5056 bytesToReadInBuffer,
5057 bufferWalker,
5058 &bytesReadFromBigBlockFile);
5060 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5063 * Step to the next big block.
5065 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5066 return FALSE;
5067 bufferWalker += bytesToReadInBuffer;
5068 size -= bytesToReadInBuffer;
5069 *bytesRead += bytesToReadInBuffer;
5070 offsetInBlock = 0; /* There is no offset on the next block */
5073 return (size == 0);
5076 /******************************************************************************
5077 * SmallBlockChainStream_WriteAt
5079 * Writes the specified number of bytes to this chain at the specified offset.
5080 * bytesWritten may be NULL.
5081 * Will fail if not all specified number of bytes have been written.
5083 BOOL SmallBlockChainStream_WriteAt(
5084 SmallBlockChainStream* This,
5085 ULARGE_INTEGER offset,
5086 ULONG size,
5087 const void* buffer,
5088 ULONG* bytesWritten)
5090 ULARGE_INTEGER offsetInBigBlockFile;
5091 ULONG blockNoInSequence =
5092 offset.u.LowPart / This->parentStorage->smallBlockSize;
5094 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5095 ULONG bytesToWriteInBuffer;
5096 ULONG blockIndex;
5097 ULONG bytesWrittenFromBigBlockFile;
5098 BYTE* bufferWalker;
5101 * This should never happen on a small block file.
5103 assert(offset.u.HighPart==0);
5106 * Find the first block in the stream that contains part of the buffer.
5108 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5110 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5112 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5113 return FALSE;
5114 blockNoInSequence--;
5118 * Start writing the buffer.
5120 * Here, I'm casting away the constness on the buffer variable
5121 * This is OK since we don't intend to modify that buffer.
5123 *bytesWritten = 0;
5124 bufferWalker = (BYTE*)buffer;
5125 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5128 * Calculate how many bytes we can copy to this small block.
5130 bytesToWriteInBuffer =
5131 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5134 * Calculate the offset of the small block in the small block file.
5136 offsetInBigBlockFile.u.HighPart = 0;
5137 offsetInBigBlockFile.u.LowPart =
5138 blockIndex * This->parentStorage->smallBlockSize;
5140 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5143 * Write those bytes in the buffer to the small block file.
5145 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5146 offsetInBigBlockFile,
5147 bytesToWriteInBuffer,
5148 bufferWalker,
5149 &bytesWrittenFromBigBlockFile);
5151 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5154 * Step to the next big block.
5156 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5157 &blockIndex)))
5158 return FALSE;
5159 bufferWalker += bytesToWriteInBuffer;
5160 size -= bytesToWriteInBuffer;
5161 *bytesWritten += bytesToWriteInBuffer;
5162 offsetInBlock = 0; /* There is no offset on the next block */
5165 return (size == 0);
5168 /******************************************************************************
5169 * SmallBlockChainStream_Shrink
5171 * Shrinks this chain in the small block depot.
5173 BOOL SmallBlockChainStream_Shrink(
5174 SmallBlockChainStream* This,
5175 ULARGE_INTEGER newSize)
5177 ULONG blockIndex, extraBlock;
5178 ULONG numBlocks;
5179 ULONG count = 0;
5181 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5183 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5184 numBlocks++;
5186 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5189 * Go to the new end of chain
5191 while (count < numBlocks)
5193 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5194 &blockIndex)))
5195 return FALSE;
5196 count++;
5200 * If the count is 0, we have a special case, the head of the chain was
5201 * just freed.
5203 if (count == 0)
5205 StgProperty chainProp;
5207 StorageImpl_ReadProperty(This->parentStorage,
5208 This->ownerPropertyIndex,
5209 &chainProp);
5211 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5213 StorageImpl_WriteProperty(This->parentStorage,
5214 This->ownerPropertyIndex,
5215 &chainProp);
5218 * We start freeing the chain at the head block.
5220 extraBlock = blockIndex;
5222 else
5224 /* Get the next block before marking the new end */
5225 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5226 &extraBlock)))
5227 return FALSE;
5229 /* Mark the new end of chain */
5230 SmallBlockChainStream_SetNextBlockInChain(
5231 This,
5232 blockIndex,
5233 BLOCK_END_OF_CHAIN);
5237 * Mark the extra blocks as free
5239 while (extraBlock != BLOCK_END_OF_CHAIN)
5241 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5242 &blockIndex)))
5243 return FALSE;
5244 SmallBlockChainStream_FreeBlock(This, extraBlock);
5245 extraBlock = blockIndex;
5248 return TRUE;
5251 /******************************************************************************
5252 * SmallBlockChainStream_Enlarge
5254 * Grows this chain in the small block depot.
5256 BOOL SmallBlockChainStream_Enlarge(
5257 SmallBlockChainStream* This,
5258 ULARGE_INTEGER newSize)
5260 ULONG blockIndex, currentBlock;
5261 ULONG newNumBlocks;
5262 ULONG oldNumBlocks = 0;
5264 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5267 * Empty chain
5269 if (blockIndex == BLOCK_END_OF_CHAIN)
5272 StgProperty chainProp;
5274 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5275 &chainProp);
5277 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5279 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5280 &chainProp);
5282 blockIndex = chainProp.startingBlock;
5283 SmallBlockChainStream_SetNextBlockInChain(
5284 This,
5285 blockIndex,
5286 BLOCK_END_OF_CHAIN);
5289 currentBlock = blockIndex;
5292 * Figure out how many blocks are needed to contain this stream
5294 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5296 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5297 newNumBlocks++;
5300 * Go to the current end of chain
5302 while (blockIndex != BLOCK_END_OF_CHAIN)
5304 oldNumBlocks++;
5305 currentBlock = blockIndex;
5306 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5307 return FALSE;
5311 * Add new blocks to the chain
5313 while (oldNumBlocks < newNumBlocks)
5315 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5316 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5318 SmallBlockChainStream_SetNextBlockInChain(
5319 This,
5320 blockIndex,
5321 BLOCK_END_OF_CHAIN);
5323 currentBlock = blockIndex;
5324 oldNumBlocks++;
5327 return TRUE;
5330 /******************************************************************************
5331 * SmallBlockChainStream_GetCount
5333 * Returns the number of blocks that comprises this chain.
5334 * This is not the size of this chain as the last block may not be full!
5336 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5338 ULONG blockIndex;
5339 ULONG count = 0;
5341 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5343 while (blockIndex != BLOCK_END_OF_CHAIN)
5345 count++;
5347 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5348 return 0;
5351 return count;
5354 /******************************************************************************
5355 * SmallBlockChainStream_SetSize
5357 * Sets the size of this stream.
5358 * The file will grow if we grow the chain.
5360 * TODO: Free the actual blocks in the file when we shrink the chain.
5361 * Currently, the blocks are still in the file. So the file size
5362 * doesn't shrink even if we shrink streams.
5364 BOOL SmallBlockChainStream_SetSize(
5365 SmallBlockChainStream* This,
5366 ULARGE_INTEGER newSize)
5368 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5370 if (newSize.u.LowPart == size.u.LowPart)
5371 return TRUE;
5373 if (newSize.u.LowPart < size.u.LowPart)
5375 SmallBlockChainStream_Shrink(This, newSize);
5377 else
5379 SmallBlockChainStream_Enlarge(This, newSize);
5382 return TRUE;
5385 /******************************************************************************
5386 * SmallBlockChainStream_GetSize
5388 * Returns the size of this chain.
5390 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5392 StgProperty chainProperty;
5394 StorageImpl_ReadProperty(
5395 This->parentStorage,
5396 This->ownerPropertyIndex,
5397 &chainProperty);
5399 return chainProperty.size;
5402 /******************************************************************************
5403 * StgCreateDocfile [OLE32.@]
5405 HRESULT WINAPI StgCreateDocfile(
5406 LPCOLESTR pwcsName,
5407 DWORD grfMode,
5408 DWORD reserved,
5409 IStorage **ppstgOpen)
5411 StorageImpl* newStorage = 0;
5412 HANDLE hFile = INVALID_HANDLE_VALUE;
5413 HRESULT hr = S_OK;
5414 DWORD shareMode;
5415 DWORD accessMode;
5416 DWORD creationMode;
5417 DWORD fileAttributes;
5418 WCHAR tempFileName[MAX_PATH];
5420 TRACE("(%s, %lx, %ld, %p)\n",
5421 debugstr_w(pwcsName), grfMode,
5422 reserved, ppstgOpen);
5425 * Validate the parameters
5427 if (ppstgOpen == 0)
5428 return STG_E_INVALIDPOINTER;
5431 * Validate the STGM flags
5433 if ( FAILED( validateSTGM(grfMode) ))
5434 return STG_E_INVALIDFLAG;
5437 * Generate a unique name.
5439 if (pwcsName == 0)
5441 WCHAR tempPath[MAX_PATH];
5442 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5444 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5445 return STG_E_INVALIDFLAG;
5446 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5447 return STG_E_INVALIDFLAG;
5449 memset(tempPath, 0, sizeof(tempPath));
5450 memset(tempFileName, 0, sizeof(tempFileName));
5452 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5453 tempPath[0] = '.';
5455 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5456 pwcsName = tempFileName;
5457 else
5458 return STG_E_INSUFFICIENTMEMORY;
5460 creationMode = TRUNCATE_EXISTING;
5462 else
5464 creationMode = GetCreationModeFromSTGM(grfMode);
5468 * Interpret the STGM value grfMode
5470 shareMode = GetShareModeFromSTGM(grfMode);
5471 accessMode = GetAccessModeFromSTGM(grfMode);
5473 if (grfMode & STGM_DELETEONRELEASE)
5474 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5475 else
5476 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5478 if (grfMode & STGM_TRANSACTED)
5479 FIXME("Transacted mode not implemented.\n");
5482 * Initialize the "out" parameter.
5484 *ppstgOpen = 0;
5486 hFile = CreateFileW(pwcsName,
5487 accessMode,
5488 shareMode,
5489 NULL,
5490 creationMode,
5491 fileAttributes,
5494 if (hFile == INVALID_HANDLE_VALUE)
5496 return E_FAIL;
5500 * Allocate and initialize the new IStorage32object.
5502 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5504 if (newStorage == 0)
5505 return STG_E_INSUFFICIENTMEMORY;
5507 hr = StorageImpl_Construct(
5508 newStorage,
5509 hFile,
5510 pwcsName,
5511 NULL,
5512 grfMode,
5513 TRUE,
5514 TRUE);
5516 if (FAILED(hr))
5518 HeapFree(GetProcessHeap(), 0, newStorage);
5519 return hr;
5523 * Get an "out" pointer for the caller.
5525 hr = StorageBaseImpl_QueryInterface(
5526 (IStorage*)newStorage,
5527 (REFIID)&IID_IStorage,
5528 (void**)ppstgOpen);
5530 return hr;
5533 /******************************************************************************
5534 * StgCreateStorageEx [OLE32.@]
5536 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5538 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5539 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5540 return STG_E_UNIMPLEMENTEDFUNCTION;
5543 /******************************************************************************
5544 * StgOpenStorage [OLE32.@]
5546 HRESULT WINAPI StgOpenStorage(
5547 const OLECHAR *pwcsName,
5548 IStorage *pstgPriority,
5549 DWORD grfMode,
5550 SNB snbExclude,
5551 DWORD reserved,
5552 IStorage **ppstgOpen)
5554 StorageImpl* newStorage = 0;
5555 HRESULT hr = S_OK;
5556 HANDLE hFile = 0;
5557 DWORD shareMode;
5558 DWORD accessMode;
5559 WCHAR fullname[MAX_PATH];
5560 DWORD length;
5562 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5563 debugstr_w(pwcsName), pstgPriority, grfMode,
5564 snbExclude, reserved, ppstgOpen);
5567 * Perform a sanity check
5569 if (( pwcsName == 0) || (ppstgOpen == 0) )
5571 hr = STG_E_INVALIDPOINTER;
5572 goto end;
5576 * Validate the STGM flags
5578 if ( FAILED( validateSTGM(grfMode) ))
5580 hr = STG_E_INVALIDFLAG;
5581 goto end;
5585 * Interpret the STGM value grfMode
5587 shareMode = GetShareModeFromSTGM(grfMode);
5588 accessMode = GetAccessModeFromSTGM(grfMode);
5591 * Initialize the "out" parameter.
5593 *ppstgOpen = 0;
5595 hFile = CreateFileW( pwcsName,
5596 accessMode,
5597 shareMode,
5598 NULL,
5599 OPEN_EXISTING,
5600 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5603 length = GetFileSize(hFile, NULL);
5605 if (hFile==INVALID_HANDLE_VALUE)
5607 DWORD last_error = GetLastError();
5609 hr = E_FAIL;
5611 switch (last_error)
5613 case ERROR_FILE_NOT_FOUND:
5614 hr = STG_E_FILENOTFOUND;
5615 break;
5617 case ERROR_PATH_NOT_FOUND:
5618 hr = STG_E_PATHNOTFOUND;
5619 break;
5621 case ERROR_ACCESS_DENIED:
5622 case ERROR_WRITE_PROTECT:
5623 hr = STG_E_ACCESSDENIED;
5624 break;
5626 case ERROR_SHARING_VIOLATION:
5627 hr = STG_E_SHAREVIOLATION;
5628 break;
5630 default:
5631 hr = E_FAIL;
5634 goto end;
5638 * Allocate and initialize the new IStorage32object.
5640 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5642 if (newStorage == 0)
5644 hr = STG_E_INSUFFICIENTMEMORY;
5645 goto end;
5648 /* if the file's length was zero, initialize the storage */
5649 hr = StorageImpl_Construct(
5650 newStorage,
5651 hFile,
5652 pwcsName,
5653 NULL,
5654 grfMode,
5655 TRUE,
5656 !length );
5658 if (FAILED(hr))
5660 HeapFree(GetProcessHeap(), 0, newStorage);
5662 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5664 if(hr == STG_E_INVALIDHEADER)
5665 hr = STG_E_FILEALREADYEXISTS;
5666 goto end;
5669 /* prepare the file name string given in lieu of the root property name */
5670 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5671 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5672 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5675 * Get an "out" pointer for the caller.
5677 hr = StorageBaseImpl_QueryInterface(
5678 (IStorage*)newStorage,
5679 (REFIID)&IID_IStorage,
5680 (void**)ppstgOpen);
5682 end:
5683 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5684 return hr;
5687 /******************************************************************************
5688 * StgCreateDocfileOnILockBytes [OLE32.@]
5690 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5691 ILockBytes *plkbyt,
5692 DWORD grfMode,
5693 DWORD reserved,
5694 IStorage** ppstgOpen)
5696 StorageImpl* newStorage = 0;
5697 HRESULT hr = S_OK;
5700 * Validate the parameters
5702 if ((ppstgOpen == 0) || (plkbyt == 0))
5703 return STG_E_INVALIDPOINTER;
5706 * Allocate and initialize the new IStorage object.
5708 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5710 if (newStorage == 0)
5711 return STG_E_INSUFFICIENTMEMORY;
5713 hr = StorageImpl_Construct(
5714 newStorage,
5717 plkbyt,
5718 grfMode,
5719 FALSE,
5720 TRUE);
5722 if (FAILED(hr))
5724 HeapFree(GetProcessHeap(), 0, newStorage);
5725 return hr;
5729 * Get an "out" pointer for the caller.
5731 hr = StorageBaseImpl_QueryInterface(
5732 (IStorage*)newStorage,
5733 (REFIID)&IID_IStorage,
5734 (void**)ppstgOpen);
5736 return hr;
5739 /******************************************************************************
5740 * StgOpenStorageOnILockBytes [OLE32.@]
5742 HRESULT WINAPI StgOpenStorageOnILockBytes(
5743 ILockBytes *plkbyt,
5744 IStorage *pstgPriority,
5745 DWORD grfMode,
5746 SNB snbExclude,
5747 DWORD reserved,
5748 IStorage **ppstgOpen)
5750 StorageImpl* newStorage = 0;
5751 HRESULT hr = S_OK;
5754 * Perform a sanity check
5756 if ((plkbyt == 0) || (ppstgOpen == 0))
5757 return STG_E_INVALIDPOINTER;
5760 * Validate the STGM flags
5762 if ( FAILED( validateSTGM(grfMode) ))
5763 return STG_E_INVALIDFLAG;
5766 * Initialize the "out" parameter.
5768 *ppstgOpen = 0;
5771 * Allocate and initialize the new IStorage object.
5773 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5775 if (newStorage == 0)
5776 return STG_E_INSUFFICIENTMEMORY;
5778 hr = StorageImpl_Construct(
5779 newStorage,
5782 plkbyt,
5783 grfMode,
5784 FALSE,
5785 FALSE);
5787 if (FAILED(hr))
5789 HeapFree(GetProcessHeap(), 0, newStorage);
5790 return hr;
5794 * Get an "out" pointer for the caller.
5796 hr = StorageBaseImpl_QueryInterface(
5797 (IStorage*)newStorage,
5798 (REFIID)&IID_IStorage,
5799 (void**)ppstgOpen);
5801 return hr;
5804 /******************************************************************************
5805 * StgSetTimes [ole32.@]
5806 * StgSetTimes [OLE32.@]
5810 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *a,
5811 FILETIME const *b, FILETIME const *c )
5813 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5814 return S_OK;
5817 /******************************************************************************
5818 * StgIsStorageILockBytes [OLE32.@]
5820 * Determines if the ILockBytes contains a storage object.
5822 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5824 BYTE sig[8];
5825 ULARGE_INTEGER offset;
5827 offset.u.HighPart = 0;
5828 offset.u.LowPart = 0;
5830 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5832 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5833 return S_OK;
5835 return S_FALSE;
5838 /******************************************************************************
5839 * WriteClassStg [OLE32.@]
5841 * This method will store the specified CLSID in the specified storage object
5843 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5845 HRESULT hRes;
5847 assert(pStg != 0);
5849 hRes = IStorage_SetClass(pStg, rclsid);
5851 return hRes;
5854 /***********************************************************************
5855 * ReadClassStg (OLE32.@)
5857 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5859 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5861 STATSTG pstatstg;
5862 HRESULT hRes;
5864 TRACE("()\n");
5866 if(pclsid==NULL)
5867 return E_POINTER;
5869 * read a STATSTG structure (contains the clsid) from the storage
5871 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5873 if(SUCCEEDED(hRes))
5874 *pclsid=pstatstg.clsid;
5876 return hRes;
5879 /***********************************************************************
5880 * OleLoadFromStream (OLE32.@)
5882 * This function loads an object from stream
5884 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5886 CLSID clsid;
5887 HRESULT res;
5888 LPPERSISTSTREAM xstm;
5890 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5892 res=ReadClassStm(pStm,&clsid);
5893 if (!SUCCEEDED(res))
5894 return res;
5895 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5896 if (!SUCCEEDED(res))
5897 return res;
5898 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5899 if (!SUCCEEDED(res)) {
5900 IUnknown_Release((IUnknown*)*ppvObj);
5901 return res;
5903 res=IPersistStream_Load(xstm,pStm);
5904 IPersistStream_Release(xstm);
5905 /* FIXME: all refcounts ok at this point? I think they should be:
5906 * pStm : unchanged
5907 * ppvObj : 1
5908 * xstm : 0 (released)
5910 return res;
5913 /***********************************************************************
5914 * OleSaveToStream (OLE32.@)
5916 * This function saves an object with the IPersistStream interface on it
5917 * to the specified stream.
5919 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5922 CLSID clsid;
5923 HRESULT res;
5925 TRACE("(%p,%p)\n",pPStm,pStm);
5927 res=IPersistStream_GetClassID(pPStm,&clsid);
5929 if (SUCCEEDED(res)){
5931 res=WriteClassStm(pStm,&clsid);
5933 if (SUCCEEDED(res))
5935 res=IPersistStream_Save(pPStm,pStm,TRUE);
5938 TRACE("Finished Save\n");
5939 return res;
5942 /****************************************************************************
5943 * This method validate a STGM parameter that can contain the values below
5945 * STGM_DIRECT 0x00000000
5946 * STGM_TRANSACTED 0x00010000
5947 * STGM_SIMPLE 0x08000000
5949 * STGM_READ 0x00000000
5950 * STGM_WRITE 0x00000001
5951 * STGM_READWRITE 0x00000002
5953 * STGM_SHARE_DENY_NONE 0x00000040
5954 * STGM_SHARE_DENY_READ 0x00000030
5955 * STGM_SHARE_DENY_WRITE 0x00000020
5956 * STGM_SHARE_EXCLUSIVE 0x00000010
5958 * STGM_PRIORITY 0x00040000
5959 * STGM_DELETEONRELEASE 0x04000000
5961 * STGM_CREATE 0x00001000
5962 * STGM_CONVERT 0x00020000
5963 * STGM_FAILIFTHERE 0x00000000
5965 * STGM_NOSCRATCH 0x00100000
5966 * STGM_NOSNAPSHOT 0x00200000
5968 static HRESULT validateSTGM(DWORD stgm)
5970 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5971 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5972 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5974 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5975 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5976 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5978 BOOL bSTGM_SHARE_DENY_NONE =
5979 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5981 BOOL bSTGM_SHARE_DENY_READ =
5982 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5984 BOOL bSTGM_SHARE_DENY_WRITE =
5985 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5987 BOOL bSTGM_SHARE_EXCLUSIVE =
5988 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5990 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5991 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5993 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5994 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5997 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5999 if ( ! bSTGM_DIRECT )
6000 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
6001 return E_FAIL;
6004 * STGM_WRITE | STGM_READWRITE | STGM_READ
6006 if ( ! bSTGM_READ )
6007 if( bSTGM_WRITE && bSTGM_READWRITE )
6008 return E_FAIL;
6011 * STGM_SHARE_DENY_NONE | others
6012 * (I assume here that DENY_READ implies DENY_WRITE)
6014 if ( bSTGM_SHARE_DENY_NONE )
6015 if ( bSTGM_SHARE_DENY_READ ||
6016 bSTGM_SHARE_DENY_WRITE ||
6017 bSTGM_SHARE_EXCLUSIVE)
6018 return E_FAIL;
6021 * STGM_CREATE | STGM_CONVERT
6022 * if both are false, STGM_FAILIFTHERE is set to TRUE
6024 if ( bSTGM_CREATE && bSTGM_CONVERT )
6025 return E_FAIL;
6028 * STGM_NOSCRATCH requires STGM_TRANSACTED
6030 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6031 return E_FAIL;
6034 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6035 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6037 if (bSTGM_NOSNAPSHOT)
6039 if ( ! ( bSTGM_TRANSACTED &&
6040 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6041 return E_FAIL;
6044 return S_OK;
6047 /****************************************************************************
6048 * GetShareModeFromSTGM
6050 * This method will return a share mode flag from a STGM value.
6051 * The STGM value is assumed valid.
6053 static DWORD GetShareModeFromSTGM(DWORD stgm)
6055 DWORD dwShareMode = 0;
6056 BOOL bSTGM_SHARE_DENY_NONE =
6057 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6059 BOOL bSTGM_SHARE_DENY_READ =
6060 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6062 BOOL bSTGM_SHARE_DENY_WRITE =
6063 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6065 BOOL bSTGM_SHARE_EXCLUSIVE =
6066 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6068 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6069 dwShareMode = 0;
6071 if (bSTGM_SHARE_DENY_NONE)
6072 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6074 if (bSTGM_SHARE_DENY_WRITE)
6075 dwShareMode = FILE_SHARE_READ;
6077 return dwShareMode;
6080 /****************************************************************************
6081 * GetAccessModeFromSTGM
6083 * This method will return an access mode flag from a STGM value.
6084 * The STGM value is assumed valid.
6086 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6088 DWORD dwDesiredAccess = GENERIC_READ;
6089 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6090 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6091 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6093 if (bSTGM_READ)
6094 dwDesiredAccess = GENERIC_READ;
6096 if (bSTGM_WRITE)
6097 dwDesiredAccess |= GENERIC_WRITE;
6099 if (bSTGM_READWRITE)
6100 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6102 return dwDesiredAccess;
6105 /****************************************************************************
6106 * GetCreationModeFromSTGM
6108 * This method will return a creation mode flag from a STGM value.
6109 * The STGM value is assumed valid.
6111 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6113 if ( stgm & STGM_CREATE)
6114 return CREATE_ALWAYS;
6115 if (stgm & STGM_CONVERT) {
6116 FIXME("STGM_CONVERT not implemented!\n");
6117 return CREATE_NEW;
6119 /* All other cases */
6120 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6121 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6122 return CREATE_NEW;
6126 /*************************************************************************
6127 * OLECONVERT_LoadOLE10 [Internal]
6129 * Loads the OLE10 STREAM to memory
6131 * PARAMS
6132 * pOleStream [I] The OLESTREAM
6133 * pData [I] Data Structure for the OLESTREAM Data
6135 * RETURNS
6136 * Success: S_OK
6137 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6138 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6140 * NOTES
6141 * This function is used by OleConvertOLESTREAMToIStorage only.
6143 * Memory allocated for pData must be freed by the caller
6145 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6147 DWORD dwSize;
6148 HRESULT hRes = S_OK;
6149 int nTryCnt=0;
6150 int max_try = 6;
6152 pData->pData = NULL;
6153 pData->pstrOleObjFileName = (CHAR *) NULL;
6155 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6157 /* Get the OleID */
6158 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6159 if(dwSize != sizeof(pData->dwOleID))
6161 hRes = CONVERT10_E_OLESTREAM_GET;
6163 else if(pData->dwOleID != OLESTREAM_ID)
6165 hRes = CONVERT10_E_OLESTREAM_FMT;
6167 else
6169 hRes = S_OK;
6170 break;
6174 if(hRes == S_OK)
6176 /* Get the TypeID...more info needed for this field */
6177 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6178 if(dwSize != sizeof(pData->dwTypeID))
6180 hRes = CONVERT10_E_OLESTREAM_GET;
6183 if(hRes == S_OK)
6185 if(pData->dwTypeID != 0)
6187 /* Get the length of the OleTypeName */
6188 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6189 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6191 hRes = CONVERT10_E_OLESTREAM_GET;
6194 if(hRes == S_OK)
6196 if(pData->dwOleTypeNameLength > 0)
6198 /* Get the OleTypeName */
6199 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6200 if(dwSize != pData->dwOleTypeNameLength)
6202 hRes = CONVERT10_E_OLESTREAM_GET;
6206 if(bStrem1)
6208 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6209 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6211 hRes = CONVERT10_E_OLESTREAM_GET;
6213 if(hRes == S_OK)
6215 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6216 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6217 pData->pstrOleObjFileName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6218 if(pData->pstrOleObjFileName)
6220 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6221 if(dwSize != pData->dwOleObjFileNameLength)
6223 hRes = CONVERT10_E_OLESTREAM_GET;
6226 else
6227 hRes = CONVERT10_E_OLESTREAM_GET;
6230 else
6232 /* Get the Width of the Metafile */
6233 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6234 if(dwSize != sizeof(pData->dwMetaFileWidth))
6236 hRes = CONVERT10_E_OLESTREAM_GET;
6238 if(hRes == S_OK)
6240 /* Get the Height of the Metafile */
6241 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6242 if(dwSize != sizeof(pData->dwMetaFileHeight))
6244 hRes = CONVERT10_E_OLESTREAM_GET;
6248 if(hRes == S_OK)
6250 /* Get the Length of the Data */
6251 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6252 if(dwSize != sizeof(pData->dwDataLength))
6254 hRes = CONVERT10_E_OLESTREAM_GET;
6258 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6260 if(!bStrem1) /* if it is a second OLE stream data */
6262 pData->dwDataLength -= 8;
6263 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6264 if(dwSize != sizeof(pData->strUnknown))
6266 hRes = CONVERT10_E_OLESTREAM_GET;
6270 if(hRes == S_OK)
6272 if(pData->dwDataLength > 0)
6274 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6276 /* Get Data (ex. IStorage, Metafile, or BMP) */
6277 if(pData->pData)
6279 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6280 if(dwSize != pData->dwDataLength)
6282 hRes = CONVERT10_E_OLESTREAM_GET;
6285 else
6287 hRes = CONVERT10_E_OLESTREAM_GET;
6293 return hRes;
6296 /*************************************************************************
6297 * OLECONVERT_SaveOLE10 [Internal]
6299 * Saves the OLE10 STREAM From memory
6301 * PARAMS
6302 * pData [I] Data Structure for the OLESTREAM Data
6303 * pOleStream [I] The OLESTREAM to save
6305 * RETURNS
6306 * Success: S_OK
6307 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6309 * NOTES
6310 * This function is used by OleConvertIStorageToOLESTREAM only.
6313 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6315 DWORD dwSize;
6316 HRESULT hRes = S_OK;
6319 /* Set the OleID */
6320 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6321 if(dwSize != sizeof(pData->dwOleID))
6323 hRes = CONVERT10_E_OLESTREAM_PUT;
6326 if(hRes == S_OK)
6328 /* Set the TypeID */
6329 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6330 if(dwSize != sizeof(pData->dwTypeID))
6332 hRes = CONVERT10_E_OLESTREAM_PUT;
6336 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6338 /* Set the Length of the OleTypeName */
6339 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6340 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6342 hRes = CONVERT10_E_OLESTREAM_PUT;
6345 if(hRes == S_OK)
6347 if(pData->dwOleTypeNameLength > 0)
6349 /* Set the OleTypeName */
6350 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6351 if(dwSize != pData->dwOleTypeNameLength)
6353 hRes = CONVERT10_E_OLESTREAM_PUT;
6358 if(hRes == S_OK)
6360 /* Set the width of the Metafile */
6361 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6362 if(dwSize != sizeof(pData->dwMetaFileWidth))
6364 hRes = CONVERT10_E_OLESTREAM_PUT;
6368 if(hRes == S_OK)
6370 /* Set the height of the Metafile */
6371 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6372 if(dwSize != sizeof(pData->dwMetaFileHeight))
6374 hRes = CONVERT10_E_OLESTREAM_PUT;
6378 if(hRes == S_OK)
6380 /* Set the length of the Data */
6381 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6382 if(dwSize != sizeof(pData->dwDataLength))
6384 hRes = CONVERT10_E_OLESTREAM_PUT;
6388 if(hRes == S_OK)
6390 if(pData->dwDataLength > 0)
6392 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6393 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6394 if(dwSize != pData->dwDataLength)
6396 hRes = CONVERT10_E_OLESTREAM_PUT;
6401 return hRes;
6404 /*************************************************************************
6405 * OLECONVERT_GetOLE20FromOLE10[Internal]
6407 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6408 * opens it, and copies the content to the dest IStorage for
6409 * OleConvertOLESTREAMToIStorage
6412 * PARAMS
6413 * pDestStorage [I] The IStorage to copy the data to
6414 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6415 * nBufferLength [I] The size of the buffer
6417 * RETURNS
6418 * Nothing
6420 * NOTES
6424 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6426 HRESULT hRes;
6427 HANDLE hFile;
6428 IStorage *pTempStorage;
6429 DWORD dwNumOfBytesWritten;
6430 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6431 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6433 /* Create a temp File */
6434 GetTempPathW(MAX_PATH, wstrTempDir);
6435 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6436 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6438 if(hFile != INVALID_HANDLE_VALUE)
6440 /* Write IStorage Data to File */
6441 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6442 CloseHandle(hFile);
6444 /* Open and copy temp storage to the Dest Storage */
6445 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6446 if(hRes == S_OK)
6448 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6449 StorageBaseImpl_Release(pTempStorage);
6451 DeleteFileW(wstrTempFile);
6456 /*************************************************************************
6457 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6459 * Saves the OLE10 STREAM From memory
6461 * PARAMS
6462 * pStorage [I] The Src IStorage to copy
6463 * pData [I] The Dest Memory to write to.
6465 * RETURNS
6466 * The size in bytes allocated for pData
6468 * NOTES
6469 * Memory allocated for pData must be freed by the caller
6471 * Used by OleConvertIStorageToOLESTREAM only.
6474 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6476 HANDLE hFile;
6477 HRESULT hRes;
6478 DWORD nDataLength = 0;
6479 IStorage *pTempStorage;
6480 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6481 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6483 *pData = NULL;
6485 /* Create temp Storage */
6486 GetTempPathW(MAX_PATH, wstrTempDir);
6487 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6488 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6490 if(hRes == S_OK)
6492 /* Copy Src Storage to the Temp Storage */
6493 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6494 StorageBaseImpl_Release(pTempStorage);
6496 /* Open Temp Storage as a file and copy to memory */
6497 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6498 if(hFile != INVALID_HANDLE_VALUE)
6500 nDataLength = GetFileSize(hFile, NULL);
6501 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6502 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6503 CloseHandle(hFile);
6505 DeleteFileW(wstrTempFile);
6507 return nDataLength;
6510 /*************************************************************************
6511 * OLECONVERT_CreateOleStream [Internal]
6513 * Creates the "\001OLE" stream in the IStorage if necessary.
6515 * PARAMS
6516 * pStorage [I] Dest storage to create the stream in
6518 * RETURNS
6519 * Nothing
6521 * NOTES
6522 * This function is used by OleConvertOLESTREAMToIStorage only.
6524 * This stream is still unknown, MS Word seems to have extra data
6525 * but since the data is stored in the OLESTREAM there should be
6526 * no need to recreate the stream. If the stream is manually
6527 * deleted it will create it with this default data.
6530 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6532 HRESULT hRes;
6533 IStream *pStream;
6534 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6535 BYTE pOleStreamHeader [] =
6537 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6538 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6539 0x00, 0x00, 0x00, 0x00
6542 /* Create stream if not present */
6543 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6544 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6546 if(hRes == S_OK)
6548 /* Write default Data */
6549 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6550 IStream_Release(pStream);
6554 /* write a string to a stream, preceded by its length */
6555 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6557 HRESULT r;
6558 LPSTR str;
6559 DWORD len = 0;
6561 if( string )
6562 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6563 r = IStream_Write( stm, &len, sizeof(len), NULL);
6564 if( FAILED( r ) )
6565 return r;
6566 if(len == 0)
6567 return r;
6568 str = CoTaskMemAlloc( len );
6569 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6570 r = IStream_Write( stm, str, len, NULL);
6571 CoTaskMemFree( str );
6572 return r;
6575 /* read a string preceded by its length from a stream */
6576 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6578 HRESULT r;
6579 DWORD len, count = 0;
6580 LPSTR str;
6581 LPWSTR wstr;
6583 r = IStream_Read( stm, &len, sizeof(len), &count );
6584 if( FAILED( r ) )
6585 return r;
6586 if( count != sizeof(len) )
6587 return E_OUTOFMEMORY;
6589 TRACE("%ld bytes\n",len);
6591 str = CoTaskMemAlloc( len );
6592 if( !str )
6593 return E_OUTOFMEMORY;
6594 count = 0;
6595 r = IStream_Read( stm, str, len, &count );
6596 if( FAILED( r ) )
6597 return r;
6598 if( count != len )
6600 CoTaskMemFree( str );
6601 return E_OUTOFMEMORY;
6604 TRACE("Read string %s\n",debugstr_an(str,len));
6606 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6607 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6608 if( wstr )
6609 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6610 CoTaskMemFree( str );
6612 *string = wstr;
6614 return r;
6618 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6619 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6621 IStream *pstm;
6622 HRESULT r = S_OK;
6623 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6625 static const BYTE unknown1[12] =
6626 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6627 0xFF, 0xFF, 0xFF, 0xFF};
6628 static const BYTE unknown2[16] =
6629 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6630 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6632 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6633 debugstr_w(lpszUserType), debugstr_w(szClipName),
6634 debugstr_w(szProgIDName));
6636 /* Create a CompObj stream if it doesn't exist */
6637 r = IStorage_CreateStream(pstg, szwStreamName,
6638 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6639 if( FAILED (r) )
6640 return r;
6642 /* Write CompObj Structure to stream */
6643 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6645 if( SUCCEEDED( r ) )
6646 r = WriteClassStm( pstm, clsid );
6648 if( SUCCEEDED( r ) )
6649 r = STREAM_WriteString( pstm, lpszUserType );
6650 if( SUCCEEDED( r ) )
6651 r = STREAM_WriteString( pstm, szClipName );
6652 if( SUCCEEDED( r ) )
6653 r = STREAM_WriteString( pstm, szProgIDName );
6654 if( SUCCEEDED( r ) )
6655 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6657 IStream_Release( pstm );
6659 return r;
6662 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6663 static HRESULT CLSIDFromUserType(LPCWSTR lpszUserType, CLSID *clsid)
6665 LONG r, i, len;
6666 ULONG count;
6667 WCHAR szKey[0x40];
6668 HKEY hkey, hkeyclsid;
6669 LPWSTR buffer = NULL;
6670 BOOL found = FALSE;
6671 static const WCHAR szclsid[] = { 'C','L','S','I','D',0 };
6673 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType));
6675 r = RegOpenKeyW( HKEY_CLASSES_ROOT, szclsid, &hkeyclsid );
6676 if( r )
6677 return E_INVALIDARG;
6679 len = lstrlenW( lpszUserType ) + 1;
6680 buffer = CoTaskMemAlloc( len * sizeof (WCHAR) );
6681 if( !buffer )
6682 goto end;
6684 for(i=0; !found; i++ )
6686 r = RegEnumKeyW( hkeyclsid, i, szKey, sizeof(szKey)/sizeof(WCHAR));
6687 if( r != ERROR_SUCCESS )
6688 break;
6689 hkey = 0;
6690 r = RegOpenKeyW( hkeyclsid, szKey, &hkey );
6691 if( r != ERROR_SUCCESS )
6692 break;
6693 count = len * sizeof (WCHAR);
6694 r = RegQueryValueW( hkey, NULL, buffer, &count );
6695 found = ( r == ERROR_SUCCESS ) &&
6696 ( count == len*sizeof(WCHAR) ) &&
6697 !lstrcmpW( buffer, lpszUserType ) ;
6698 RegCloseKey( hkey );
6701 end:
6702 if( buffer )
6703 CoTaskMemFree( buffer );
6704 RegCloseKey( hkeyclsid );
6706 if ( !found )
6707 return E_INVALIDARG;
6709 TRACE("clsid is %s\n", debugstr_w( szKey ) );
6711 r = CLSIDFromString( szKey, clsid );
6713 return r;
6717 /***********************************************************************
6718 * WriteFmtUserTypeStg (OLE32.@)
6720 HRESULT WINAPI WriteFmtUserTypeStg(
6721 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6723 HRESULT r;
6724 WCHAR szwClipName[0x40];
6725 WCHAR szCLSIDName[OLESTREAM_MAX_STR_LEN];
6726 CLSID clsid;
6727 LPWSTR wstrProgID;
6728 DWORD n;
6729 LPMALLOC allocator = NULL;
6731 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6733 r = CoGetMalloc(0, &allocator);
6734 if( FAILED( r) )
6735 return E_OUTOFMEMORY;
6737 /* get the clipboard format name */
6738 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6739 szwClipName[n]=0;
6741 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6743 /* Get the CLSID */
6744 szCLSIDName[0]=0;
6745 r = CLSIDFromUserType(lpszUserType, &clsid);
6746 if( FAILED( r ) )
6747 return r;
6749 TRACE("CLSID is %s\n",debugstr_guid(&clsid));
6751 /* get the real program ID */
6752 r = ProgIDFromCLSID( &clsid, &wstrProgID);
6753 if( FAILED( r ) )
6754 return r;
6756 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6758 /* if we have a good string, write the stream */
6759 if( wstrProgID )
6760 r = STORAGE_WriteCompObj( pstg, &clsid,
6761 lpszUserType, szwClipName, wstrProgID );
6762 else
6763 r = E_OUTOFMEMORY;
6765 IMalloc_Free( allocator, wstrProgID);
6767 return r;
6771 /******************************************************************************
6772 * ReadFmtUserTypeStg [OLE32.@]
6774 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6776 HRESULT r;
6777 IStream *stm = 0;
6778 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6779 unsigned char unknown1[12];
6780 unsigned char unknown2[16];
6781 DWORD count;
6782 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6783 CLSID clsid;
6785 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6787 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6788 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6789 if( FAILED ( r ) )
6791 ERR("Failed to open stream\n");
6792 return r;
6795 /* read the various parts of the structure */
6796 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6797 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6798 goto end;
6799 r = ReadClassStm( stm, &clsid );
6800 if( FAILED( r ) )
6801 goto end;
6803 r = STREAM_ReadString( stm, &szCLSIDName );
6804 if( FAILED( r ) )
6805 goto end;
6807 r = STREAM_ReadString( stm, &szOleTypeName );
6808 if( FAILED( r ) )
6809 goto end;
6811 r = STREAM_ReadString( stm, &szProgIDName );
6812 if( FAILED( r ) )
6813 goto end;
6815 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6816 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6817 goto end;
6819 /* ok, success... now we just need to store what we found */
6820 if( pcf )
6821 *pcf = RegisterClipboardFormatW( szOleTypeName );
6822 CoTaskMemFree( szOleTypeName );
6824 if( lplpszUserType )
6825 *lplpszUserType = szCLSIDName;
6826 CoTaskMemFree( szProgIDName );
6828 end:
6829 IStream_Release( stm );
6831 return r;
6835 /*************************************************************************
6836 * OLECONVERT_CreateCompObjStream [Internal]
6838 * Creates a "\001CompObj" is the destination IStorage if necessary.
6840 * PARAMS
6841 * pStorage [I] The dest IStorage to create the CompObj Stream
6842 * if necessary.
6843 * strOleTypeName [I] The ProgID
6845 * RETURNS
6846 * Success: S_OK
6847 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6849 * NOTES
6850 * This function is used by OleConvertOLESTREAMToIStorage only.
6852 * The stream data is stored in the OLESTREAM and there should be
6853 * no need to recreate the stream. If the stream is manually
6854 * deleted it will attempt to create it by querying the registry.
6858 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6860 IStream *pStream;
6861 HRESULT hStorageRes, hRes = S_OK;
6862 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6863 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6864 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
6866 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6867 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6869 /* Initialize the CompObj structure */
6870 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6871 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6872 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6875 /* Create a CompObj stream if it doesn't exist */
6876 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6877 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6878 if(hStorageRes == S_OK)
6880 /* copy the OleTypeName to the compobj struct */
6881 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6882 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6884 /* copy the OleTypeName to the compobj struct */
6885 /* Note: in the test made, these were Identical */
6886 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6887 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6889 /* Get the CLSID */
6890 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
6891 bufferW, OLESTREAM_MAX_STR_LEN );
6892 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
6894 if(hRes == S_OK)
6896 HKEY hKey;
6897 LONG hErr;
6898 /* Get the CLSID Default Name from the Registry */
6899 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6900 if(hErr == ERROR_SUCCESS)
6902 char strTemp[OLESTREAM_MAX_STR_LEN];
6903 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6904 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6905 if(hErr == ERROR_SUCCESS)
6907 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6909 RegCloseKey(hKey);
6913 /* Write CompObj Structure to stream */
6914 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6916 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6918 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6919 if(IStorageCompObj.dwCLSIDNameLength > 0)
6921 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6923 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6924 if(IStorageCompObj.dwOleTypeNameLength > 0)
6926 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6928 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6929 if(IStorageCompObj.dwProgIDNameLength > 0)
6931 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6933 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6934 IStream_Release(pStream);
6936 return hRes;
6940 /*************************************************************************
6941 * OLECONVERT_CreateOlePresStream[Internal]
6943 * Creates the "\002OlePres000" Stream with the Metafile data
6945 * PARAMS
6946 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6947 * dwExtentX [I] Width of the Metafile
6948 * dwExtentY [I] Height of the Metafile
6949 * pData [I] Metafile data
6950 * dwDataLength [I] Size of the Metafile data
6952 * RETURNS
6953 * Success: S_OK
6954 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6956 * NOTES
6957 * This function is used by OleConvertOLESTREAMToIStorage only.
6960 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6962 HRESULT hRes;
6963 IStream *pStream;
6964 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6965 BYTE pOlePresStreamHeader [] =
6967 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6968 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6969 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6970 0x00, 0x00, 0x00, 0x00
6973 BYTE pOlePresStreamHeaderEmpty [] =
6975 0x00, 0x00, 0x00, 0x00,
6976 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6977 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6978 0x00, 0x00, 0x00, 0x00
6981 /* Create the OlePres000 Stream */
6982 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6983 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6985 if(hRes == S_OK)
6987 DWORD nHeaderSize;
6988 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6990 memset(&OlePres, 0, sizeof(OlePres));
6991 /* Do we have any metafile data to save */
6992 if(dwDataLength > 0)
6994 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6995 nHeaderSize = sizeof(pOlePresStreamHeader);
6997 else
6999 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7000 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7002 /* Set width and height of the metafile */
7003 OlePres.dwExtentX = dwExtentX;
7004 OlePres.dwExtentY = -dwExtentY;
7006 /* Set Data and Length */
7007 if(dwDataLength > sizeof(METAFILEPICT16))
7009 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7010 OlePres.pData = &(pData[8]);
7012 /* Save OlePres000 Data to Stream */
7013 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7014 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7015 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7016 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7017 if(OlePres.dwSize > 0)
7019 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7021 IStream_Release(pStream);
7025 /*************************************************************************
7026 * OLECONVERT_CreateOle10NativeStream [Internal]
7028 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7030 * PARAMS
7031 * pStorage [I] Dest storage to create the stream in
7032 * pData [I] Ole10 Native Data (ex. bmp)
7033 * dwDataLength [I] Size of the Ole10 Native Data
7035 * RETURNS
7036 * Nothing
7038 * NOTES
7039 * This function is used by OleConvertOLESTREAMToIStorage only.
7041 * Might need to verify the data and return appropriate error message
7044 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7046 HRESULT hRes;
7047 IStream *pStream;
7048 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7050 /* Create the Ole10Native Stream */
7051 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7052 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7054 if(hRes == S_OK)
7056 /* Write info to stream */
7057 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7058 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7059 IStream_Release(pStream);
7064 /*************************************************************************
7065 * OLECONVERT_GetOLE10ProgID [Internal]
7067 * Finds the ProgID (or OleTypeID) from the IStorage
7069 * PARAMS
7070 * pStorage [I] The Src IStorage to get the ProgID
7071 * strProgID [I] the ProgID string to get
7072 * dwSize [I] the size of the string
7074 * RETURNS
7075 * Success: S_OK
7076 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7078 * NOTES
7079 * This function is used by OleConvertIStorageToOLESTREAM only.
7083 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7085 HRESULT hRes;
7086 IStream *pStream;
7087 LARGE_INTEGER iSeekPos;
7088 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7089 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7091 /* Open the CompObj Stream */
7092 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7093 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7094 if(hRes == S_OK)
7097 /*Get the OleType from the CompObj Stream */
7098 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7099 iSeekPos.u.HighPart = 0;
7101 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7102 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7103 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7104 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7105 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7106 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7107 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7109 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7110 if(*dwSize > 0)
7112 IStream_Read(pStream, strProgID, *dwSize, NULL);
7114 IStream_Release(pStream);
7116 else
7118 STATSTG stat;
7119 LPOLESTR wstrProgID;
7121 /* Get the OleType from the registry */
7122 REFCLSID clsid = &(stat.clsid);
7123 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7124 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7125 if(hRes == S_OK)
7127 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7131 return hRes;
7134 /*************************************************************************
7135 * OLECONVERT_GetOle10PresData [Internal]
7137 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7139 * PARAMS
7140 * pStorage [I] Src IStroage
7141 * pOleStream [I] Dest OleStream Mem Struct
7143 * RETURNS
7144 * Nothing
7146 * NOTES
7147 * This function is used by OleConvertIStorageToOLESTREAM only.
7149 * Memory allocated for pData must be freed by the caller
7153 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7156 HRESULT hRes;
7157 IStream *pStream;
7158 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7160 /* Initialize Default data for OLESTREAM */
7161 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7162 pOleStreamData[0].dwTypeID = 2;
7163 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7164 pOleStreamData[1].dwTypeID = 0;
7165 pOleStreamData[0].dwMetaFileWidth = 0;
7166 pOleStreamData[0].dwMetaFileHeight = 0;
7167 pOleStreamData[0].pData = NULL;
7168 pOleStreamData[1].pData = NULL;
7170 /* Open Ole10Native Stream */
7171 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7172 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7173 if(hRes == S_OK)
7176 /* Read Size and Data */
7177 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7178 if(pOleStreamData->dwDataLength > 0)
7180 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7181 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7183 IStream_Release(pStream);
7189 /*************************************************************************
7190 * OLECONVERT_GetOle20PresData[Internal]
7192 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7194 * PARAMS
7195 * pStorage [I] Src IStroage
7196 * pOleStreamData [I] Dest OleStream Mem Struct
7198 * RETURNS
7199 * Nothing
7201 * NOTES
7202 * This function is used by OleConvertIStorageToOLESTREAM only.
7204 * Memory allocated for pData must be freed by the caller
7206 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7208 HRESULT hRes;
7209 IStream *pStream;
7210 OLECONVERT_ISTORAGE_OLEPRES olePress;
7211 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7213 /* Initialize Default data for OLESTREAM */
7214 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7215 pOleStreamData[0].dwTypeID = 2;
7216 pOleStreamData[0].dwMetaFileWidth = 0;
7217 pOleStreamData[0].dwMetaFileHeight = 0;
7218 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7219 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7220 pOleStreamData[1].dwTypeID = 0;
7221 pOleStreamData[1].dwOleTypeNameLength = 0;
7222 pOleStreamData[1].strOleTypeName[0] = 0;
7223 pOleStreamData[1].dwMetaFileWidth = 0;
7224 pOleStreamData[1].dwMetaFileHeight = 0;
7225 pOleStreamData[1].pData = NULL;
7226 pOleStreamData[1].dwDataLength = 0;
7229 /* Open OlePress000 stream */
7230 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7231 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7232 if(hRes == S_OK)
7234 LARGE_INTEGER iSeekPos;
7235 METAFILEPICT16 MetaFilePict;
7236 static const char strMetafilePictName[] = "METAFILEPICT";
7238 /* Set the TypeID for a Metafile */
7239 pOleStreamData[1].dwTypeID = 5;
7241 /* Set the OleTypeName to Metafile */
7242 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7243 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7245 iSeekPos.u.HighPart = 0;
7246 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7248 /* Get Presentation Data */
7249 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7250 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7251 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7252 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7254 /*Set width and Height */
7255 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7256 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7257 if(olePress.dwSize > 0)
7259 /* Set Length */
7260 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7262 /* Set MetaFilePict struct */
7263 MetaFilePict.mm = 8;
7264 MetaFilePict.xExt = olePress.dwExtentX;
7265 MetaFilePict.yExt = olePress.dwExtentY;
7266 MetaFilePict.hMF = 0;
7268 /* Get Metafile Data */
7269 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7270 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7271 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7273 IStream_Release(pStream);
7277 /*************************************************************************
7278 * OleConvertOLESTREAMToIStorage [OLE32.@]
7280 * Read info on MSDN
7282 * TODO
7283 * DVTARGETDEVICE paramenter is not handled
7284 * Still unsure of some mem fields for OLE 10 Stream
7285 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7286 * and "\001OLE" streams
7289 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7290 LPOLESTREAM pOleStream,
7291 LPSTORAGE pstg,
7292 const DVTARGETDEVICE* ptd)
7294 int i;
7295 HRESULT hRes=S_OK;
7296 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7298 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7300 if(ptd != NULL)
7302 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7305 if(pstg == NULL || pOleStream == NULL)
7307 hRes = E_INVALIDARG;
7310 if(hRes == S_OK)
7312 /* Load the OLESTREAM to Memory */
7313 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7316 if(hRes == S_OK)
7318 /* Load the OLESTREAM to Memory (part 2)*/
7319 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7322 if(hRes == S_OK)
7325 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7327 /* Do we have the IStorage Data in the OLESTREAM */
7328 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7330 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7331 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7333 else
7335 /* It must be an original OLE 1.0 source */
7336 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7339 else
7341 /* It must be an original OLE 1.0 source */
7342 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7345 /* Create CompObj Stream if necessary */
7346 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7347 if(hRes == S_OK)
7349 /*Create the Ole Stream if necessary */
7350 OLECONVERT_CreateOleStream(pstg);
7355 /* Free allocated memory */
7356 for(i=0; i < 2; i++)
7358 if(pOleStreamData[i].pData != NULL)
7360 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7362 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7364 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7365 pOleStreamData[i].pstrOleObjFileName = NULL;
7368 return hRes;
7371 /*************************************************************************
7372 * OleConvertIStorageToOLESTREAM [OLE32.@]
7374 * Read info on MSDN
7376 * Read info on MSDN
7378 * TODO
7379 * Still unsure of some mem fields for OLE 10 Stream
7380 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7381 * and "\001OLE" streams.
7384 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7385 LPSTORAGE pstg,
7386 LPOLESTREAM pOleStream)
7388 int i;
7389 HRESULT hRes = S_OK;
7390 IStream *pStream;
7391 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7392 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7395 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7397 if(pstg == NULL || pOleStream == NULL)
7399 hRes = E_INVALIDARG;
7401 if(hRes == S_OK)
7403 /* Get the ProgID */
7404 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7405 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7407 if(hRes == S_OK)
7409 /* Was it originally Ole10 */
7410 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7411 if(hRes == S_OK)
7413 IStream_Release(pStream);
7414 /* Get Presentation Data for Ole10Native */
7415 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7417 else
7419 /* Get Presentation Data (OLE20) */
7420 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7423 /* Save OLESTREAM */
7424 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7425 if(hRes == S_OK)
7427 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7432 /* Free allocated memory */
7433 for(i=0; i < 2; i++)
7435 if(pOleStreamData[i].pData != NULL)
7437 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7441 return hRes;
7444 /***********************************************************************
7445 * GetConvertStg (OLE32.@)
7447 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7448 FIXME("unimplemented stub!\n");
7449 return E_FAIL;
7452 /******************************************************************************
7453 * StgIsStorageFile [OLE32.@]
7455 HRESULT WINAPI
7456 StgIsStorageFile(LPCOLESTR fn)
7458 HANDLE hf;
7459 BYTE magic[8];
7460 DWORD bytes_read;
7462 TRACE("(\'%s\')\n", debugstr_w(fn));
7463 hf = CreateFileW(fn, GENERIC_READ,
7464 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7465 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7467 if (hf == INVALID_HANDLE_VALUE)
7468 return STG_E_FILENOTFOUND;
7470 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7472 WARN(" unable to read file\n");
7473 CloseHandle(hf);
7474 return S_FALSE;
7477 CloseHandle(hf);
7479 if (bytes_read != 8) {
7480 WARN(" too short\n");
7481 return S_FALSE;
7484 if (!memcmp(magic,STORAGE_magic,8)) {
7485 WARN(" -> YES\n");
7486 return S_OK;
7489 WARN(" -> Invalid header.\n");
7490 return S_FALSE;