Ref count increment/decrement cleanup.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob9aba5d8b8f600802b71459c4356df5660546e459
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 NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winnls.h"
38 #include "winuser.h"
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
42 #include "storage32.h"
43 #include "ole2.h" /* For Write/ReadClassStm */
45 #include "winreg.h"
46 #include "wine/wingdi16.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(storage);
50 #define FILE_BEGIN 0
53 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
54 #define OLESTREAM_ID 0x501
55 #define OLESTREAM_MAX_STR_LEN 255
57 static const char rootPropertyName[] = "Root Entry";
60 /* OLESTREAM memory structure to use for Get and Put Routines */
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 typedef struct
64 DWORD dwOleID;
65 DWORD dwTypeID;
66 DWORD dwOleTypeNameLength;
67 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
68 CHAR *pstrOleObjFileName;
69 DWORD dwOleObjFileNameLength;
70 DWORD dwMetaFileWidth;
71 DWORD dwMetaFileHeight;
72 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
73 DWORD dwDataLength;
74 BYTE *pData;
75 }OLECONVERT_OLESTREAM_DATA;
77 /* CompObj Stream structure */
78 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
79 typedef struct
81 BYTE byUnknown1[12];
82 CLSID clsid;
83 DWORD dwCLSIDNameLength;
84 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
85 DWORD dwOleTypeNameLength;
86 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
87 DWORD dwProgIDNameLength;
88 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
89 BYTE byUnknown2[16];
90 }OLECONVERT_ISTORAGE_COMPOBJ;
93 /* Ole Presention Stream structure */
94 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
95 typedef struct
97 BYTE byUnknown1[28];
98 DWORD dwExtentX;
99 DWORD dwExtentY;
100 DWORD dwSize;
101 BYTE *pData;
102 }OLECONVERT_ISTORAGE_OLEPRES;
106 /***********************************************************************
107 * Forward declaration of internal functions used by the method DestroyElement
109 static HRESULT deleteStorageProperty(
110 StorageImpl *parentStorage,
111 ULONG foundPropertyIndexToDelete,
112 StgProperty propertyToDelete);
114 static HRESULT deleteStreamProperty(
115 StorageImpl *parentStorage,
116 ULONG foundPropertyIndexToDelete,
117 StgProperty propertyToDelete);
119 static HRESULT findPlaceholder(
120 StorageImpl *storage,
121 ULONG propertyIndexToStore,
122 ULONG storagePropertyIndex,
123 INT typeOfRelation);
125 static HRESULT adjustPropertyChain(
126 StorageImpl *This,
127 StgProperty propertyToDelete,
128 StgProperty parentProperty,
129 ULONG parentPropertyId,
130 INT typeOfRelation);
132 /***********************************************************************
133 * Declaration of the functions used to manipulate StgProperty
136 static ULONG getFreeProperty(
137 StorageImpl *storage);
139 static void updatePropertyChain(
140 StorageImpl *storage,
141 ULONG newPropertyIndex,
142 StgProperty newProperty);
144 static LONG propertyNameCmp(
145 OLECHAR *newProperty,
146 OLECHAR *currentProperty);
149 /***********************************************************************
150 * Declaration of miscellaneous functions...
152 static HRESULT validateSTGM(DWORD stgmValue);
154 static DWORD GetShareModeFromSTGM(DWORD stgm);
155 static DWORD GetAccessModeFromSTGM(DWORD stgm);
156 static DWORD GetCreationModeFromSTGM(DWORD stgm);
159 * Virtual function table for the IStorage32Impl class.
161 static IStorageVtbl Storage32Impl_Vtbl =
163 StorageBaseImpl_QueryInterface,
164 StorageBaseImpl_AddRef,
165 StorageBaseImpl_Release,
166 StorageBaseImpl_CreateStream,
167 StorageBaseImpl_OpenStream,
168 StorageImpl_CreateStorage,
169 StorageBaseImpl_OpenStorage,
170 StorageImpl_CopyTo,
171 StorageImpl_MoveElementTo,
172 StorageImpl_Commit,
173 StorageImpl_Revert,
174 StorageBaseImpl_EnumElements,
175 StorageImpl_DestroyElement,
176 StorageBaseImpl_RenameElement,
177 StorageImpl_SetElementTimes,
178 StorageBaseImpl_SetClass,
179 StorageImpl_SetStateBits,
180 StorageImpl_Stat
184 * Virtual function table for the Storage32InternalImpl class.
186 static IStorageVtbl Storage32InternalImpl_Vtbl =
188 StorageBaseImpl_QueryInterface,
189 StorageBaseImpl_AddRef,
190 StorageBaseImpl_Release,
191 StorageBaseImpl_CreateStream,
192 StorageBaseImpl_OpenStream,
193 StorageImpl_CreateStorage,
194 StorageBaseImpl_OpenStorage,
195 StorageImpl_CopyTo,
196 StorageImpl_MoveElementTo,
197 StorageInternalImpl_Commit,
198 StorageInternalImpl_Revert,
199 StorageBaseImpl_EnumElements,
200 StorageImpl_DestroyElement,
201 StorageBaseImpl_RenameElement,
202 StorageImpl_SetElementTimes,
203 StorageBaseImpl_SetClass,
204 StorageImpl_SetStateBits,
205 StorageBaseImpl_Stat
209 * Virtual function table for the IEnumSTATSTGImpl class.
211 static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
213 IEnumSTATSTGImpl_QueryInterface,
214 IEnumSTATSTGImpl_AddRef,
215 IEnumSTATSTGImpl_Release,
216 IEnumSTATSTGImpl_Next,
217 IEnumSTATSTGImpl_Skip,
218 IEnumSTATSTGImpl_Reset,
219 IEnumSTATSTGImpl_Clone
226 /************************************************************************
227 ** Storage32BaseImpl implementatiion
230 /************************************************************************
231 * Storage32BaseImpl_QueryInterface (IUnknown)
233 * This method implements the common QueryInterface for all IStorage32
234 * implementations contained in this file.
236 * See Windows documentation for more details on IUnknown methods.
238 HRESULT WINAPI StorageBaseImpl_QueryInterface(
239 IStorage* iface,
240 REFIID riid,
241 void** ppvObject)
243 StorageBaseImpl *This = (StorageBaseImpl *)iface;
245 * Perform a sanity check on the parameters.
247 if ( (This==0) || (ppvObject==0) )
248 return E_INVALIDARG;
251 * Initialize the return parameter.
253 *ppvObject = 0;
256 * Compare the riid with the interface IDs implemented by this object.
258 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
260 *ppvObject = (IStorage*)This;
262 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
264 *ppvObject = (IStorage*)This;
268 * Check that we obtained an interface.
270 if ((*ppvObject)==0)
271 return E_NOINTERFACE;
274 * Query Interface always increases the reference count by one when it is
275 * successful
277 StorageBaseImpl_AddRef(iface);
279 return S_OK;
282 /************************************************************************
283 * Storage32BaseImpl_AddRef (IUnknown)
285 * This method implements the common AddRef for all IStorage32
286 * implementations contained in this file.
288 * See Windows documentation for more details on IUnknown methods.
290 ULONG WINAPI StorageBaseImpl_AddRef(
291 IStorage* iface)
293 StorageBaseImpl *This = (StorageBaseImpl *)iface;
294 return InterlockedIncrement(&This->ref);
297 /************************************************************************
298 * Storage32BaseImpl_Release (IUnknown)
300 * This method implements the common Release for all IStorage32
301 * implementations contained in this file.
303 * See Windows documentation for more details on IUnknown methods.
305 ULONG WINAPI StorageBaseImpl_Release(
306 IStorage* iface)
308 StorageBaseImpl *This = (StorageBaseImpl *)iface;
310 * Decrease the reference count on this object.
312 ULONG ref = InterlockedDecrement(&This->ref);
315 * If the reference count goes down to 0, perform suicide.
317 if (ref == 0)
320 * Since we are using a system of base-classes, we want to call the
321 * destructor of the appropriate derived class. To do this, we are
322 * using virtual functions to implement the destructor.
324 This->v_destructor(This);
327 return ref;
330 /************************************************************************
331 * Storage32BaseImpl_OpenStream (IStorage)
333 * This method will open the specified stream object from the current storage.
335 * See Windows documentation for more details on IStorage methods.
337 HRESULT WINAPI StorageBaseImpl_OpenStream(
338 IStorage* iface,
339 const OLECHAR* pwcsName, /* [string][in] */
340 void* reserved1, /* [unique][in] */
341 DWORD grfMode, /* [in] */
342 DWORD reserved2, /* [in] */
343 IStream** ppstm) /* [out] */
345 StorageBaseImpl *This = (StorageBaseImpl *)iface;
346 IEnumSTATSTGImpl* propertyEnumeration;
347 StgStreamImpl* newStream;
348 StgProperty currentProperty;
349 ULONG foundPropertyIndex;
350 HRESULT res = STG_E_UNKNOWN;
352 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
353 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
356 * Perform a sanity check on the parameters.
358 if ( (pwcsName==NULL) || (ppstm==0) )
360 res = E_INVALIDARG;
361 goto end;
365 * Initialize the out parameter
367 *ppstm = NULL;
370 * Validate the STGM flags
372 if ( FAILED( validateSTGM(grfMode) ))
374 res = STG_E_INVALIDFLAG;
375 goto end;
379 * As documented.
381 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
382 (grfMode & STGM_DELETEONRELEASE) ||
383 (grfMode & STGM_TRANSACTED) )
385 res = STG_E_INVALIDFUNCTION;
386 goto end;
390 * Create a property enumeration to search the properties
392 propertyEnumeration = IEnumSTATSTGImpl_Construct(
393 This->ancestorStorage,
394 This->rootPropertySetIndex);
397 * Search the enumeration for the property with the given name
399 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
400 propertyEnumeration,
401 pwcsName,
402 &currentProperty);
405 * Delete the property enumeration since we don't need it anymore
407 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
410 * If it was found, construct the stream object and return a pointer to it.
412 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
413 (currentProperty.propertyType==PROPTYPE_STREAM) )
415 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
417 if (newStream!=0)
419 newStream->grfMode = grfMode;
420 *ppstm = (IStream*)newStream;
423 * Since we are returning a pointer to the interface, we have to
424 * nail down the reference.
426 StgStreamImpl_AddRef(*ppstm);
428 res = S_OK;
429 goto end;
432 res = E_OUTOFMEMORY;
433 goto end;
436 res = STG_E_FILENOTFOUND;
438 end:
439 if (res == S_OK)
440 TRACE("<-- IStream %p\n", *ppstm);
441 TRACE("<-- %08lx\n", res);
442 return res;
445 /************************************************************************
446 * Storage32BaseImpl_OpenStorage (IStorage)
448 * This method will open a new storage object from the current storage.
450 * See Windows documentation for more details on IStorage methods.
452 HRESULT WINAPI StorageBaseImpl_OpenStorage(
453 IStorage* iface,
454 const OLECHAR* pwcsName, /* [string][unique][in] */
455 IStorage* pstgPriority, /* [unique][in] */
456 DWORD grfMode, /* [in] */
457 SNB snbExclude, /* [unique][in] */
458 DWORD reserved, /* [in] */
459 IStorage** ppstg) /* [out] */
461 StorageBaseImpl *This = (StorageBaseImpl *)iface;
462 StorageInternalImpl* newStorage;
463 IEnumSTATSTGImpl* propertyEnumeration;
464 StgProperty currentProperty;
465 ULONG foundPropertyIndex;
466 HRESULT res = STG_E_UNKNOWN;
468 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
469 iface, debugstr_w(pwcsName), pstgPriority,
470 grfMode, snbExclude, reserved, ppstg);
473 * Perform a sanity check on the parameters.
475 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
477 res = E_INVALIDARG;
478 goto end;
481 /* as documented */
482 if (snbExclude != NULL)
484 res = STG_E_INVALIDPARAMETER;
485 goto end;
489 * Validate the STGM flags
491 if ( FAILED( validateSTGM(grfMode) ))
493 res = STG_E_INVALIDFLAG;
494 goto end;
498 * As documented.
500 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
501 (grfMode & STGM_DELETEONRELEASE) ||
502 (grfMode & STGM_PRIORITY) )
504 res = STG_E_INVALIDFUNCTION;
505 goto end;
509 * Initialize the out parameter
511 *ppstg = NULL;
514 * Create a property enumeration to search the properties
516 propertyEnumeration = IEnumSTATSTGImpl_Construct(
517 This->ancestorStorage,
518 This->rootPropertySetIndex);
521 * Search the enumeration for the property with the given name
523 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
524 propertyEnumeration,
525 pwcsName,
526 &currentProperty);
529 * Delete the property enumeration since we don't need it anymore
531 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
534 * If it was found, construct the stream object and return a pointer to it.
536 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
537 (currentProperty.propertyType==PROPTYPE_STORAGE) )
540 * Construct a new Storage object
542 newStorage = StorageInternalImpl_Construct(
543 This->ancestorStorage,
544 foundPropertyIndex);
546 if (newStorage != 0)
548 *ppstg = (IStorage*)newStorage;
551 * Since we are returning a pointer to the interface,
552 * we have to nail down the reference.
554 StorageBaseImpl_AddRef(*ppstg);
556 res = S_OK;
557 goto end;
560 res = STG_E_INSUFFICIENTMEMORY;
561 goto end;
564 res = STG_E_FILENOTFOUND;
566 end:
567 TRACE("<-- %08lx\n", res);
568 return res;
571 /************************************************************************
572 * Storage32BaseImpl_EnumElements (IStorage)
574 * This method will create an enumerator object that can be used to
575 * retrieve informatino about all the properties in the storage object.
577 * See Windows documentation for more details on IStorage methods.
579 HRESULT WINAPI StorageBaseImpl_EnumElements(
580 IStorage* iface,
581 DWORD reserved1, /* [in] */
582 void* reserved2, /* [size_is][unique][in] */
583 DWORD reserved3, /* [in] */
584 IEnumSTATSTG** ppenum) /* [out] */
586 StorageBaseImpl *This = (StorageBaseImpl *)iface;
587 IEnumSTATSTGImpl* newEnum;
589 TRACE("(%p, %ld, %p, %ld, %p)\n",
590 iface, reserved1, reserved2, reserved3, ppenum);
593 * Perform a sanity check on the parameters.
595 if ( (This==0) || (ppenum==0))
596 return E_INVALIDARG;
599 * Construct the enumerator.
601 newEnum = IEnumSTATSTGImpl_Construct(
602 This->ancestorStorage,
603 This->rootPropertySetIndex);
605 if (newEnum!=0)
607 *ppenum = (IEnumSTATSTG*)newEnum;
610 * Don't forget to nail down a reference to the new object before
611 * returning it.
613 IEnumSTATSTGImpl_AddRef(*ppenum);
615 return S_OK;
618 return E_OUTOFMEMORY;
621 /************************************************************************
622 * Storage32BaseImpl_Stat (IStorage)
624 * This method will retrieve information about this storage object.
626 * See Windows documentation for more details on IStorage methods.
628 HRESULT WINAPI StorageBaseImpl_Stat(
629 IStorage* iface,
630 STATSTG* pstatstg, /* [out] */
631 DWORD grfStatFlag) /* [in] */
633 StorageBaseImpl *This = (StorageBaseImpl *)iface;
634 StgProperty curProperty;
635 BOOL readSuccessful;
636 HRESULT res = STG_E_UNKNOWN;
638 TRACE("(%p, %p, %lx)\n",
639 iface, pstatstg, grfStatFlag);
642 * Perform a sanity check on the parameters.
644 if ( (This==0) || (pstatstg==0))
646 res = E_INVALIDARG;
647 goto end;
651 * Read the information from the property.
653 readSuccessful = StorageImpl_ReadProperty(
654 This->ancestorStorage,
655 This->rootPropertySetIndex,
656 &curProperty);
658 if (readSuccessful)
660 StorageUtl_CopyPropertyToSTATSTG(
661 pstatstg,
662 &curProperty,
663 grfStatFlag);
665 res = S_OK;
666 goto end;
669 res = E_FAIL;
671 end:
672 if (res == S_OK)
674 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);
676 TRACE("<-- %08lx\n", res);
677 return res;
680 /************************************************************************
681 * Storage32BaseImpl_RenameElement (IStorage)
683 * This method will rename the specified element.
685 * See Windows documentation for more details on IStorage methods.
687 * Implementation notes: The method used to rename consists of creating a clone
688 * of the deleted StgProperty object setting it with the new name and to
689 * perform a DestroyElement of the old StgProperty.
691 HRESULT WINAPI StorageBaseImpl_RenameElement(
692 IStorage* iface,
693 const OLECHAR* pwcsOldName, /* [in] */
694 const OLECHAR* pwcsNewName) /* [in] */
696 StorageBaseImpl *This = (StorageBaseImpl *)iface;
697 IEnumSTATSTGImpl* propertyEnumeration;
698 StgProperty currentProperty;
699 ULONG foundPropertyIndex;
701 TRACE("(%p, %s, %s)\n",
702 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
705 * Create a property enumeration to search the properties
707 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
708 This->rootPropertySetIndex);
711 * Search the enumeration for the new property name
713 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
714 pwcsNewName,
715 &currentProperty);
717 if (foundPropertyIndex != PROPERTY_NULL)
720 * There is already a property with the new name
722 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
723 return STG_E_FILEALREADYEXISTS;
726 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
729 * Search the enumeration for the old property name
731 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
732 pwcsOldName,
733 &currentProperty);
736 * Delete the property enumeration since we don't need it anymore
738 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
740 if (foundPropertyIndex != PROPERTY_NULL)
742 StgProperty renamedProperty;
743 ULONG renamedPropertyIndex;
746 * Setup a new property for the renamed property
748 renamedProperty.sizeOfNameString =
749 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
751 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
752 return STG_E_INVALIDNAME;
754 strcpyW(renamedProperty.name, pwcsNewName);
756 renamedProperty.propertyType = currentProperty.propertyType;
757 renamedProperty.startingBlock = currentProperty.startingBlock;
758 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
759 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
761 renamedProperty.previousProperty = PROPERTY_NULL;
762 renamedProperty.nextProperty = PROPERTY_NULL;
765 * Bring the dirProperty link in case it is a storage and in which
766 * case the renamed storage elements don't require to be reorganized.
768 renamedProperty.dirProperty = currentProperty.dirProperty;
770 /* call CoFileTime to get the current time
771 renamedProperty.timeStampS1
772 renamedProperty.timeStampD1
773 renamedProperty.timeStampS2
774 renamedProperty.timeStampD2
775 renamedProperty.propertyUniqueID
779 * Obtain a free property in the property chain
781 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
784 * Save the new property into the new property spot
786 StorageImpl_WriteProperty(
787 This->ancestorStorage,
788 renamedPropertyIndex,
789 &renamedProperty);
792 * Find a spot in the property chain for our newly created property.
794 updatePropertyChain(
795 (StorageImpl*)This,
796 renamedPropertyIndex,
797 renamedProperty);
800 * At this point the renamed property has been inserted in the tree,
801 * now, before to Destroy the old property we must zeroed it's dirProperty
802 * otherwise the DestroyProperty below will zap it all and we do not want
803 * this to happen.
804 * Also, we fake that the old property is a storage so the DestroyProperty
805 * will not do a SetSize(0) on the stream data.
807 * This means that we need to tweek the StgProperty if it is a stream or a
808 * non empty storage.
810 StorageImpl_ReadProperty(This->ancestorStorage,
811 foundPropertyIndex,
812 &currentProperty);
814 currentProperty.dirProperty = PROPERTY_NULL;
815 currentProperty.propertyType = PROPTYPE_STORAGE;
816 StorageImpl_WriteProperty(
817 This->ancestorStorage,
818 foundPropertyIndex,
819 &currentProperty);
822 * Invoke Destroy to get rid of the ole property and automatically redo
823 * the linking of it's previous and next members...
825 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
828 else
831 * There is no property with the old name
833 return STG_E_FILENOTFOUND;
836 return S_OK;
839 /************************************************************************
840 * Storage32BaseImpl_CreateStream (IStorage)
842 * This method will create a stream object within this storage
844 * See Windows documentation for more details on IStorage methods.
846 HRESULT WINAPI StorageBaseImpl_CreateStream(
847 IStorage* iface,
848 const OLECHAR* pwcsName, /* [string][in] */
849 DWORD grfMode, /* [in] */
850 DWORD reserved1, /* [in] */
851 DWORD reserved2, /* [in] */
852 IStream** ppstm) /* [out] */
854 StorageBaseImpl *This = (StorageBaseImpl *)iface;
855 IEnumSTATSTGImpl* propertyEnumeration;
856 StgStreamImpl* newStream;
857 StgProperty currentProperty, newStreamProperty;
858 ULONG foundPropertyIndex, newPropertyIndex;
860 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
861 iface, debugstr_w(pwcsName), grfMode,
862 reserved1, reserved2, ppstm);
865 * Validate parameters
867 if (ppstm == 0)
868 return STG_E_INVALIDPOINTER;
870 if (pwcsName == 0)
871 return STG_E_INVALIDNAME;
874 * Validate the STGM flags
876 if ( FAILED( validateSTGM(grfMode) ))
877 return STG_E_INVALIDFLAG;
880 * As documented.
882 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
883 (grfMode & STGM_DELETEONRELEASE) ||
884 (grfMode & STGM_TRANSACTED) )
885 return STG_E_INVALIDFUNCTION;
888 * Initialize the out parameter
890 *ppstm = 0;
893 * Create a property enumeration to search the properties
895 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
896 This->rootPropertySetIndex);
898 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
899 pwcsName,
900 &currentProperty);
902 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
904 if (foundPropertyIndex != PROPERTY_NULL)
907 * An element with this name already exists
909 if (grfMode & STGM_CREATE)
911 IStorage_DestroyElement(iface, pwcsName);
913 else
914 return STG_E_FILEALREADYEXISTS;
918 * memset the empty property
920 memset(&newStreamProperty, 0, sizeof(StgProperty));
922 newStreamProperty.sizeOfNameString =
923 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
925 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
926 return STG_E_INVALIDNAME;
928 strcpyW(newStreamProperty.name, pwcsName);
930 newStreamProperty.propertyType = PROPTYPE_STREAM;
931 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
932 newStreamProperty.size.u.LowPart = 0;
933 newStreamProperty.size.u.HighPart = 0;
935 newStreamProperty.previousProperty = PROPERTY_NULL;
936 newStreamProperty.nextProperty = PROPERTY_NULL;
937 newStreamProperty.dirProperty = PROPERTY_NULL;
939 /* call CoFileTime to get the current time
940 newStreamProperty.timeStampS1
941 newStreamProperty.timeStampD1
942 newStreamProperty.timeStampS2
943 newStreamProperty.timeStampD2
946 /* newStreamProperty.propertyUniqueID */
949 * Get a free property or create a new one
951 newPropertyIndex = getFreeProperty(This->ancestorStorage);
954 * Save the new property into the new property spot
956 StorageImpl_WriteProperty(
957 This->ancestorStorage,
958 newPropertyIndex,
959 &newStreamProperty);
962 * Find a spot in the property chain for our newly created property.
964 updatePropertyChain(
965 (StorageImpl*)This,
966 newPropertyIndex,
967 newStreamProperty);
970 * Open the stream to return it.
972 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
974 if (newStream != 0)
976 *ppstm = (IStream*)newStream;
979 * Since we are returning a pointer to the interface, we have to nail down
980 * the reference.
982 StgStreamImpl_AddRef(*ppstm);
984 else
986 return STG_E_INSUFFICIENTMEMORY;
989 return S_OK;
992 /************************************************************************
993 * Storage32BaseImpl_SetClass (IStorage)
995 * This method will write the specified CLSID in the property of this
996 * storage.
998 * See Windows documentation for more details on IStorage methods.
1000 HRESULT WINAPI StorageBaseImpl_SetClass(
1001 IStorage* iface,
1002 REFCLSID clsid) /* [in] */
1004 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1005 HRESULT hRes = E_FAIL;
1006 StgProperty curProperty;
1007 BOOL success;
1009 TRACE("(%p, %p)\n", iface, clsid);
1011 success = StorageImpl_ReadProperty(This->ancestorStorage,
1012 This->rootPropertySetIndex,
1013 &curProperty);
1014 if (success)
1016 curProperty.propertyUniqueID = *clsid;
1018 success = StorageImpl_WriteProperty(This->ancestorStorage,
1019 This->rootPropertySetIndex,
1020 &curProperty);
1021 if (success)
1022 hRes = S_OK;
1025 return hRes;
1028 /************************************************************************
1029 ** Storage32Impl implementation
1032 /************************************************************************
1033 * Storage32Impl_CreateStorage (IStorage)
1035 * This method will create the storage object within the provided storage.
1037 * See Windows documentation for more details on IStorage methods.
1039 HRESULT WINAPI StorageImpl_CreateStorage(
1040 IStorage* iface,
1041 const OLECHAR *pwcsName, /* [string][in] */
1042 DWORD grfMode, /* [in] */
1043 DWORD reserved1, /* [in] */
1044 DWORD reserved2, /* [in] */
1045 IStorage **ppstg) /* [out] */
1047 StorageImpl* const This=(StorageImpl*)iface;
1049 IEnumSTATSTGImpl *propertyEnumeration;
1050 StgProperty currentProperty;
1051 StgProperty newProperty;
1052 ULONG foundPropertyIndex;
1053 ULONG newPropertyIndex;
1054 HRESULT hr;
1056 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1057 iface, debugstr_w(pwcsName), grfMode,
1058 reserved1, reserved2, ppstg);
1061 * Validate parameters
1063 if (ppstg == 0)
1064 return STG_E_INVALIDPOINTER;
1066 if (pwcsName == 0)
1067 return STG_E_INVALIDNAME;
1070 * Validate the STGM flags
1072 if ( FAILED( validateSTGM(grfMode) ) ||
1073 (grfMode & STGM_DELETEONRELEASE) )
1074 return STG_E_INVALIDFLAG;
1077 * Initialize the out parameter
1079 *ppstg = 0;
1082 * Create a property enumeration and search the properties
1084 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1085 This->rootPropertySetIndex);
1087 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1088 pwcsName,
1089 &currentProperty);
1090 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1092 if (foundPropertyIndex != PROPERTY_NULL)
1095 * An element with this name already exists
1097 if (grfMode & STGM_CREATE)
1098 IStorage_DestroyElement(iface, pwcsName);
1099 else
1100 return STG_E_FILEALREADYEXISTS;
1104 * memset the empty property
1106 memset(&newProperty, 0, sizeof(StgProperty));
1108 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1110 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1111 return STG_E_INVALIDNAME;
1113 strcpyW(newProperty.name, pwcsName);
1115 newProperty.propertyType = PROPTYPE_STORAGE;
1116 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1117 newProperty.size.u.LowPart = 0;
1118 newProperty.size.u.HighPart = 0;
1120 newProperty.previousProperty = PROPERTY_NULL;
1121 newProperty.nextProperty = PROPERTY_NULL;
1122 newProperty.dirProperty = PROPERTY_NULL;
1124 /* call CoFileTime to get the current time
1125 newProperty.timeStampS1
1126 newProperty.timeStampD1
1127 newProperty.timeStampS2
1128 newProperty.timeStampD2
1131 /* newStorageProperty.propertyUniqueID */
1134 * Obtain a free property in the property chain
1136 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1139 * Save the new property into the new property spot
1141 StorageImpl_WriteProperty(
1142 This->ancestorStorage,
1143 newPropertyIndex,
1144 &newProperty);
1147 * Find a spot in the property chain for our newly created property.
1149 updatePropertyChain(
1150 This,
1151 newPropertyIndex,
1152 newProperty);
1155 * Open it to get a pointer to return.
1157 hr = IStorage_OpenStorage(
1158 iface,
1159 (OLECHAR*)pwcsName,
1161 grfMode,
1164 ppstg);
1166 if( (hr != S_OK) || (*ppstg == NULL))
1168 return hr;
1172 return S_OK;
1176 /***************************************************************************
1178 * Internal Method
1180 * Get a free property or create a new one.
1182 static ULONG getFreeProperty(
1183 StorageImpl *storage)
1185 ULONG currentPropertyIndex = 0;
1186 ULONG newPropertyIndex = PROPERTY_NULL;
1187 BOOL readSuccessful = TRUE;
1188 StgProperty currentProperty;
1193 * Start by reading the root property
1195 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1196 currentPropertyIndex,
1197 &currentProperty);
1198 if (readSuccessful)
1200 if (currentProperty.sizeOfNameString == 0)
1203 * The property existis and is available, we found it.
1205 newPropertyIndex = currentPropertyIndex;
1208 else
1211 * We exhausted the property list, we will create more space below
1213 newPropertyIndex = currentPropertyIndex;
1215 currentPropertyIndex++;
1217 } while (newPropertyIndex == PROPERTY_NULL);
1220 * grow the property chain
1222 if (! readSuccessful)
1224 StgProperty emptyProperty;
1225 ULARGE_INTEGER newSize;
1226 ULONG propertyIndex;
1227 ULONG lastProperty = 0;
1228 ULONG blockCount = 0;
1231 * obtain the new count of property blocks
1233 blockCount = BlockChainStream_GetCount(
1234 storage->ancestorStorage->rootBlockChain)+1;
1237 * initialize the size used by the property stream
1239 newSize.u.HighPart = 0;
1240 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1243 * add a property block to the property chain
1245 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1248 * memset the empty property in order to initialize the unused newly
1249 * created property
1251 memset(&emptyProperty, 0, sizeof(StgProperty));
1254 * initialize them
1256 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1258 for(
1259 propertyIndex = newPropertyIndex;
1260 propertyIndex < lastProperty;
1261 propertyIndex++)
1263 StorageImpl_WriteProperty(
1264 storage->ancestorStorage,
1265 propertyIndex,
1266 &emptyProperty);
1270 return newPropertyIndex;
1273 /****************************************************************************
1275 * Internal Method
1277 * Case insensitive comparaison of StgProperty.name by first considering
1278 * their size.
1280 * Returns <0 when newPrpoerty < currentProperty
1281 * >0 when newPrpoerty > currentProperty
1282 * 0 when newPrpoerty == currentProperty
1284 static LONG propertyNameCmp(
1285 OLECHAR *newProperty,
1286 OLECHAR *currentProperty)
1288 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1290 if (diff == 0)
1293 * We compare the string themselves only when they are of the same length
1295 diff = lstrcmpiW( newProperty, currentProperty);
1298 return diff;
1301 /****************************************************************************
1303 * Internal Method
1305 * Properly link this new element in the property chain.
1307 static void updatePropertyChain(
1308 StorageImpl *storage,
1309 ULONG newPropertyIndex,
1310 StgProperty newProperty)
1312 StgProperty currentProperty;
1315 * Read the root property
1317 StorageImpl_ReadProperty(storage->ancestorStorage,
1318 storage->rootPropertySetIndex,
1319 &currentProperty);
1321 if (currentProperty.dirProperty != PROPERTY_NULL)
1324 * The root storage contains some element, therefore, start the research
1325 * for the appropriate location.
1327 BOOL found = 0;
1328 ULONG current, next, previous, currentPropertyId;
1331 * Keep the StgProperty sequence number of the storage first property
1333 currentPropertyId = currentProperty.dirProperty;
1336 * Read
1338 StorageImpl_ReadProperty(storage->ancestorStorage,
1339 currentProperty.dirProperty,
1340 &currentProperty);
1342 previous = currentProperty.previousProperty;
1343 next = currentProperty.nextProperty;
1344 current = currentPropertyId;
1346 while (found == 0)
1348 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1350 if (diff < 0)
1352 if (previous != PROPERTY_NULL)
1354 StorageImpl_ReadProperty(storage->ancestorStorage,
1355 previous,
1356 &currentProperty);
1357 current = previous;
1359 else
1361 currentProperty.previousProperty = newPropertyIndex;
1362 StorageImpl_WriteProperty(storage->ancestorStorage,
1363 current,
1364 &currentProperty);
1365 found = 1;
1368 else if (diff > 0)
1370 if (next != PROPERTY_NULL)
1372 StorageImpl_ReadProperty(storage->ancestorStorage,
1373 next,
1374 &currentProperty);
1375 current = next;
1377 else
1379 currentProperty.nextProperty = newPropertyIndex;
1380 StorageImpl_WriteProperty(storage->ancestorStorage,
1381 current,
1382 &currentProperty);
1383 found = 1;
1386 else
1389 * Trying to insert an item with the same name in the
1390 * subtree structure.
1392 assert(FALSE);
1395 previous = currentProperty.previousProperty;
1396 next = currentProperty.nextProperty;
1399 else
1402 * The root storage is empty, link the new property to it's dir property
1404 currentProperty.dirProperty = newPropertyIndex;
1405 StorageImpl_WriteProperty(storage->ancestorStorage,
1406 storage->rootPropertySetIndex,
1407 &currentProperty);
1412 /*************************************************************************
1413 * CopyTo (IStorage)
1415 HRESULT WINAPI StorageImpl_CopyTo(
1416 IStorage* iface,
1417 DWORD ciidExclude, /* [in] */
1418 const IID* rgiidExclude, /* [size_is][unique][in] */
1419 SNB snbExclude, /* [unique][in] */
1420 IStorage* pstgDest) /* [unique][in] */
1422 IEnumSTATSTG *elements = 0;
1423 STATSTG curElement, strStat;
1424 HRESULT hr;
1425 IStorage *pstgTmp, *pstgChild;
1426 IStream *pstrTmp, *pstrChild;
1428 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1429 FIXME("Exclude option not implemented\n");
1431 TRACE("(%p, %ld, %p, %p, %p)\n",
1432 iface, ciidExclude, rgiidExclude,
1433 snbExclude, pstgDest);
1436 * Perform a sanity check
1438 if ( pstgDest == 0 )
1439 return STG_E_INVALIDPOINTER;
1442 * Enumerate the elements
1444 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1446 if ( hr != S_OK )
1447 return hr;
1450 * set the class ID
1452 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1453 IStorage_SetClass( pstgDest, &curElement.clsid );
1458 * Obtain the next element
1460 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1462 if ( hr == S_FALSE )
1464 hr = S_OK; /* done, every element has been copied */
1465 break;
1468 if (curElement.type == STGTY_STORAGE)
1471 * open child source storage
1473 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1474 STGM_READ|STGM_SHARE_EXCLUSIVE,
1475 NULL, 0, &pstgChild );
1477 if (hr != S_OK)
1478 break;
1481 * Check if destination storage is not a child of the source
1482 * storage, which will cause an infinite loop
1484 if (pstgChild == pstgDest)
1486 IEnumSTATSTG_Release(elements);
1488 return STG_E_ACCESSDENIED;
1492 * create a new storage in destination storage
1494 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1495 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1496 0, 0,
1497 &pstgTmp );
1499 * if it already exist, don't create a new one use this one
1501 if (hr == STG_E_FILEALREADYEXISTS)
1503 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1504 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1505 NULL, 0, &pstgTmp );
1508 if (hr != S_OK)
1509 break;
1513 * do the copy recursively
1515 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1516 snbExclude, pstgTmp );
1518 IStorage_Release( pstgTmp );
1519 IStorage_Release( pstgChild );
1521 else if (curElement.type == STGTY_STREAM)
1524 * create a new stream in destination storage. If the stream already
1525 * exist, it will be deleted and a new one will be created.
1527 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1528 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1529 0, 0, &pstrTmp );
1531 if (hr != S_OK)
1532 break;
1535 * open child stream storage
1537 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1538 STGM_READ|STGM_SHARE_EXCLUSIVE,
1539 0, &pstrChild );
1541 if (hr != S_OK)
1542 break;
1545 * Get the size of the source stream
1547 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1550 * Set the size of the destination stream.
1552 IStream_SetSize(pstrTmp, strStat.cbSize);
1555 * do the copy
1557 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1558 NULL, NULL );
1560 IStream_Release( pstrTmp );
1561 IStream_Release( pstrChild );
1563 else
1565 WARN("unknown element type: %ld\n", curElement.type);
1568 } while (hr == S_OK);
1571 * Clean-up
1573 IEnumSTATSTG_Release(elements);
1575 return hr;
1578 /*************************************************************************
1579 * MoveElementTo (IStorage)
1581 HRESULT WINAPI StorageImpl_MoveElementTo(
1582 IStorage* iface,
1583 const OLECHAR *pwcsName, /* [string][in] */
1584 IStorage *pstgDest, /* [unique][in] */
1585 const OLECHAR *pwcsNewName,/* [string][in] */
1586 DWORD grfFlags) /* [in] */
1588 FIXME("not implemented!\n");
1589 return E_NOTIMPL;
1592 /*************************************************************************
1593 * Commit (IStorage)
1595 HRESULT WINAPI StorageImpl_Commit(
1596 IStorage* iface,
1597 DWORD grfCommitFlags)/* [in] */
1599 FIXME("(%ld): stub!\n", grfCommitFlags);
1600 return S_OK;
1603 /*************************************************************************
1604 * Revert (IStorage)
1606 HRESULT WINAPI StorageImpl_Revert(
1607 IStorage* iface)
1609 FIXME("not implemented!\n");
1610 return E_NOTIMPL;
1613 /*************************************************************************
1614 * DestroyElement (IStorage)
1616 * Stategy: This implementation is build this way for simplicity not for speed.
1617 * I always delete the top most element of the enumeration and adjust
1618 * the deleted element pointer all the time. This takes longer to
1619 * do but allow to reinvoke DestroyElement whenever we encounter a
1620 * storage object. The optimisation reside in the usage of another
1621 * enumeration stategy that would give all the leaves of a storage
1622 * first. (postfix order)
1624 HRESULT WINAPI StorageImpl_DestroyElement(
1625 IStorage* iface,
1626 const OLECHAR *pwcsName)/* [string][in] */
1628 StorageImpl* const This=(StorageImpl*)iface;
1630 IEnumSTATSTGImpl* propertyEnumeration;
1631 HRESULT hr = S_OK;
1632 BOOL res;
1633 StgProperty propertyToDelete;
1634 StgProperty parentProperty;
1635 ULONG foundPropertyIndexToDelete;
1636 ULONG typeOfRelation;
1637 ULONG parentPropertyId;
1639 TRACE("(%p, %s)\n",
1640 iface, debugstr_w(pwcsName));
1643 * Perform a sanity check on the parameters.
1645 if (pwcsName==NULL)
1646 return STG_E_INVALIDPOINTER;
1649 * Create a property enumeration to search the property with the given name
1651 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1652 This->ancestorStorage,
1653 This->rootPropertySetIndex);
1655 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1656 propertyEnumeration,
1657 pwcsName,
1658 &propertyToDelete);
1660 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1662 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1664 return STG_E_FILENOTFOUND;
1668 * Find the parent property of the property to delete (the one that
1669 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1670 * the parent is This. Otherwise, the parent is one of it's sibling...
1674 * First, read This's StgProperty..
1676 res = StorageImpl_ReadProperty(
1677 This->ancestorStorage,
1678 This->rootPropertySetIndex,
1679 &parentProperty);
1681 assert(res==TRUE);
1684 * Second, check to see if by any chance the actual storage (This) is not
1685 * the parent of the property to delete... We never know...
1687 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1690 * Set data as it would have been done in the else part...
1692 typeOfRelation = PROPERTY_RELATION_DIR;
1693 parentPropertyId = This->rootPropertySetIndex;
1695 else
1698 * Create a property enumeration to search the parent properties, and
1699 * delete it once done.
1701 IEnumSTATSTGImpl* propertyEnumeration2;
1703 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1704 This->ancestorStorage,
1705 This->rootPropertySetIndex);
1707 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1708 propertyEnumeration2,
1709 foundPropertyIndexToDelete,
1710 &parentProperty,
1711 &parentPropertyId);
1713 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1716 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1718 hr = deleteStorageProperty(
1719 This,
1720 foundPropertyIndexToDelete,
1721 propertyToDelete);
1723 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1725 hr = deleteStreamProperty(
1726 This,
1727 foundPropertyIndexToDelete,
1728 propertyToDelete);
1731 if (hr!=S_OK)
1732 return hr;
1735 * Adjust the property chain
1737 hr = adjustPropertyChain(
1738 This,
1739 propertyToDelete,
1740 parentProperty,
1741 parentPropertyId,
1742 typeOfRelation);
1744 return hr;
1748 /************************************************************************
1749 * StorageImpl_Stat (IStorage)
1751 * This method will retrieve information about this storage object.
1753 * See Windows documentation for more details on IStorage methods.
1755 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1756 STATSTG* pstatstg, /* [out] */
1757 DWORD grfStatFlag) /* [in] */
1759 StorageImpl* const This = (StorageImpl*)iface;
1760 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1762 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1764 CoTaskMemFree(pstatstg->pwcsName);
1765 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1766 strcpyW(pstatstg->pwcsName, This->pwcsName);
1769 return result;
1774 /*********************************************************************
1776 * Internal Method
1778 * Perform the deletion of a complete storage node
1781 static HRESULT deleteStorageProperty(
1782 StorageImpl *parentStorage,
1783 ULONG indexOfPropertyToDelete,
1784 StgProperty propertyToDelete)
1786 IEnumSTATSTG *elements = 0;
1787 IStorage *childStorage = 0;
1788 STATSTG currentElement;
1789 HRESULT hr;
1790 HRESULT destroyHr = S_OK;
1793 * Open the storage and enumerate it
1795 hr = StorageBaseImpl_OpenStorage(
1796 (IStorage*)parentStorage,
1797 propertyToDelete.name,
1799 STGM_SHARE_EXCLUSIVE,
1802 &childStorage);
1804 if (hr != S_OK)
1806 return hr;
1810 * Enumerate the elements
1812 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1817 * Obtain the next element
1819 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1820 if (hr==S_OK)
1822 destroyHr = StorageImpl_DestroyElement(
1823 (IStorage*)childStorage,
1824 (OLECHAR*)currentElement.pwcsName);
1826 CoTaskMemFree(currentElement.pwcsName);
1830 * We need to Reset the enumeration every time because we delete elements
1831 * and the enumeration could be invalid
1833 IEnumSTATSTG_Reset(elements);
1835 } while ((hr == S_OK) && (destroyHr == S_OK));
1838 * Invalidate the property by zeroing it's name member.
1840 propertyToDelete.sizeOfNameString = 0;
1842 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1843 indexOfPropertyToDelete,
1844 &propertyToDelete);
1846 IStorage_Release(childStorage);
1847 IEnumSTATSTG_Release(elements);
1849 return destroyHr;
1852 /*********************************************************************
1854 * Internal Method
1856 * Perform the deletion of a stream node
1859 static HRESULT deleteStreamProperty(
1860 StorageImpl *parentStorage,
1861 ULONG indexOfPropertyToDelete,
1862 StgProperty propertyToDelete)
1864 IStream *pis;
1865 HRESULT hr;
1866 ULARGE_INTEGER size;
1868 size.u.HighPart = 0;
1869 size.u.LowPart = 0;
1871 hr = StorageBaseImpl_OpenStream(
1872 (IStorage*)parentStorage,
1873 (OLECHAR*)propertyToDelete.name,
1874 NULL,
1875 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1877 &pis);
1879 if (hr!=S_OK)
1881 return(hr);
1885 * Zap the stream
1887 hr = IStream_SetSize(pis, size);
1889 if(hr != S_OK)
1891 return hr;
1895 * Release the stream object.
1897 IStream_Release(pis);
1900 * Invalidate the property by zeroing it's name member.
1902 propertyToDelete.sizeOfNameString = 0;
1905 * Here we should re-read the property so we get the updated pointer
1906 * but since we are here to zap it, I don't do it...
1908 StorageImpl_WriteProperty(
1909 parentStorage->ancestorStorage,
1910 indexOfPropertyToDelete,
1911 &propertyToDelete);
1913 return S_OK;
1916 /*********************************************************************
1918 * Internal Method
1920 * Finds a placeholder for the StgProperty within the Storage
1923 static HRESULT findPlaceholder(
1924 StorageImpl *storage,
1925 ULONG propertyIndexToStore,
1926 ULONG storePropertyIndex,
1927 INT typeOfRelation)
1929 StgProperty storeProperty;
1930 HRESULT hr = S_OK;
1931 BOOL res = TRUE;
1934 * Read the storage property
1936 res = StorageImpl_ReadProperty(
1937 storage->ancestorStorage,
1938 storePropertyIndex,
1939 &storeProperty);
1941 if(! res)
1943 return E_FAIL;
1946 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1948 if (storeProperty.previousProperty != PROPERTY_NULL)
1950 return findPlaceholder(
1951 storage,
1952 propertyIndexToStore,
1953 storeProperty.previousProperty,
1954 typeOfRelation);
1956 else
1958 storeProperty.previousProperty = propertyIndexToStore;
1961 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1963 if (storeProperty.nextProperty != PROPERTY_NULL)
1965 return findPlaceholder(
1966 storage,
1967 propertyIndexToStore,
1968 storeProperty.nextProperty,
1969 typeOfRelation);
1971 else
1973 storeProperty.nextProperty = propertyIndexToStore;
1976 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1978 if (storeProperty.dirProperty != PROPERTY_NULL)
1980 return findPlaceholder(
1981 storage,
1982 propertyIndexToStore,
1983 storeProperty.dirProperty,
1984 typeOfRelation);
1986 else
1988 storeProperty.dirProperty = propertyIndexToStore;
1992 hr = StorageImpl_WriteProperty(
1993 storage->ancestorStorage,
1994 storePropertyIndex,
1995 &storeProperty);
1997 if(! hr)
1999 return E_FAIL;
2002 return S_OK;
2005 /*************************************************************************
2007 * Internal Method
2009 * This method takes the previous and the next property link of a property
2010 * to be deleted and find them a place in the Storage.
2012 static HRESULT adjustPropertyChain(
2013 StorageImpl *This,
2014 StgProperty propertyToDelete,
2015 StgProperty parentProperty,
2016 ULONG parentPropertyId,
2017 INT typeOfRelation)
2019 ULONG newLinkProperty = PROPERTY_NULL;
2020 BOOL needToFindAPlaceholder = FALSE;
2021 ULONG storeNode = PROPERTY_NULL;
2022 ULONG toStoreNode = PROPERTY_NULL;
2023 INT relationType = 0;
2024 HRESULT hr = S_OK;
2025 BOOL res = TRUE;
2027 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2029 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2032 * Set the parent previous to the property to delete previous
2034 newLinkProperty = propertyToDelete.previousProperty;
2036 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2039 * We also need to find a storage for the other link, setup variables
2040 * to do this at the end...
2042 needToFindAPlaceholder = TRUE;
2043 storeNode = propertyToDelete.previousProperty;
2044 toStoreNode = propertyToDelete.nextProperty;
2045 relationType = PROPERTY_RELATION_NEXT;
2048 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2051 * Set the parent previous to the property to delete next
2053 newLinkProperty = propertyToDelete.nextProperty;
2057 * Link it for real...
2059 parentProperty.previousProperty = newLinkProperty;
2062 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2064 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2067 * Set the parent next to the property to delete next previous
2069 newLinkProperty = propertyToDelete.previousProperty;
2071 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2074 * We also need to find a storage for the other link, setup variables
2075 * to do this at the end...
2077 needToFindAPlaceholder = TRUE;
2078 storeNode = propertyToDelete.previousProperty;
2079 toStoreNode = propertyToDelete.nextProperty;
2080 relationType = PROPERTY_RELATION_NEXT;
2083 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2086 * Set the parent next to the property to delete next
2088 newLinkProperty = propertyToDelete.nextProperty;
2092 * Link it for real...
2094 parentProperty.nextProperty = newLinkProperty;
2096 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2098 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2101 * Set the parent dir to the property to delete previous
2103 newLinkProperty = propertyToDelete.previousProperty;
2105 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2108 * We also need to find a storage for the other link, setup variables
2109 * to do this at the end...
2111 needToFindAPlaceholder = TRUE;
2112 storeNode = propertyToDelete.previousProperty;
2113 toStoreNode = propertyToDelete.nextProperty;
2114 relationType = PROPERTY_RELATION_NEXT;
2117 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2120 * Set the parent dir to the property to delete next
2122 newLinkProperty = propertyToDelete.nextProperty;
2126 * Link it for real...
2128 parentProperty.dirProperty = newLinkProperty;
2132 * Write back the parent property
2134 res = StorageImpl_WriteProperty(
2135 This->ancestorStorage,
2136 parentPropertyId,
2137 &parentProperty);
2138 if(! res)
2140 return E_FAIL;
2144 * If a placeholder is required for the other link, then, find one and
2145 * get out of here...
2147 if (needToFindAPlaceholder)
2149 hr = findPlaceholder(
2150 This,
2151 toStoreNode,
2152 storeNode,
2153 relationType);
2156 return hr;
2160 /******************************************************************************
2161 * SetElementTimes (IStorage)
2163 HRESULT WINAPI StorageImpl_SetElementTimes(
2164 IStorage* iface,
2165 const OLECHAR *pwcsName,/* [string][in] */
2166 const FILETIME *pctime, /* [in] */
2167 const FILETIME *patime, /* [in] */
2168 const FILETIME *pmtime) /* [in] */
2170 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2171 return S_OK;
2174 /******************************************************************************
2175 * SetStateBits (IStorage)
2177 HRESULT WINAPI StorageImpl_SetStateBits(
2178 IStorage* iface,
2179 DWORD grfStateBits,/* [in] */
2180 DWORD grfMask) /* [in] */
2182 FIXME("not implemented!\n");
2183 return E_NOTIMPL;
2186 HRESULT StorageImpl_Construct(
2187 StorageImpl* This,
2188 HANDLE hFile,
2189 LPCOLESTR pwcsName,
2190 ILockBytes* pLkbyt,
2191 DWORD openFlags,
2192 BOOL fileBased,
2193 BOOL fileCreate)
2195 HRESULT hr = S_OK;
2196 StgProperty currentProperty;
2197 BOOL readSuccessful;
2198 ULONG currentPropertyIndex;
2200 if ( FAILED( validateSTGM(openFlags) ))
2201 return STG_E_INVALIDFLAG;
2203 memset(This, 0, sizeof(StorageImpl));
2206 * Initialize the virtual function table.
2208 This->lpVtbl = &Storage32Impl_Vtbl;
2209 This->v_destructor = &StorageImpl_Destroy;
2212 * This is the top-level storage so initialize the ancestor pointer
2213 * to this.
2215 This->ancestorStorage = This;
2218 * Initialize the physical support of the storage.
2220 This->hFile = hFile;
2223 * Store copy of file path.
2225 if(pwcsName) {
2226 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2227 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2228 if (!This->pwcsName)
2229 return STG_E_INSUFFICIENTMEMORY;
2230 strcpyW(This->pwcsName, pwcsName);
2234 * Initialize the big block cache.
2236 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2237 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2238 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2239 pLkbyt,
2240 openFlags,
2241 This->bigBlockSize,
2242 fileBased);
2244 if (This->bigBlockFile == 0)
2245 return E_FAIL;
2247 if (fileCreate)
2249 ULARGE_INTEGER size;
2250 BYTE* bigBlockBuffer;
2253 * Initialize all header variables:
2254 * - The big block depot consists of one block and it is at block 0
2255 * - The properties start at block 1
2256 * - There is no small block depot
2258 memset( This->bigBlockDepotStart,
2259 BLOCK_UNUSED,
2260 sizeof(This->bigBlockDepotStart));
2262 This->bigBlockDepotCount = 1;
2263 This->bigBlockDepotStart[0] = 0;
2264 This->rootStartBlock = 1;
2265 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2266 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2267 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2268 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2269 This->extBigBlockDepotCount = 0;
2271 StorageImpl_SaveFileHeader(This);
2274 * Add one block for the big block depot and one block for the properties
2276 size.u.HighPart = 0;
2277 size.u.LowPart = This->bigBlockSize * 3;
2278 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2281 * Initialize the big block depot
2283 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2284 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2285 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2286 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2287 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2289 else
2292 * Load the header for the file.
2294 hr = StorageImpl_LoadFileHeader(This);
2296 if (FAILED(hr))
2298 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2300 return hr;
2305 * There is no block depot cached yet.
2307 This->indexBlockDepotCached = 0xFFFFFFFF;
2310 * Start searching for free blocks with block 0.
2312 This->prevFreeBlock = 0;
2315 * Create the block chain abstractions.
2317 if(!(This->rootBlockChain =
2318 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2319 return STG_E_READFAULT;
2321 if(!(This->smallBlockDepotChain =
2322 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2323 PROPERTY_NULL)))
2324 return STG_E_READFAULT;
2327 * Write the root property
2329 if (fileCreate)
2331 StgProperty rootProp;
2333 * Initialize the property chain
2335 memset(&rootProp, 0, sizeof(rootProp));
2336 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2337 sizeof(rootProp.name)/sizeof(WCHAR) );
2338 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2339 rootProp.propertyType = PROPTYPE_ROOT;
2340 rootProp.previousProperty = PROPERTY_NULL;
2341 rootProp.nextProperty = PROPERTY_NULL;
2342 rootProp.dirProperty = PROPERTY_NULL;
2343 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2344 rootProp.size.u.HighPart = 0;
2345 rootProp.size.u.LowPart = 0;
2347 StorageImpl_WriteProperty(This, 0, &rootProp);
2351 * Find the ID of the root in the property sets.
2353 currentPropertyIndex = 0;
2357 readSuccessful = StorageImpl_ReadProperty(
2358 This,
2359 currentPropertyIndex,
2360 &currentProperty);
2362 if (readSuccessful)
2364 if ( (currentProperty.sizeOfNameString != 0 ) &&
2365 (currentProperty.propertyType == PROPTYPE_ROOT) )
2367 This->rootPropertySetIndex = currentPropertyIndex;
2371 currentPropertyIndex++;
2373 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2375 if (!readSuccessful)
2377 /* TODO CLEANUP */
2378 return STG_E_READFAULT;
2382 * Create the block chain abstraction for the small block root chain.
2384 if(!(This->smallBlockRootChain =
2385 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2386 return STG_E_READFAULT;
2388 return hr;
2391 void StorageImpl_Destroy(
2392 StorageImpl* This)
2394 TRACE("(%p)\n", This);
2396 if(This->pwcsName)
2397 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2399 BlockChainStream_Destroy(This->smallBlockRootChain);
2400 BlockChainStream_Destroy(This->rootBlockChain);
2401 BlockChainStream_Destroy(This->smallBlockDepotChain);
2403 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2404 return;
2407 /******************************************************************************
2408 * Storage32Impl_GetNextFreeBigBlock
2410 * Returns the index of the next free big block.
2411 * If the big block depot is filled, this method will enlarge it.
2414 ULONG StorageImpl_GetNextFreeBigBlock(
2415 StorageImpl* This)
2417 ULONG depotBlockIndexPos;
2418 void *depotBuffer;
2419 ULONG depotBlockOffset;
2420 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2421 ULONG nextBlockIndex = BLOCK_SPECIAL;
2422 int depotIndex = 0;
2423 ULONG freeBlock = BLOCK_UNUSED;
2425 depotIndex = This->prevFreeBlock / blocksPerDepot;
2426 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2429 * Scan the entire big block depot until we find a block marked free
2431 while (nextBlockIndex != BLOCK_UNUSED)
2433 if (depotIndex < COUNT_BBDEPOTINHEADER)
2435 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2438 * Grow the primary depot.
2440 if (depotBlockIndexPos == BLOCK_UNUSED)
2442 depotBlockIndexPos = depotIndex*blocksPerDepot;
2445 * Add a block depot.
2447 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2448 This->bigBlockDepotCount++;
2449 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2452 * Flag it as a block depot.
2454 StorageImpl_SetNextBlockInChain(This,
2455 depotBlockIndexPos,
2456 BLOCK_SPECIAL);
2458 /* Save new header information.
2460 StorageImpl_SaveFileHeader(This);
2463 else
2465 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2467 if (depotBlockIndexPos == BLOCK_UNUSED)
2470 * Grow the extended depot.
2472 ULONG extIndex = BLOCK_UNUSED;
2473 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2474 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2476 if (extBlockOffset == 0)
2478 /* We need an extended block.
2480 extIndex = Storage32Impl_AddExtBlockDepot(This);
2481 This->extBigBlockDepotCount++;
2482 depotBlockIndexPos = extIndex + 1;
2484 else
2485 depotBlockIndexPos = depotIndex * blocksPerDepot;
2488 * Add a block depot and mark it in the extended block.
2490 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2491 This->bigBlockDepotCount++;
2492 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2494 /* Flag the block depot.
2496 StorageImpl_SetNextBlockInChain(This,
2497 depotBlockIndexPos,
2498 BLOCK_SPECIAL);
2500 /* If necessary, flag the extended depot block.
2502 if (extIndex != BLOCK_UNUSED)
2503 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2505 /* Save header information.
2507 StorageImpl_SaveFileHeader(This);
2511 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2513 if (depotBuffer != 0)
2515 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2516 ( nextBlockIndex != BLOCK_UNUSED))
2518 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2520 if (nextBlockIndex == BLOCK_UNUSED)
2522 freeBlock = (depotIndex * blocksPerDepot) +
2523 (depotBlockOffset/sizeof(ULONG));
2526 depotBlockOffset += sizeof(ULONG);
2529 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2532 depotIndex++;
2533 depotBlockOffset = 0;
2536 This->prevFreeBlock = freeBlock;
2538 return freeBlock;
2541 /******************************************************************************
2542 * Storage32Impl_AddBlockDepot
2544 * This will create a depot block, essentially it is a block initialized
2545 * to BLOCK_UNUSEDs.
2547 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2549 BYTE* blockBuffer;
2551 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2554 * Initialize blocks as free
2556 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2558 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2561 /******************************************************************************
2562 * Storage32Impl_GetExtDepotBlock
2564 * Returns the index of the block that corresponds to the specified depot
2565 * index. This method is only for depot indexes equal or greater than
2566 * COUNT_BBDEPOTINHEADER.
2568 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2570 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2571 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2572 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2573 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2574 ULONG blockIndex = BLOCK_UNUSED;
2575 ULONG extBlockIndex = This->extBigBlockDepotStart;
2577 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2579 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2580 return BLOCK_UNUSED;
2582 while (extBlockCount > 0)
2584 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2585 extBlockCount--;
2588 if (extBlockIndex != BLOCK_UNUSED)
2590 BYTE* depotBuffer;
2592 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2594 if (depotBuffer != 0)
2596 StorageUtl_ReadDWord(depotBuffer,
2597 extBlockOffset * sizeof(ULONG),
2598 &blockIndex);
2600 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2604 return blockIndex;
2607 /******************************************************************************
2608 * Storage32Impl_SetExtDepotBlock
2610 * Associates the specified block index to the specified depot index.
2611 * This method is only for depot indexes equal or greater than
2612 * COUNT_BBDEPOTINHEADER.
2614 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2615 ULONG depotIndex,
2616 ULONG blockIndex)
2618 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2619 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2620 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2621 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2622 ULONG extBlockIndex = This->extBigBlockDepotStart;
2624 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2626 while (extBlockCount > 0)
2628 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2629 extBlockCount--;
2632 if (extBlockIndex != BLOCK_UNUSED)
2634 BYTE* depotBuffer;
2636 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2638 if (depotBuffer != 0)
2640 StorageUtl_WriteDWord(depotBuffer,
2641 extBlockOffset * sizeof(ULONG),
2642 blockIndex);
2644 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2649 /******************************************************************************
2650 * Storage32Impl_AddExtBlockDepot
2652 * Creates an extended depot block.
2654 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2656 ULONG numExtBlocks = This->extBigBlockDepotCount;
2657 ULONG nextExtBlock = This->extBigBlockDepotStart;
2658 BYTE* depotBuffer = NULL;
2659 ULONG index = BLOCK_UNUSED;
2660 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2661 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2662 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2664 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2665 blocksPerDepotBlock;
2667 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2670 * The first extended block.
2672 This->extBigBlockDepotStart = index;
2674 else
2676 unsigned int i;
2678 * Follow the chain to the last one.
2680 for (i = 0; i < (numExtBlocks - 1); i++)
2682 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2686 * Add the new extended block to the chain.
2688 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2689 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2690 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2694 * Initialize this block.
2696 depotBuffer = StorageImpl_GetBigBlock(This, index);
2697 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2698 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2700 return index;
2703 /******************************************************************************
2704 * Storage32Impl_FreeBigBlock
2706 * This method will flag the specified block as free in the big block depot.
2708 void StorageImpl_FreeBigBlock(
2709 StorageImpl* This,
2710 ULONG blockIndex)
2712 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2714 if (blockIndex < This->prevFreeBlock)
2715 This->prevFreeBlock = blockIndex;
2718 /************************************************************************
2719 * Storage32Impl_GetNextBlockInChain
2721 * This method will retrieve the block index of the next big block in
2722 * in the chain.
2724 * Params: This - Pointer to the Storage object.
2725 * blockIndex - Index of the block to retrieve the chain
2726 * for.
2727 * nextBlockIndex - receives the return value.
2729 * Returns: This method returns the index of the next block in the chain.
2730 * It will return the constants:
2731 * BLOCK_SPECIAL - If the block given was not part of a
2732 * chain.
2733 * BLOCK_END_OF_CHAIN - If the block given was the last in
2734 * a chain.
2735 * BLOCK_UNUSED - If the block given was not past of a chain
2736 * and is available.
2737 * BLOCK_EXTBBDEPOT - This block is part of the extended
2738 * big block depot.
2740 * See Windows documentation for more details on IStorage methods.
2742 HRESULT StorageImpl_GetNextBlockInChain(
2743 StorageImpl* This,
2744 ULONG blockIndex,
2745 ULONG* nextBlockIndex)
2747 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2748 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2749 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2750 void* depotBuffer;
2751 ULONG depotBlockIndexPos;
2752 int index;
2754 *nextBlockIndex = BLOCK_SPECIAL;
2756 if(depotBlockCount >= This->bigBlockDepotCount)
2758 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2759 This->bigBlockDepotCount);
2760 return STG_E_READFAULT;
2764 * Cache the currently accessed depot block.
2766 if (depotBlockCount != This->indexBlockDepotCached)
2768 This->indexBlockDepotCached = depotBlockCount;
2770 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2772 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2774 else
2777 * We have to look in the extended depot.
2779 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2782 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2784 if (!depotBuffer)
2785 return STG_E_READFAULT;
2787 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2789 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2790 This->blockDepotCached[index] = *nextBlockIndex;
2792 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2795 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2797 return S_OK;
2800 /******************************************************************************
2801 * Storage32Impl_GetNextExtendedBlock
2803 * Given an extended block this method will return the next extended block.
2805 * NOTES:
2806 * The last ULONG of an extended block is the block index of the next
2807 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2808 * depot.
2810 * Return values:
2811 * - The index of the next extended block
2812 * - BLOCK_UNUSED: there is no next extended block.
2813 * - Any other return values denotes failure.
2815 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2817 ULONG nextBlockIndex = BLOCK_SPECIAL;
2818 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2819 void* depotBuffer;
2821 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2823 if (depotBuffer!=0)
2825 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2827 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2830 return nextBlockIndex;
2833 /******************************************************************************
2834 * Storage32Impl_SetNextBlockInChain
2836 * This method will write the index of the specified block's next block
2837 * in the big block depot.
2839 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2840 * do the following
2842 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2843 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2844 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2847 void StorageImpl_SetNextBlockInChain(
2848 StorageImpl* This,
2849 ULONG blockIndex,
2850 ULONG nextBlock)
2852 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2853 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2854 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2855 ULONG depotBlockIndexPos;
2856 void* depotBuffer;
2858 assert(depotBlockCount < This->bigBlockDepotCount);
2859 assert(blockIndex != nextBlock);
2861 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2863 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2865 else
2868 * We have to look in the extended depot.
2870 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2873 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2875 if (depotBuffer!=0)
2877 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2878 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2882 * Update the cached block depot, if necessary.
2884 if (depotBlockCount == This->indexBlockDepotCached)
2886 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2890 /******************************************************************************
2891 * Storage32Impl_LoadFileHeader
2893 * This method will read in the file header, i.e. big block index -1.
2895 HRESULT StorageImpl_LoadFileHeader(
2896 StorageImpl* This)
2898 HRESULT hr = STG_E_FILENOTFOUND;
2899 void* headerBigBlock = NULL;
2900 int index;
2903 * Get a pointer to the big block of data containing the header.
2905 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2908 * Extract the information from the header.
2910 if (headerBigBlock!=0)
2913 * Check for the "magic number" signature and return an error if it is not
2914 * found.
2916 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2918 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2919 return STG_E_OLDFORMAT;
2922 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2924 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2925 return STG_E_INVALIDHEADER;
2928 StorageUtl_ReadWord(
2929 headerBigBlock,
2930 OFFSET_BIGBLOCKSIZEBITS,
2931 &This->bigBlockSizeBits);
2933 StorageUtl_ReadWord(
2934 headerBigBlock,
2935 OFFSET_SMALLBLOCKSIZEBITS,
2936 &This->smallBlockSizeBits);
2938 StorageUtl_ReadDWord(
2939 headerBigBlock,
2940 OFFSET_BBDEPOTCOUNT,
2941 &This->bigBlockDepotCount);
2943 StorageUtl_ReadDWord(
2944 headerBigBlock,
2945 OFFSET_ROOTSTARTBLOCK,
2946 &This->rootStartBlock);
2948 StorageUtl_ReadDWord(
2949 headerBigBlock,
2950 OFFSET_SBDEPOTSTART,
2951 &This->smallBlockDepotStart);
2953 StorageUtl_ReadDWord(
2954 headerBigBlock,
2955 OFFSET_EXTBBDEPOTSTART,
2956 &This->extBigBlockDepotStart);
2958 StorageUtl_ReadDWord(
2959 headerBigBlock,
2960 OFFSET_EXTBBDEPOTCOUNT,
2961 &This->extBigBlockDepotCount);
2963 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2965 StorageUtl_ReadDWord(
2966 headerBigBlock,
2967 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2968 &(This->bigBlockDepotStart[index]));
2972 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2974 if ((1 << 2) == 4)
2976 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2977 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2979 else
2981 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2982 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2986 * Right now, the code is making some assumptions about the size of the
2987 * blocks, just make sure they are what we're expecting.
2989 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2990 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2992 WARN("Broken OLE storage file\n");
2993 hr = STG_E_INVALIDHEADER;
2995 else
2996 hr = S_OK;
2999 * Release the block.
3001 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3004 return hr;
3007 /******************************************************************************
3008 * Storage32Impl_SaveFileHeader
3010 * This method will save to the file the header, i.e. big block -1.
3012 void StorageImpl_SaveFileHeader(
3013 StorageImpl* This)
3015 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3016 int index;
3017 BOOL success;
3020 * Get a pointer to the big block of data containing the header.
3022 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3025 * If the block read failed, the file is probably new.
3027 if (!success)
3030 * Initialize for all unknown fields.
3032 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3035 * Initialize the magic number.
3037 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3040 * And a bunch of things we don't know what they mean
3042 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3043 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3044 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3045 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3049 * Write the information to the header.
3051 StorageUtl_WriteWord(
3052 headerBigBlock,
3053 OFFSET_BIGBLOCKSIZEBITS,
3054 This->bigBlockSizeBits);
3056 StorageUtl_WriteWord(
3057 headerBigBlock,
3058 OFFSET_SMALLBLOCKSIZEBITS,
3059 This->smallBlockSizeBits);
3061 StorageUtl_WriteDWord(
3062 headerBigBlock,
3063 OFFSET_BBDEPOTCOUNT,
3064 This->bigBlockDepotCount);
3066 StorageUtl_WriteDWord(
3067 headerBigBlock,
3068 OFFSET_ROOTSTARTBLOCK,
3069 This->rootStartBlock);
3071 StorageUtl_WriteDWord(
3072 headerBigBlock,
3073 OFFSET_SBDEPOTSTART,
3074 This->smallBlockDepotStart);
3076 StorageUtl_WriteDWord(
3077 headerBigBlock,
3078 OFFSET_SBDEPOTCOUNT,
3079 This->smallBlockDepotChain ?
3080 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3082 StorageUtl_WriteDWord(
3083 headerBigBlock,
3084 OFFSET_EXTBBDEPOTSTART,
3085 This->extBigBlockDepotStart);
3087 StorageUtl_WriteDWord(
3088 headerBigBlock,
3089 OFFSET_EXTBBDEPOTCOUNT,
3090 This->extBigBlockDepotCount);
3092 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3094 StorageUtl_WriteDWord(
3095 headerBigBlock,
3096 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3097 (This->bigBlockDepotStart[index]));
3101 * Write the big block back to the file.
3103 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3106 /******************************************************************************
3107 * Storage32Impl_ReadProperty
3109 * This method will read the specified property from the property chain.
3111 BOOL StorageImpl_ReadProperty(
3112 StorageImpl* This,
3113 ULONG index,
3114 StgProperty* buffer)
3116 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3117 ULARGE_INTEGER offsetInPropSet;
3118 BOOL readSuccessful;
3119 ULONG bytesRead;
3121 offsetInPropSet.u.HighPart = 0;
3122 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3124 readSuccessful = BlockChainStream_ReadAt(
3125 This->rootBlockChain,
3126 offsetInPropSet,
3127 PROPSET_BLOCK_SIZE,
3128 currentProperty,
3129 &bytesRead);
3131 if (readSuccessful)
3133 /* replace the name of root entry (often "Root Entry") by the file name */
3134 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3135 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3137 memset(buffer->name, 0, sizeof(buffer->name));
3138 memcpy(
3139 buffer->name,
3140 propName,
3141 PROPERTY_NAME_BUFFER_LEN );
3142 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3144 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3146 StorageUtl_ReadWord(
3147 currentProperty,
3148 OFFSET_PS_NAMELENGTH,
3149 &buffer->sizeOfNameString);
3151 StorageUtl_ReadDWord(
3152 currentProperty,
3153 OFFSET_PS_PREVIOUSPROP,
3154 &buffer->previousProperty);
3156 StorageUtl_ReadDWord(
3157 currentProperty,
3158 OFFSET_PS_NEXTPROP,
3159 &buffer->nextProperty);
3161 StorageUtl_ReadDWord(
3162 currentProperty,
3163 OFFSET_PS_DIRPROP,
3164 &buffer->dirProperty);
3166 StorageUtl_ReadGUID(
3167 currentProperty,
3168 OFFSET_PS_GUID,
3169 &buffer->propertyUniqueID);
3171 StorageUtl_ReadDWord(
3172 currentProperty,
3173 OFFSET_PS_TSS1,
3174 &buffer->timeStampS1);
3176 StorageUtl_ReadDWord(
3177 currentProperty,
3178 OFFSET_PS_TSD1,
3179 &buffer->timeStampD1);
3181 StorageUtl_ReadDWord(
3182 currentProperty,
3183 OFFSET_PS_TSS2,
3184 &buffer->timeStampS2);
3186 StorageUtl_ReadDWord(
3187 currentProperty,
3188 OFFSET_PS_TSD2,
3189 &buffer->timeStampD2);
3191 StorageUtl_ReadDWord(
3192 currentProperty,
3193 OFFSET_PS_STARTBLOCK,
3194 &buffer->startingBlock);
3196 StorageUtl_ReadDWord(
3197 currentProperty,
3198 OFFSET_PS_SIZE,
3199 &buffer->size.u.LowPart);
3201 buffer->size.u.HighPart = 0;
3204 return readSuccessful;
3207 /*********************************************************************
3208 * Write the specified property into the property chain
3210 BOOL StorageImpl_WriteProperty(
3211 StorageImpl* This,
3212 ULONG index,
3213 StgProperty* buffer)
3215 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3216 ULARGE_INTEGER offsetInPropSet;
3217 BOOL writeSuccessful;
3218 ULONG bytesWritten;
3220 offsetInPropSet.u.HighPart = 0;
3221 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3223 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3225 memcpy(
3226 currentProperty + OFFSET_PS_NAME,
3227 buffer->name,
3228 PROPERTY_NAME_BUFFER_LEN );
3230 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3232 StorageUtl_WriteWord(
3233 currentProperty,
3234 OFFSET_PS_NAMELENGTH,
3235 buffer->sizeOfNameString);
3237 StorageUtl_WriteDWord(
3238 currentProperty,
3239 OFFSET_PS_PREVIOUSPROP,
3240 buffer->previousProperty);
3242 StorageUtl_WriteDWord(
3243 currentProperty,
3244 OFFSET_PS_NEXTPROP,
3245 buffer->nextProperty);
3247 StorageUtl_WriteDWord(
3248 currentProperty,
3249 OFFSET_PS_DIRPROP,
3250 buffer->dirProperty);
3252 StorageUtl_WriteGUID(
3253 currentProperty,
3254 OFFSET_PS_GUID,
3255 &buffer->propertyUniqueID);
3257 StorageUtl_WriteDWord(
3258 currentProperty,
3259 OFFSET_PS_TSS1,
3260 buffer->timeStampS1);
3262 StorageUtl_WriteDWord(
3263 currentProperty,
3264 OFFSET_PS_TSD1,
3265 buffer->timeStampD1);
3267 StorageUtl_WriteDWord(
3268 currentProperty,
3269 OFFSET_PS_TSS2,
3270 buffer->timeStampS2);
3272 StorageUtl_WriteDWord(
3273 currentProperty,
3274 OFFSET_PS_TSD2,
3275 buffer->timeStampD2);
3277 StorageUtl_WriteDWord(
3278 currentProperty,
3279 OFFSET_PS_STARTBLOCK,
3280 buffer->startingBlock);
3282 StorageUtl_WriteDWord(
3283 currentProperty,
3284 OFFSET_PS_SIZE,
3285 buffer->size.u.LowPart);
3287 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3288 offsetInPropSet,
3289 PROPSET_BLOCK_SIZE,
3290 currentProperty,
3291 &bytesWritten);
3292 return writeSuccessful;
3295 BOOL StorageImpl_ReadBigBlock(
3296 StorageImpl* This,
3297 ULONG blockIndex,
3298 void* buffer)
3300 void* bigBlockBuffer;
3302 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3304 if (bigBlockBuffer!=0)
3306 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3308 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3310 return TRUE;
3313 return FALSE;
3316 BOOL StorageImpl_WriteBigBlock(
3317 StorageImpl* This,
3318 ULONG blockIndex,
3319 void* buffer)
3321 void* bigBlockBuffer;
3323 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3325 if (bigBlockBuffer!=0)
3327 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3329 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3331 return TRUE;
3334 return FALSE;
3337 void* StorageImpl_GetROBigBlock(
3338 StorageImpl* This,
3339 ULONG blockIndex)
3341 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3344 void* StorageImpl_GetBigBlock(
3345 StorageImpl* This,
3346 ULONG blockIndex)
3348 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3351 void StorageImpl_ReleaseBigBlock(
3352 StorageImpl* This,
3353 void* pBigBlock)
3355 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3358 /******************************************************************************
3359 * Storage32Impl_SmallBlocksToBigBlocks
3361 * This method will convert a small block chain to a big block chain.
3362 * The small block chain will be destroyed.
3364 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3365 StorageImpl* This,
3366 SmallBlockChainStream** ppsbChain)
3368 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3369 ULARGE_INTEGER size, offset;
3370 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3371 ULONG propertyIndex;
3372 BOOL successRead, successWrite;
3373 StgProperty chainProperty;
3374 BYTE *buffer;
3375 BlockChainStream *bbTempChain = NULL;
3376 BlockChainStream *bigBlockChain = NULL;
3379 * Create a temporary big block chain that doesn't have
3380 * an associated property. This temporary chain will be
3381 * used to copy data from small blocks to big blocks.
3383 bbTempChain = BlockChainStream_Construct(This,
3384 &bbHeadOfChain,
3385 PROPERTY_NULL);
3386 if(!bbTempChain) return NULL;
3388 * Grow the big block chain.
3390 size = SmallBlockChainStream_GetSize(*ppsbChain);
3391 BlockChainStream_SetSize(bbTempChain, size);
3394 * Copy the contents of the small block chain to the big block chain
3395 * by small block size increments.
3397 offset.u.LowPart = 0;
3398 offset.u.HighPart = 0;
3399 cbTotalRead = 0;
3400 cbTotalWritten = 0;
3402 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3405 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3406 offset,
3407 DEF_SMALL_BLOCK_SIZE,
3408 buffer,
3409 &cbRead);
3410 cbTotalRead += cbRead;
3412 successWrite = BlockChainStream_WriteAt(bbTempChain,
3413 offset,
3414 cbRead,
3415 buffer,
3416 &cbWritten);
3417 cbTotalWritten += cbWritten;
3419 offset.u.LowPart += This->smallBlockSize;
3421 } while (successRead && successWrite);
3422 HeapFree(GetProcessHeap(),0,buffer);
3424 assert(cbTotalRead == cbTotalWritten);
3427 * Destroy the small block chain.
3429 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3430 size.u.HighPart = 0;
3431 size.u.LowPart = 0;
3432 SmallBlockChainStream_SetSize(*ppsbChain, size);
3433 SmallBlockChainStream_Destroy(*ppsbChain);
3434 *ppsbChain = 0;
3437 * Change the property information. This chain is now a big block chain
3438 * and it doesn't reside in the small blocks chain anymore.
3440 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3442 chainProperty.startingBlock = bbHeadOfChain;
3444 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3447 * Destroy the temporary propertyless big block chain.
3448 * Create a new big block chain associated with this property.
3450 BlockChainStream_Destroy(bbTempChain);
3451 bigBlockChain = BlockChainStream_Construct(This,
3452 NULL,
3453 propertyIndex);
3455 return bigBlockChain;
3458 /******************************************************************************
3459 ** Storage32InternalImpl implementation
3462 StorageInternalImpl* StorageInternalImpl_Construct(
3463 StorageImpl* ancestorStorage,
3464 ULONG rootPropertyIndex)
3466 StorageInternalImpl* newStorage;
3469 * Allocate space for the new storage object
3471 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3473 if (newStorage!=0)
3475 memset(newStorage, 0, sizeof(StorageInternalImpl));
3478 * Initialize the virtual function table.
3480 newStorage->lpVtbl = &Storage32InternalImpl_Vtbl;
3481 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3484 * Keep the ancestor storage pointer and nail a reference to it.
3486 newStorage->ancestorStorage = ancestorStorage;
3487 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3490 * Keep the index of the root property set for this storage,
3492 newStorage->rootPropertySetIndex = rootPropertyIndex;
3494 return newStorage;
3497 return 0;
3500 void StorageInternalImpl_Destroy(
3501 StorageInternalImpl* This)
3503 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3504 HeapFree(GetProcessHeap(), 0, This);
3507 /******************************************************************************
3509 ** Storage32InternalImpl_Commit
3511 ** The non-root storages cannot be opened in transacted mode thus this function
3512 ** does nothing.
3514 HRESULT WINAPI StorageInternalImpl_Commit(
3515 IStorage* iface,
3516 DWORD grfCommitFlags) /* [in] */
3518 return S_OK;
3521 /******************************************************************************
3523 ** Storage32InternalImpl_Revert
3525 ** The non-root storages cannot be opened in transacted mode thus this function
3526 ** does nothing.
3528 HRESULT WINAPI StorageInternalImpl_Revert(
3529 IStorage* iface)
3531 return S_OK;
3534 /******************************************************************************
3535 ** IEnumSTATSTGImpl implementation
3538 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3539 StorageImpl* parentStorage,
3540 ULONG firstPropertyNode)
3542 IEnumSTATSTGImpl* newEnumeration;
3544 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3546 if (newEnumeration!=0)
3549 * Set-up the virtual function table and reference count.
3551 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3552 newEnumeration->ref = 0;
3555 * We want to nail-down the reference to the storage in case the
3556 * enumeration out-lives the storage in the client application.
3558 newEnumeration->parentStorage = parentStorage;
3559 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3561 newEnumeration->firstPropertyNode = firstPropertyNode;
3564 * Initialize the search stack
3566 newEnumeration->stackSize = 0;
3567 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3568 newEnumeration->stackToVisit =
3569 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3572 * Make sure the current node of the iterator is the first one.
3574 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3577 return newEnumeration;
3580 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3582 IStorage_Release((IStorage*)This->parentStorage);
3583 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3584 HeapFree(GetProcessHeap(), 0, This);
3587 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3588 IEnumSTATSTG* iface,
3589 REFIID riid,
3590 void** ppvObject)
3592 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3595 * Perform a sanity check on the parameters.
3597 if (ppvObject==0)
3598 return E_INVALIDARG;
3601 * Initialize the return parameter.
3603 *ppvObject = 0;
3606 * Compare the riid with the interface IDs implemented by this object.
3608 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3610 *ppvObject = (IEnumSTATSTG*)This;
3612 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3614 *ppvObject = (IEnumSTATSTG*)This;
3618 * Check that we obtained an interface.
3620 if ((*ppvObject)==0)
3621 return E_NOINTERFACE;
3624 * Query Interface always increases the reference count by one when it is
3625 * successful
3627 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3629 return S_OK;
3632 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3633 IEnumSTATSTG* iface)
3635 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3636 return InterlockedIncrement(&This->ref);
3639 ULONG WINAPI IEnumSTATSTGImpl_Release(
3640 IEnumSTATSTG* iface)
3642 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3644 ULONG newRef;
3646 newRef = InterlockedDecrement(&This->ref);
3649 * If the reference count goes down to 0, perform suicide.
3651 if (newRef==0)
3653 IEnumSTATSTGImpl_Destroy(This);
3656 return newRef;
3659 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3660 IEnumSTATSTG* iface,
3661 ULONG celt,
3662 STATSTG* rgelt,
3663 ULONG* pceltFetched)
3665 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3667 StgProperty currentProperty;
3668 STATSTG* currentReturnStruct = rgelt;
3669 ULONG objectFetched = 0;
3670 ULONG currentSearchNode;
3673 * Perform a sanity check on the parameters.
3675 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3676 return E_INVALIDARG;
3679 * To avoid the special case, get another pointer to a ULONG value if
3680 * the caller didn't supply one.
3682 if (pceltFetched==0)
3683 pceltFetched = &objectFetched;
3686 * Start the iteration, we will iterate until we hit the end of the
3687 * linked list or until we hit the number of items to iterate through
3689 *pceltFetched = 0;
3692 * Start with the node at the top of the stack.
3694 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3696 while ( ( *pceltFetched < celt) &&
3697 ( currentSearchNode!=PROPERTY_NULL) )
3700 * Remove the top node from the stack
3702 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3705 * Read the property from the storage.
3707 StorageImpl_ReadProperty(This->parentStorage,
3708 currentSearchNode,
3709 &currentProperty);
3712 * Copy the information to the return buffer.
3714 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3715 &currentProperty,
3716 STATFLAG_DEFAULT);
3719 * Step to the next item in the iteration
3721 (*pceltFetched)++;
3722 currentReturnStruct++;
3725 * Push the next search node in the search stack.
3727 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3730 * continue the iteration.
3732 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3735 if (*pceltFetched == celt)
3736 return S_OK;
3738 return S_FALSE;
3742 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3743 IEnumSTATSTG* iface,
3744 ULONG celt)
3746 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3748 StgProperty currentProperty;
3749 ULONG objectFetched = 0;
3750 ULONG currentSearchNode;
3753 * Start with the node at the top of the stack.
3755 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3757 while ( (objectFetched < celt) &&
3758 (currentSearchNode!=PROPERTY_NULL) )
3761 * Remove the top node from the stack
3763 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3766 * Read the property from the storage.
3768 StorageImpl_ReadProperty(This->parentStorage,
3769 currentSearchNode,
3770 &currentProperty);
3773 * Step to the next item in the iteration
3775 objectFetched++;
3778 * Push the next search node in the search stack.
3780 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3783 * continue the iteration.
3785 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3788 if (objectFetched == celt)
3789 return S_OK;
3791 return S_FALSE;
3794 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3795 IEnumSTATSTG* iface)
3797 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3799 StgProperty rootProperty;
3800 BOOL readSuccessful;
3803 * Re-initialize the search stack to an empty stack
3805 This->stackSize = 0;
3808 * Read the root property from the storage.
3810 readSuccessful = StorageImpl_ReadProperty(
3811 This->parentStorage,
3812 This->firstPropertyNode,
3813 &rootProperty);
3815 if (readSuccessful)
3817 assert(rootProperty.sizeOfNameString!=0);
3820 * Push the search node in the search stack.
3822 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3825 return S_OK;
3828 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3829 IEnumSTATSTG* iface,
3830 IEnumSTATSTG** ppenum)
3832 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3834 IEnumSTATSTGImpl* newClone;
3837 * Perform a sanity check on the parameters.
3839 if (ppenum==0)
3840 return E_INVALIDARG;
3842 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3843 This->firstPropertyNode);
3847 * The new clone enumeration must point to the same current node as
3848 * the ole one.
3850 newClone->stackSize = This->stackSize ;
3851 newClone->stackMaxSize = This->stackMaxSize ;
3852 newClone->stackToVisit =
3853 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3855 memcpy(
3856 newClone->stackToVisit,
3857 This->stackToVisit,
3858 sizeof(ULONG) * newClone->stackSize);
3860 *ppenum = (IEnumSTATSTG*)newClone;
3863 * Don't forget to nail down a reference to the clone before
3864 * returning it.
3866 IEnumSTATSTGImpl_AddRef(*ppenum);
3868 return S_OK;
3871 INT IEnumSTATSTGImpl_FindParentProperty(
3872 IEnumSTATSTGImpl *This,
3873 ULONG childProperty,
3874 StgProperty *currentProperty,
3875 ULONG *thisNodeId)
3877 ULONG currentSearchNode;
3878 ULONG foundNode;
3881 * To avoid the special case, get another pointer to a ULONG value if
3882 * the caller didn't supply one.
3884 if (thisNodeId==0)
3885 thisNodeId = &foundNode;
3888 * Start with the node at the top of the stack.
3890 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3893 while (currentSearchNode!=PROPERTY_NULL)
3896 * Store the current node in the returned parameters
3898 *thisNodeId = currentSearchNode;
3901 * Remove the top node from the stack
3903 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3906 * Read the property from the storage.
3908 StorageImpl_ReadProperty(
3909 This->parentStorage,
3910 currentSearchNode,
3911 currentProperty);
3913 if (currentProperty->previousProperty == childProperty)
3914 return PROPERTY_RELATION_PREVIOUS;
3916 else if (currentProperty->nextProperty == childProperty)
3917 return PROPERTY_RELATION_NEXT;
3919 else if (currentProperty->dirProperty == childProperty)
3920 return PROPERTY_RELATION_DIR;
3923 * Push the next search node in the search stack.
3925 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3928 * continue the iteration.
3930 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3933 return PROPERTY_NULL;
3936 ULONG IEnumSTATSTGImpl_FindProperty(
3937 IEnumSTATSTGImpl* This,
3938 const OLECHAR* lpszPropName,
3939 StgProperty* currentProperty)
3941 ULONG currentSearchNode;
3944 * Start with the node at the top of the stack.
3946 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3948 while (currentSearchNode!=PROPERTY_NULL)
3951 * Remove the top node from the stack
3953 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3956 * Read the property from the storage.
3958 StorageImpl_ReadProperty(This->parentStorage,
3959 currentSearchNode,
3960 currentProperty);
3962 if ( propertyNameCmp(
3963 (OLECHAR*)currentProperty->name,
3964 (OLECHAR*)lpszPropName) == 0)
3965 return currentSearchNode;
3968 * Push the next search node in the search stack.
3970 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3973 * continue the iteration.
3975 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3978 return PROPERTY_NULL;
3981 void IEnumSTATSTGImpl_PushSearchNode(
3982 IEnumSTATSTGImpl* This,
3983 ULONG nodeToPush)
3985 StgProperty rootProperty;
3986 BOOL readSuccessful;
3989 * First, make sure we're not trying to push an unexisting node.
3991 if (nodeToPush==PROPERTY_NULL)
3992 return;
3995 * First push the node to the stack
3997 if (This->stackSize == This->stackMaxSize)
3999 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4001 This->stackToVisit = HeapReAlloc(
4002 GetProcessHeap(),
4004 This->stackToVisit,
4005 sizeof(ULONG) * This->stackMaxSize);
4008 This->stackToVisit[This->stackSize] = nodeToPush;
4009 This->stackSize++;
4012 * Read the root property from the storage.
4014 readSuccessful = StorageImpl_ReadProperty(
4015 This->parentStorage,
4016 nodeToPush,
4017 &rootProperty);
4019 if (readSuccessful)
4021 assert(rootProperty.sizeOfNameString!=0);
4024 * Push the previous search node in the search stack.
4026 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4030 ULONG IEnumSTATSTGImpl_PopSearchNode(
4031 IEnumSTATSTGImpl* This,
4032 BOOL remove)
4034 ULONG topNode;
4036 if (This->stackSize == 0)
4037 return PROPERTY_NULL;
4039 topNode = This->stackToVisit[This->stackSize-1];
4041 if (remove)
4042 This->stackSize--;
4044 return topNode;
4047 /******************************************************************************
4048 ** StorageUtl implementation
4051 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4053 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4056 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4058 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4061 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4063 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4066 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4068 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4071 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4073 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4074 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4075 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4077 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4080 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4082 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4083 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4084 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4086 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4089 void StorageUtl_CopyPropertyToSTATSTG(
4090 STATSTG* destination,
4091 StgProperty* source,
4092 int statFlags)
4095 * The copy of the string occurs only when the flag is not set
4097 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4098 (source->name == NULL) ||
4099 (source->name[0] == 0) )
4101 destination->pwcsName = 0;
4103 else
4105 destination->pwcsName =
4106 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4108 strcpyW((LPWSTR)destination->pwcsName, source->name);
4111 switch (source->propertyType)
4113 case PROPTYPE_STORAGE:
4114 case PROPTYPE_ROOT:
4115 destination->type = STGTY_STORAGE;
4116 break;
4117 case PROPTYPE_STREAM:
4118 destination->type = STGTY_STREAM;
4119 break;
4120 default:
4121 destination->type = STGTY_STREAM;
4122 break;
4125 destination->cbSize = source->size;
4127 currentReturnStruct->mtime = {0}; TODO
4128 currentReturnStruct->ctime = {0};
4129 currentReturnStruct->atime = {0};
4131 destination->grfMode = 0;
4132 destination->grfLocksSupported = 0;
4133 destination->clsid = source->propertyUniqueID;
4134 destination->grfStateBits = 0;
4135 destination->reserved = 0;
4138 /******************************************************************************
4139 ** BlockChainStream implementation
4142 BlockChainStream* BlockChainStream_Construct(
4143 StorageImpl* parentStorage,
4144 ULONG* headOfStreamPlaceHolder,
4145 ULONG propertyIndex)
4147 BlockChainStream* newStream;
4148 ULONG blockIndex;
4150 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4152 newStream->parentStorage = parentStorage;
4153 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4154 newStream->ownerPropertyIndex = propertyIndex;
4155 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4156 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4157 newStream->numBlocks = 0;
4159 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4161 while (blockIndex != BLOCK_END_OF_CHAIN)
4163 newStream->numBlocks++;
4164 newStream->tailIndex = blockIndex;
4166 if(FAILED(StorageImpl_GetNextBlockInChain(
4167 parentStorage,
4168 blockIndex,
4169 &blockIndex)))
4171 HeapFree(GetProcessHeap(), 0, newStream);
4172 return NULL;
4176 return newStream;
4179 void BlockChainStream_Destroy(BlockChainStream* This)
4181 HeapFree(GetProcessHeap(), 0, This);
4184 /******************************************************************************
4185 * BlockChainStream_GetHeadOfChain
4187 * Returns the head of this stream chain.
4188 * Some special chains don't have properties, their heads are kept in
4189 * This->headOfStreamPlaceHolder.
4192 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4194 StgProperty chainProperty;
4195 BOOL readSuccessful;
4197 if (This->headOfStreamPlaceHolder != 0)
4198 return *(This->headOfStreamPlaceHolder);
4200 if (This->ownerPropertyIndex != PROPERTY_NULL)
4202 readSuccessful = StorageImpl_ReadProperty(
4203 This->parentStorage,
4204 This->ownerPropertyIndex,
4205 &chainProperty);
4207 if (readSuccessful)
4209 return chainProperty.startingBlock;
4213 return BLOCK_END_OF_CHAIN;
4216 /******************************************************************************
4217 * BlockChainStream_GetCount
4219 * Returns the number of blocks that comprises this chain.
4220 * This is not the size of the stream as the last block may not be full!
4223 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4225 ULONG blockIndex;
4226 ULONG count = 0;
4228 blockIndex = BlockChainStream_GetHeadOfChain(This);
4230 while (blockIndex != BLOCK_END_OF_CHAIN)
4232 count++;
4234 if(FAILED(StorageImpl_GetNextBlockInChain(
4235 This->parentStorage,
4236 blockIndex,
4237 &blockIndex)))
4238 return 0;
4241 return count;
4244 /******************************************************************************
4245 * BlockChainStream_ReadAt
4247 * Reads a specified number of bytes from this chain at the specified offset.
4248 * bytesRead may be NULL.
4249 * Failure will be returned if the specified number of bytes has not been read.
4251 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4252 ULARGE_INTEGER offset,
4253 ULONG size,
4254 void* buffer,
4255 ULONG* bytesRead)
4257 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4258 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4259 ULONG bytesToReadInBuffer;
4260 ULONG blockIndex;
4261 BYTE* bufferWalker;
4262 BYTE* bigBlockBuffer;
4265 * Find the first block in the stream that contains part of the buffer.
4267 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4268 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4269 (blockNoInSequence < This->lastBlockNoInSequence) )
4271 blockIndex = BlockChainStream_GetHeadOfChain(This);
4272 This->lastBlockNoInSequence = blockNoInSequence;
4274 else
4276 ULONG temp = blockNoInSequence;
4278 blockIndex = This->lastBlockNoInSequenceIndex;
4279 blockNoInSequence -= This->lastBlockNoInSequence;
4280 This->lastBlockNoInSequence = temp;
4283 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4285 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4286 return FALSE;
4287 blockNoInSequence--;
4290 This->lastBlockNoInSequenceIndex = blockIndex;
4293 * Start reading the buffer.
4295 *bytesRead = 0;
4296 bufferWalker = buffer;
4298 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4301 * Calculate how many bytes we can copy from this big block.
4303 bytesToReadInBuffer =
4304 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4307 * Copy those bytes to the buffer
4309 bigBlockBuffer =
4310 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4312 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4314 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4317 * Step to the next big block.
4319 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4320 return FALSE;
4322 bufferWalker += bytesToReadInBuffer;
4323 size -= bytesToReadInBuffer;
4324 *bytesRead += bytesToReadInBuffer;
4325 offsetInBlock = 0; /* There is no offset on the next block */
4329 return (size == 0);
4332 /******************************************************************************
4333 * BlockChainStream_WriteAt
4335 * Writes the specified number of bytes to this chain at the specified offset.
4336 * bytesWritten may be NULL.
4337 * Will fail if not all specified number of bytes have been written.
4339 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4340 ULARGE_INTEGER offset,
4341 ULONG size,
4342 const void* buffer,
4343 ULONG* bytesWritten)
4345 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4346 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4347 ULONG bytesToWrite;
4348 ULONG blockIndex;
4349 BYTE* bufferWalker;
4350 BYTE* bigBlockBuffer;
4353 * Find the first block in the stream that contains part of the buffer.
4355 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4356 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4357 (blockNoInSequence < This->lastBlockNoInSequence) )
4359 blockIndex = BlockChainStream_GetHeadOfChain(This);
4360 This->lastBlockNoInSequence = blockNoInSequence;
4362 else
4364 ULONG temp = blockNoInSequence;
4366 blockIndex = This->lastBlockNoInSequenceIndex;
4367 blockNoInSequence -= This->lastBlockNoInSequence;
4368 This->lastBlockNoInSequence = temp;
4371 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4373 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4374 &blockIndex)))
4375 return FALSE;
4376 blockNoInSequence--;
4379 This->lastBlockNoInSequenceIndex = blockIndex;
4382 * Here, I'm casting away the constness on the buffer variable
4383 * This is OK since we don't intend to modify that buffer.
4385 *bytesWritten = 0;
4386 bufferWalker = (BYTE*)buffer;
4388 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4391 * Calculate how many bytes we can copy from this big block.
4393 bytesToWrite =
4394 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4397 * Copy those bytes to the buffer
4399 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4401 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4403 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4406 * Step to the next big block.
4408 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4409 &blockIndex)))
4410 return FALSE;
4411 bufferWalker += bytesToWrite;
4412 size -= bytesToWrite;
4413 *bytesWritten += bytesToWrite;
4414 offsetInBlock = 0; /* There is no offset on the next block */
4417 return (size == 0);
4420 /******************************************************************************
4421 * BlockChainStream_Shrink
4423 * Shrinks this chain in the big block depot.
4425 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4426 ULARGE_INTEGER newSize)
4428 ULONG blockIndex, extraBlock;
4429 ULONG numBlocks;
4430 ULONG count = 1;
4433 * Reset the last accessed block cache.
4435 This->lastBlockNoInSequence = 0xFFFFFFFF;
4436 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4439 * Figure out how many blocks are needed to contain the new size
4441 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4443 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4444 numBlocks++;
4446 blockIndex = BlockChainStream_GetHeadOfChain(This);
4449 * Go to the new end of chain
4451 while (count < numBlocks)
4453 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4454 &blockIndex)))
4455 return FALSE;
4456 count++;
4459 /* Get the next block before marking the new end */
4460 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4461 &extraBlock)))
4462 return FALSE;
4464 /* Mark the new end of chain */
4465 StorageImpl_SetNextBlockInChain(
4466 This->parentStorage,
4467 blockIndex,
4468 BLOCK_END_OF_CHAIN);
4470 This->tailIndex = blockIndex;
4471 This->numBlocks = numBlocks;
4474 * Mark the extra blocks as free
4476 while (extraBlock != BLOCK_END_OF_CHAIN)
4478 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4479 &blockIndex)))
4480 return FALSE;
4481 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4482 extraBlock = blockIndex;
4485 return TRUE;
4488 /******************************************************************************
4489 * BlockChainStream_Enlarge
4491 * Grows this chain in the big block depot.
4493 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4494 ULARGE_INTEGER newSize)
4496 ULONG blockIndex, currentBlock;
4497 ULONG newNumBlocks;
4498 ULONG oldNumBlocks = 0;
4500 blockIndex = BlockChainStream_GetHeadOfChain(This);
4503 * Empty chain. Create the head.
4505 if (blockIndex == BLOCK_END_OF_CHAIN)
4507 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4508 StorageImpl_SetNextBlockInChain(This->parentStorage,
4509 blockIndex,
4510 BLOCK_END_OF_CHAIN);
4512 if (This->headOfStreamPlaceHolder != 0)
4514 *(This->headOfStreamPlaceHolder) = blockIndex;
4516 else
4518 StgProperty chainProp;
4519 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4521 StorageImpl_ReadProperty(
4522 This->parentStorage,
4523 This->ownerPropertyIndex,
4524 &chainProp);
4526 chainProp.startingBlock = blockIndex;
4528 StorageImpl_WriteProperty(
4529 This->parentStorage,
4530 This->ownerPropertyIndex,
4531 &chainProp);
4534 This->tailIndex = blockIndex;
4535 This->numBlocks = 1;
4539 * Figure out how many blocks are needed to contain this stream
4541 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4543 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4544 newNumBlocks++;
4547 * Go to the current end of chain
4549 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4551 currentBlock = blockIndex;
4553 while (blockIndex != BLOCK_END_OF_CHAIN)
4555 This->numBlocks++;
4556 currentBlock = blockIndex;
4558 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4559 &blockIndex)))
4560 return FALSE;
4563 This->tailIndex = currentBlock;
4566 currentBlock = This->tailIndex;
4567 oldNumBlocks = This->numBlocks;
4570 * Add new blocks to the chain
4572 if (oldNumBlocks < newNumBlocks)
4574 while (oldNumBlocks < newNumBlocks)
4576 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4578 StorageImpl_SetNextBlockInChain(
4579 This->parentStorage,
4580 currentBlock,
4581 blockIndex);
4583 StorageImpl_SetNextBlockInChain(
4584 This->parentStorage,
4585 blockIndex,
4586 BLOCK_END_OF_CHAIN);
4588 currentBlock = blockIndex;
4589 oldNumBlocks++;
4592 This->tailIndex = blockIndex;
4593 This->numBlocks = newNumBlocks;
4596 return TRUE;
4599 /******************************************************************************
4600 * BlockChainStream_SetSize
4602 * Sets the size of this stream. The big block depot will be updated.
4603 * The file will grow if we grow the chain.
4605 * TODO: Free the actual blocks in the file when we shrink the chain.
4606 * Currently, the blocks are still in the file. So the file size
4607 * doesn't shrink even if we shrink streams.
4609 BOOL BlockChainStream_SetSize(
4610 BlockChainStream* This,
4611 ULARGE_INTEGER newSize)
4613 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4615 if (newSize.u.LowPart == size.u.LowPart)
4616 return TRUE;
4618 if (newSize.u.LowPart < size.u.LowPart)
4620 BlockChainStream_Shrink(This, newSize);
4622 else
4624 ULARGE_INTEGER fileSize =
4625 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4627 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4630 * Make sure the file stays a multiple of blocksize
4632 if ((diff % This->parentStorage->bigBlockSize) != 0)
4633 diff += (This->parentStorage->bigBlockSize -
4634 (diff % This->parentStorage->bigBlockSize) );
4636 fileSize.u.LowPart += diff;
4637 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4639 BlockChainStream_Enlarge(This, newSize);
4642 return TRUE;
4645 /******************************************************************************
4646 * BlockChainStream_GetSize
4648 * Returns the size of this chain.
4649 * Will return the block count if this chain doesn't have a property.
4651 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4653 StgProperty chainProperty;
4655 if(This->headOfStreamPlaceHolder == NULL)
4658 * This chain is a data stream read the property and return
4659 * the appropriate size
4661 StorageImpl_ReadProperty(
4662 This->parentStorage,
4663 This->ownerPropertyIndex,
4664 &chainProperty);
4666 return chainProperty.size;
4668 else
4671 * this chain is a chain that does not have a property, figure out the
4672 * size by making the product number of used blocks times the
4673 * size of them
4675 ULARGE_INTEGER result;
4676 result.u.HighPart = 0;
4678 result.u.LowPart =
4679 BlockChainStream_GetCount(This) *
4680 This->parentStorage->bigBlockSize;
4682 return result;
4686 /******************************************************************************
4687 ** SmallBlockChainStream implementation
4690 SmallBlockChainStream* SmallBlockChainStream_Construct(
4691 StorageImpl* parentStorage,
4692 ULONG propertyIndex)
4694 SmallBlockChainStream* newStream;
4696 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4698 newStream->parentStorage = parentStorage;
4699 newStream->ownerPropertyIndex = propertyIndex;
4701 return newStream;
4704 void SmallBlockChainStream_Destroy(
4705 SmallBlockChainStream* This)
4707 HeapFree(GetProcessHeap(), 0, This);
4710 /******************************************************************************
4711 * SmallBlockChainStream_GetHeadOfChain
4713 * Returns the head of this chain of small blocks.
4715 ULONG SmallBlockChainStream_GetHeadOfChain(
4716 SmallBlockChainStream* This)
4718 StgProperty chainProperty;
4719 BOOL readSuccessful;
4721 if (This->ownerPropertyIndex)
4723 readSuccessful = StorageImpl_ReadProperty(
4724 This->parentStorage,
4725 This->ownerPropertyIndex,
4726 &chainProperty);
4728 if (readSuccessful)
4730 return chainProperty.startingBlock;
4735 return BLOCK_END_OF_CHAIN;
4738 /******************************************************************************
4739 * SmallBlockChainStream_GetNextBlockInChain
4741 * Returns the index of the next small block in this chain.
4743 * Return Values:
4744 * - BLOCK_END_OF_CHAIN: end of this chain
4745 * - BLOCK_UNUSED: small block 'blockIndex' is free
4747 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4748 SmallBlockChainStream* This,
4749 ULONG blockIndex,
4750 ULONG* nextBlockInChain)
4752 ULARGE_INTEGER offsetOfBlockInDepot;
4753 DWORD buffer;
4754 ULONG bytesRead;
4755 BOOL success;
4757 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4759 offsetOfBlockInDepot.u.HighPart = 0;
4760 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4763 * Read those bytes in the buffer from the small block file.
4765 success = BlockChainStream_ReadAt(
4766 This->parentStorage->smallBlockDepotChain,
4767 offsetOfBlockInDepot,
4768 sizeof(DWORD),
4769 &buffer,
4770 &bytesRead);
4772 if (success)
4774 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4775 return S_OK;
4778 return STG_E_READFAULT;
4781 /******************************************************************************
4782 * SmallBlockChainStream_SetNextBlockInChain
4784 * Writes the index of the next block of the specified block in the small
4785 * block depot.
4786 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4787 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4789 void SmallBlockChainStream_SetNextBlockInChain(
4790 SmallBlockChainStream* This,
4791 ULONG blockIndex,
4792 ULONG nextBlock)
4794 ULARGE_INTEGER offsetOfBlockInDepot;
4795 DWORD buffer;
4796 ULONG bytesWritten;
4798 offsetOfBlockInDepot.u.HighPart = 0;
4799 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4801 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4804 * Read those bytes in the buffer from the small block file.
4806 BlockChainStream_WriteAt(
4807 This->parentStorage->smallBlockDepotChain,
4808 offsetOfBlockInDepot,
4809 sizeof(DWORD),
4810 &buffer,
4811 &bytesWritten);
4814 /******************************************************************************
4815 * SmallBlockChainStream_FreeBlock
4817 * Flag small block 'blockIndex' as free in the small block depot.
4819 void SmallBlockChainStream_FreeBlock(
4820 SmallBlockChainStream* This,
4821 ULONG blockIndex)
4823 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4826 /******************************************************************************
4827 * SmallBlockChainStream_GetNextFreeBlock
4829 * Returns the index of a free small block. The small block depot will be
4830 * enlarged if necessary. The small block chain will also be enlarged if
4831 * necessary.
4833 ULONG SmallBlockChainStream_GetNextFreeBlock(
4834 SmallBlockChainStream* This)
4836 ULARGE_INTEGER offsetOfBlockInDepot;
4837 DWORD buffer;
4838 ULONG bytesRead;
4839 ULONG blockIndex = 0;
4840 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4841 BOOL success = TRUE;
4842 ULONG smallBlocksPerBigBlock;
4844 offsetOfBlockInDepot.u.HighPart = 0;
4847 * Scan the small block depot for a free block
4849 while (nextBlockIndex != BLOCK_UNUSED)
4851 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4853 success = BlockChainStream_ReadAt(
4854 This->parentStorage->smallBlockDepotChain,
4855 offsetOfBlockInDepot,
4856 sizeof(DWORD),
4857 &buffer,
4858 &bytesRead);
4861 * If we run out of space for the small block depot, enlarge it
4863 if (success)
4865 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4867 if (nextBlockIndex != BLOCK_UNUSED)
4868 blockIndex++;
4870 else
4872 ULONG count =
4873 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4875 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4876 ULONG nextBlock, newsbdIndex;
4877 BYTE* smallBlockDepot;
4879 nextBlock = sbdIndex;
4880 while (nextBlock != BLOCK_END_OF_CHAIN)
4882 sbdIndex = nextBlock;
4883 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4886 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4887 if (sbdIndex != BLOCK_END_OF_CHAIN)
4888 StorageImpl_SetNextBlockInChain(
4889 This->parentStorage,
4890 sbdIndex,
4891 newsbdIndex);
4893 StorageImpl_SetNextBlockInChain(
4894 This->parentStorage,
4895 newsbdIndex,
4896 BLOCK_END_OF_CHAIN);
4899 * Initialize all the small blocks to free
4901 smallBlockDepot =
4902 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4904 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4905 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4907 if (count == 0)
4910 * We have just created the small block depot.
4912 StgProperty rootProp;
4913 ULONG sbStartIndex;
4916 * Save it in the header
4918 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4919 StorageImpl_SaveFileHeader(This->parentStorage);
4922 * And allocate the first big block that will contain small blocks
4924 sbStartIndex =
4925 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4927 StorageImpl_SetNextBlockInChain(
4928 This->parentStorage,
4929 sbStartIndex,
4930 BLOCK_END_OF_CHAIN);
4932 StorageImpl_ReadProperty(
4933 This->parentStorage,
4934 This->parentStorage->rootPropertySetIndex,
4935 &rootProp);
4937 rootProp.startingBlock = sbStartIndex;
4938 rootProp.size.u.HighPart = 0;
4939 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
4941 StorageImpl_WriteProperty(
4942 This->parentStorage,
4943 This->parentStorage->rootPropertySetIndex,
4944 &rootProp);
4949 smallBlocksPerBigBlock =
4950 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4953 * Verify if we have to allocate big blocks to contain small blocks
4955 if (blockIndex % smallBlocksPerBigBlock == 0)
4957 StgProperty rootProp;
4958 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4960 StorageImpl_ReadProperty(
4961 This->parentStorage,
4962 This->parentStorage->rootPropertySetIndex,
4963 &rootProp);
4965 if (rootProp.size.u.LowPart <
4966 (blocksRequired * This->parentStorage->bigBlockSize))
4968 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
4970 BlockChainStream_SetSize(
4971 This->parentStorage->smallBlockRootChain,
4972 rootProp.size);
4974 StorageImpl_WriteProperty(
4975 This->parentStorage,
4976 This->parentStorage->rootPropertySetIndex,
4977 &rootProp);
4981 return blockIndex;
4984 /******************************************************************************
4985 * SmallBlockChainStream_ReadAt
4987 * Reads a specified number of bytes from this chain at the specified offset.
4988 * bytesRead may be NULL.
4989 * Failure will be returned if the specified number of bytes has not been read.
4991 BOOL SmallBlockChainStream_ReadAt(
4992 SmallBlockChainStream* This,
4993 ULARGE_INTEGER offset,
4994 ULONG size,
4995 void* buffer,
4996 ULONG* bytesRead)
4998 ULARGE_INTEGER offsetInBigBlockFile;
4999 ULONG blockNoInSequence =
5000 offset.u.LowPart / This->parentStorage->smallBlockSize;
5002 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5003 ULONG bytesToReadInBuffer;
5004 ULONG blockIndex;
5005 ULONG bytesReadFromBigBlockFile;
5006 BYTE* bufferWalker;
5009 * This should never happen on a small block file.
5011 assert(offset.u.HighPart==0);
5014 * Find the first block in the stream that contains part of the buffer.
5016 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5018 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5020 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5021 &blockIndex)))
5022 return FALSE;
5023 blockNoInSequence--;
5027 * Start reading the buffer.
5029 *bytesRead = 0;
5030 bufferWalker = buffer;
5032 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5035 * Calculate how many bytes we can copy from this small block.
5037 bytesToReadInBuffer =
5038 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5041 * Calculate the offset of the small block in the small block file.
5043 offsetInBigBlockFile.u.HighPart = 0;
5044 offsetInBigBlockFile.u.LowPart =
5045 blockIndex * This->parentStorage->smallBlockSize;
5047 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5050 * Read those bytes in the buffer from the small block file.
5052 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5053 offsetInBigBlockFile,
5054 bytesToReadInBuffer,
5055 bufferWalker,
5056 &bytesReadFromBigBlockFile);
5058 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5061 * Step to the next big block.
5063 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5064 return FALSE;
5065 bufferWalker += bytesToReadInBuffer;
5066 size -= bytesToReadInBuffer;
5067 *bytesRead += bytesToReadInBuffer;
5068 offsetInBlock = 0; /* There is no offset on the next block */
5071 return (size == 0);
5074 /******************************************************************************
5075 * SmallBlockChainStream_WriteAt
5077 * Writes the specified number of bytes to this chain at the specified offset.
5078 * bytesWritten may be NULL.
5079 * Will fail if not all specified number of bytes have been written.
5081 BOOL SmallBlockChainStream_WriteAt(
5082 SmallBlockChainStream* This,
5083 ULARGE_INTEGER offset,
5084 ULONG size,
5085 const void* buffer,
5086 ULONG* bytesWritten)
5088 ULARGE_INTEGER offsetInBigBlockFile;
5089 ULONG blockNoInSequence =
5090 offset.u.LowPart / This->parentStorage->smallBlockSize;
5092 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5093 ULONG bytesToWriteInBuffer;
5094 ULONG blockIndex;
5095 ULONG bytesWrittenFromBigBlockFile;
5096 BYTE* bufferWalker;
5099 * This should never happen on a small block file.
5101 assert(offset.u.HighPart==0);
5104 * Find the first block in the stream that contains part of the buffer.
5106 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5108 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5110 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5111 return FALSE;
5112 blockNoInSequence--;
5116 * Start writing the buffer.
5118 * Here, I'm casting away the constness on the buffer variable
5119 * This is OK since we don't intend to modify that buffer.
5121 *bytesWritten = 0;
5122 bufferWalker = (BYTE*)buffer;
5123 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5126 * Calculate how many bytes we can copy to this small block.
5128 bytesToWriteInBuffer =
5129 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5132 * Calculate the offset of the small block in the small block file.
5134 offsetInBigBlockFile.u.HighPart = 0;
5135 offsetInBigBlockFile.u.LowPart =
5136 blockIndex * This->parentStorage->smallBlockSize;
5138 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5141 * Write those bytes in the buffer to the small block file.
5143 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5144 offsetInBigBlockFile,
5145 bytesToWriteInBuffer,
5146 bufferWalker,
5147 &bytesWrittenFromBigBlockFile);
5149 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5152 * Step to the next big block.
5154 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5155 &blockIndex)))
5156 return FALSE;
5157 bufferWalker += bytesToWriteInBuffer;
5158 size -= bytesToWriteInBuffer;
5159 *bytesWritten += bytesToWriteInBuffer;
5160 offsetInBlock = 0; /* There is no offset on the next block */
5163 return (size == 0);
5166 /******************************************************************************
5167 * SmallBlockChainStream_Shrink
5169 * Shrinks this chain in the small block depot.
5171 BOOL SmallBlockChainStream_Shrink(
5172 SmallBlockChainStream* This,
5173 ULARGE_INTEGER newSize)
5175 ULONG blockIndex, extraBlock;
5176 ULONG numBlocks;
5177 ULONG count = 0;
5179 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5181 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5182 numBlocks++;
5184 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5187 * Go to the new end of chain
5189 while (count < numBlocks)
5191 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5192 &blockIndex)))
5193 return FALSE;
5194 count++;
5198 * If the count is 0, we have a special case, the head of the chain was
5199 * just freed.
5201 if (count == 0)
5203 StgProperty chainProp;
5205 StorageImpl_ReadProperty(This->parentStorage,
5206 This->ownerPropertyIndex,
5207 &chainProp);
5209 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5211 StorageImpl_WriteProperty(This->parentStorage,
5212 This->ownerPropertyIndex,
5213 &chainProp);
5216 * We start freeing the chain at the head block.
5218 extraBlock = blockIndex;
5220 else
5222 /* Get the next block before marking the new end */
5223 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5224 &extraBlock)))
5225 return FALSE;
5227 /* Mark the new end of chain */
5228 SmallBlockChainStream_SetNextBlockInChain(
5229 This,
5230 blockIndex,
5231 BLOCK_END_OF_CHAIN);
5235 * Mark the extra blocks as free
5237 while (extraBlock != BLOCK_END_OF_CHAIN)
5239 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5240 &blockIndex)))
5241 return FALSE;
5242 SmallBlockChainStream_FreeBlock(This, extraBlock);
5243 extraBlock = blockIndex;
5246 return TRUE;
5249 /******************************************************************************
5250 * SmallBlockChainStream_Enlarge
5252 * Grows this chain in the small block depot.
5254 BOOL SmallBlockChainStream_Enlarge(
5255 SmallBlockChainStream* This,
5256 ULARGE_INTEGER newSize)
5258 ULONG blockIndex, currentBlock;
5259 ULONG newNumBlocks;
5260 ULONG oldNumBlocks = 0;
5262 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5265 * Empty chain
5267 if (blockIndex == BLOCK_END_OF_CHAIN)
5270 StgProperty chainProp;
5272 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5273 &chainProp);
5275 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5277 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5278 &chainProp);
5280 blockIndex = chainProp.startingBlock;
5281 SmallBlockChainStream_SetNextBlockInChain(
5282 This,
5283 blockIndex,
5284 BLOCK_END_OF_CHAIN);
5287 currentBlock = blockIndex;
5290 * Figure out how many blocks are needed to contain this stream
5292 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5294 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5295 newNumBlocks++;
5298 * Go to the current end of chain
5300 while (blockIndex != BLOCK_END_OF_CHAIN)
5302 oldNumBlocks++;
5303 currentBlock = blockIndex;
5304 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5305 return FALSE;
5309 * Add new blocks to the chain
5311 while (oldNumBlocks < newNumBlocks)
5313 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5314 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5316 SmallBlockChainStream_SetNextBlockInChain(
5317 This,
5318 blockIndex,
5319 BLOCK_END_OF_CHAIN);
5321 currentBlock = blockIndex;
5322 oldNumBlocks++;
5325 return TRUE;
5328 /******************************************************************************
5329 * SmallBlockChainStream_GetCount
5331 * Returns the number of blocks that comprises this chain.
5332 * This is not the size of this chain as the last block may not be full!
5334 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5336 ULONG blockIndex;
5337 ULONG count = 0;
5339 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5341 while (blockIndex != BLOCK_END_OF_CHAIN)
5343 count++;
5345 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5346 return 0;
5349 return count;
5352 /******************************************************************************
5353 * SmallBlockChainStream_SetSize
5355 * Sets the size of this stream.
5356 * The file will grow if we grow the chain.
5358 * TODO: Free the actual blocks in the file when we shrink the chain.
5359 * Currently, the blocks are still in the file. So the file size
5360 * doesn't shrink even if we shrink streams.
5362 BOOL SmallBlockChainStream_SetSize(
5363 SmallBlockChainStream* This,
5364 ULARGE_INTEGER newSize)
5366 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5368 if (newSize.u.LowPart == size.u.LowPart)
5369 return TRUE;
5371 if (newSize.u.LowPart < size.u.LowPart)
5373 SmallBlockChainStream_Shrink(This, newSize);
5375 else
5377 SmallBlockChainStream_Enlarge(This, newSize);
5380 return TRUE;
5383 /******************************************************************************
5384 * SmallBlockChainStream_GetSize
5386 * Returns the size of this chain.
5388 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5390 StgProperty chainProperty;
5392 StorageImpl_ReadProperty(
5393 This->parentStorage,
5394 This->ownerPropertyIndex,
5395 &chainProperty);
5397 return chainProperty.size;
5400 /******************************************************************************
5401 * StgCreateDocfile [OLE32.@]
5403 HRESULT WINAPI StgCreateDocfile(
5404 LPCOLESTR pwcsName,
5405 DWORD grfMode,
5406 DWORD reserved,
5407 IStorage **ppstgOpen)
5409 StorageImpl* newStorage = 0;
5410 HANDLE hFile = INVALID_HANDLE_VALUE;
5411 HRESULT hr = S_OK;
5412 DWORD shareMode;
5413 DWORD accessMode;
5414 DWORD creationMode;
5415 DWORD fileAttributes;
5416 WCHAR tempFileName[MAX_PATH];
5418 TRACE("(%s, %lx, %ld, %p)\n",
5419 debugstr_w(pwcsName), grfMode,
5420 reserved, ppstgOpen);
5423 * Validate the parameters
5425 if (ppstgOpen == 0)
5426 return STG_E_INVALIDPOINTER;
5429 * Validate the STGM flags
5431 if ( FAILED( validateSTGM(grfMode) ))
5432 return STG_E_INVALIDFLAG;
5435 * Generate a unique name.
5437 if (pwcsName == 0)
5439 WCHAR tempPath[MAX_PATH];
5440 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5442 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5443 return STG_E_INVALIDFLAG;
5444 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5445 return STG_E_INVALIDFLAG;
5447 memset(tempPath, 0, sizeof(tempPath));
5448 memset(tempFileName, 0, sizeof(tempFileName));
5450 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5451 tempPath[0] = '.';
5453 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5454 pwcsName = tempFileName;
5455 else
5456 return STG_E_INSUFFICIENTMEMORY;
5458 creationMode = TRUNCATE_EXISTING;
5460 else
5462 creationMode = GetCreationModeFromSTGM(grfMode);
5466 * Interpret the STGM value grfMode
5468 shareMode = GetShareModeFromSTGM(grfMode);
5469 accessMode = GetAccessModeFromSTGM(grfMode);
5471 if (grfMode & STGM_DELETEONRELEASE)
5472 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5473 else
5474 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5476 if (grfMode & STGM_TRANSACTED)
5477 FIXME("Transacted mode not implemented.\n");
5480 * Initialize the "out" parameter.
5482 *ppstgOpen = 0;
5484 hFile = CreateFileW(pwcsName,
5485 accessMode,
5486 shareMode,
5487 NULL,
5488 creationMode,
5489 fileAttributes,
5492 if (hFile == INVALID_HANDLE_VALUE)
5494 return E_FAIL;
5498 * Allocate and initialize the new IStorage32object.
5500 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5502 if (newStorage == 0)
5503 return STG_E_INSUFFICIENTMEMORY;
5505 hr = StorageImpl_Construct(
5506 newStorage,
5507 hFile,
5508 pwcsName,
5509 NULL,
5510 grfMode,
5511 TRUE,
5512 TRUE);
5514 if (FAILED(hr))
5516 HeapFree(GetProcessHeap(), 0, newStorage);
5517 return hr;
5521 * Get an "out" pointer for the caller.
5523 hr = StorageBaseImpl_QueryInterface(
5524 (IStorage*)newStorage,
5525 (REFIID)&IID_IStorage,
5526 (void**)ppstgOpen);
5528 return hr;
5531 /******************************************************************************
5532 * StgCreateStorageEx [OLE32.@]
5534 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5536 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5537 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5538 return STG_E_UNIMPLEMENTEDFUNCTION;
5541 /******************************************************************************
5542 * StgOpenStorage [OLE32.@]
5544 HRESULT WINAPI StgOpenStorage(
5545 const OLECHAR *pwcsName,
5546 IStorage *pstgPriority,
5547 DWORD grfMode,
5548 SNB snbExclude,
5549 DWORD reserved,
5550 IStorage **ppstgOpen)
5552 StorageImpl* newStorage = 0;
5553 HRESULT hr = S_OK;
5554 HANDLE hFile = 0;
5555 DWORD shareMode;
5556 DWORD accessMode;
5557 WCHAR fullname[MAX_PATH];
5558 DWORD length;
5560 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5561 debugstr_w(pwcsName), pstgPriority, grfMode,
5562 snbExclude, reserved, ppstgOpen);
5565 * Perform a sanity check
5567 if (( pwcsName == 0) || (ppstgOpen == 0) )
5569 hr = STG_E_INVALIDPOINTER;
5570 goto end;
5574 * Validate the STGM flags
5576 if ( FAILED( validateSTGM(grfMode) ))
5578 hr = STG_E_INVALIDFLAG;
5579 goto end;
5583 * Interpret the STGM value grfMode
5585 shareMode = GetShareModeFromSTGM(grfMode);
5586 accessMode = GetAccessModeFromSTGM(grfMode);
5589 * Initialize the "out" parameter.
5591 *ppstgOpen = 0;
5593 hFile = CreateFileW( pwcsName,
5594 accessMode,
5595 shareMode,
5596 NULL,
5597 OPEN_EXISTING,
5598 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5601 length = GetFileSize(hFile, NULL);
5603 if (hFile==INVALID_HANDLE_VALUE)
5605 DWORD last_error = GetLastError();
5607 hr = E_FAIL;
5609 switch (last_error)
5611 case ERROR_FILE_NOT_FOUND:
5612 hr = STG_E_FILENOTFOUND;
5613 break;
5615 case ERROR_PATH_NOT_FOUND:
5616 hr = STG_E_PATHNOTFOUND;
5617 break;
5619 case ERROR_ACCESS_DENIED:
5620 case ERROR_WRITE_PROTECT:
5621 hr = STG_E_ACCESSDENIED;
5622 break;
5624 case ERROR_SHARING_VIOLATION:
5625 hr = STG_E_SHAREVIOLATION;
5626 break;
5628 default:
5629 hr = E_FAIL;
5632 goto end;
5636 * Allocate and initialize the new IStorage32object.
5638 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5640 if (newStorage == 0)
5642 hr = STG_E_INSUFFICIENTMEMORY;
5643 goto end;
5646 /* if the file's length was zero, initialize the storage */
5647 hr = StorageImpl_Construct(
5648 newStorage,
5649 hFile,
5650 pwcsName,
5651 NULL,
5652 grfMode,
5653 TRUE,
5654 !length );
5656 if (FAILED(hr))
5658 HeapFree(GetProcessHeap(), 0, newStorage);
5660 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5662 if(hr == STG_E_INVALIDHEADER)
5663 hr = STG_E_FILEALREADYEXISTS;
5664 goto end;
5667 /* prepare the file name string given in lieu of the root property name */
5668 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5669 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5670 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5673 * Get an "out" pointer for the caller.
5675 hr = StorageBaseImpl_QueryInterface(
5676 (IStorage*)newStorage,
5677 (REFIID)&IID_IStorage,
5678 (void**)ppstgOpen);
5680 end:
5681 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5682 return hr;
5685 /******************************************************************************
5686 * StgCreateDocfileOnILockBytes [OLE32.@]
5688 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5689 ILockBytes *plkbyt,
5690 DWORD grfMode,
5691 DWORD reserved,
5692 IStorage** ppstgOpen)
5694 StorageImpl* newStorage = 0;
5695 HRESULT hr = S_OK;
5698 * Validate the parameters
5700 if ((ppstgOpen == 0) || (plkbyt == 0))
5701 return STG_E_INVALIDPOINTER;
5704 * Allocate and initialize the new IStorage object.
5706 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5708 if (newStorage == 0)
5709 return STG_E_INSUFFICIENTMEMORY;
5711 hr = StorageImpl_Construct(
5712 newStorage,
5715 plkbyt,
5716 grfMode,
5717 FALSE,
5718 TRUE);
5720 if (FAILED(hr))
5722 HeapFree(GetProcessHeap(), 0, newStorage);
5723 return hr;
5727 * Get an "out" pointer for the caller.
5729 hr = StorageBaseImpl_QueryInterface(
5730 (IStorage*)newStorage,
5731 (REFIID)&IID_IStorage,
5732 (void**)ppstgOpen);
5734 return hr;
5737 /******************************************************************************
5738 * StgOpenStorageOnILockBytes [OLE32.@]
5740 HRESULT WINAPI StgOpenStorageOnILockBytes(
5741 ILockBytes *plkbyt,
5742 IStorage *pstgPriority,
5743 DWORD grfMode,
5744 SNB snbExclude,
5745 DWORD reserved,
5746 IStorage **ppstgOpen)
5748 StorageImpl* newStorage = 0;
5749 HRESULT hr = S_OK;
5752 * Perform a sanity check
5754 if ((plkbyt == 0) || (ppstgOpen == 0))
5755 return STG_E_INVALIDPOINTER;
5758 * Validate the STGM flags
5760 if ( FAILED( validateSTGM(grfMode) ))
5761 return STG_E_INVALIDFLAG;
5764 * Initialize the "out" parameter.
5766 *ppstgOpen = 0;
5769 * Allocate and initialize the new IStorage object.
5771 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5773 if (newStorage == 0)
5774 return STG_E_INSUFFICIENTMEMORY;
5776 hr = StorageImpl_Construct(
5777 newStorage,
5780 plkbyt,
5781 grfMode,
5782 FALSE,
5783 FALSE);
5785 if (FAILED(hr))
5787 HeapFree(GetProcessHeap(), 0, newStorage);
5788 return hr;
5792 * Get an "out" pointer for the caller.
5794 hr = StorageBaseImpl_QueryInterface(
5795 (IStorage*)newStorage,
5796 (REFIID)&IID_IStorage,
5797 (void**)ppstgOpen);
5799 return hr;
5802 /******************************************************************************
5803 * StgSetTimes [ole32.@]
5804 * StgSetTimes [OLE32.@]
5808 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *a,
5809 FILETIME const *b, FILETIME const *c )
5811 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5812 return S_OK;
5815 /******************************************************************************
5816 * StgIsStorageILockBytes [OLE32.@]
5818 * Determines if the ILockBytes contains a storage object.
5820 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5822 BYTE sig[8];
5823 ULARGE_INTEGER offset;
5825 offset.u.HighPart = 0;
5826 offset.u.LowPart = 0;
5828 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5830 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5831 return S_OK;
5833 return S_FALSE;
5836 /******************************************************************************
5837 * WriteClassStg [OLE32.@]
5839 * This method will store the specified CLSID in the specified storage object
5841 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5843 HRESULT hRes;
5845 assert(pStg != 0);
5847 hRes = IStorage_SetClass(pStg, rclsid);
5849 return hRes;
5852 /***********************************************************************
5853 * ReadClassStg (OLE32.@)
5855 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5857 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5859 STATSTG pstatstg;
5860 HRESULT hRes;
5862 TRACE("()\n");
5864 if(pclsid==NULL)
5865 return E_POINTER;
5867 * read a STATSTG structure (contains the clsid) from the storage
5869 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5871 if(SUCCEEDED(hRes))
5872 *pclsid=pstatstg.clsid;
5874 return hRes;
5877 /***********************************************************************
5878 * OleLoadFromStream (OLE32.@)
5880 * This function loads an object from stream
5882 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5884 CLSID clsid;
5885 HRESULT res;
5886 LPPERSISTSTREAM xstm;
5888 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5890 res=ReadClassStm(pStm,&clsid);
5891 if (!SUCCEEDED(res))
5892 return res;
5893 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5894 if (!SUCCEEDED(res))
5895 return res;
5896 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5897 if (!SUCCEEDED(res)) {
5898 IUnknown_Release((IUnknown*)*ppvObj);
5899 return res;
5901 res=IPersistStream_Load(xstm,pStm);
5902 IPersistStream_Release(xstm);
5903 /* FIXME: all refcounts ok at this point? I think they should be:
5904 * pStm : unchanged
5905 * ppvObj : 1
5906 * xstm : 0 (released)
5908 return res;
5911 /***********************************************************************
5912 * OleSaveToStream (OLE32.@)
5914 * This function saves an object with the IPersistStream interface on it
5915 * to the specified stream.
5917 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5920 CLSID clsid;
5921 HRESULT res;
5923 TRACE("(%p,%p)\n",pPStm,pStm);
5925 res=IPersistStream_GetClassID(pPStm,&clsid);
5927 if (SUCCEEDED(res)){
5929 res=WriteClassStm(pStm,&clsid);
5931 if (SUCCEEDED(res))
5933 res=IPersistStream_Save(pPStm,pStm,TRUE);
5936 TRACE("Finished Save\n");
5937 return res;
5940 /****************************************************************************
5941 * This method validate a STGM parameter that can contain the values below
5943 * STGM_DIRECT 0x00000000
5944 * STGM_TRANSACTED 0x00010000
5945 * STGM_SIMPLE 0x08000000
5947 * STGM_READ 0x00000000
5948 * STGM_WRITE 0x00000001
5949 * STGM_READWRITE 0x00000002
5951 * STGM_SHARE_DENY_NONE 0x00000040
5952 * STGM_SHARE_DENY_READ 0x00000030
5953 * STGM_SHARE_DENY_WRITE 0x00000020
5954 * STGM_SHARE_EXCLUSIVE 0x00000010
5956 * STGM_PRIORITY 0x00040000
5957 * STGM_DELETEONRELEASE 0x04000000
5959 * STGM_CREATE 0x00001000
5960 * STGM_CONVERT 0x00020000
5961 * STGM_FAILIFTHERE 0x00000000
5963 * STGM_NOSCRATCH 0x00100000
5964 * STGM_NOSNAPSHOT 0x00200000
5966 static HRESULT validateSTGM(DWORD stgm)
5968 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5969 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5970 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5972 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5973 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5974 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5976 BOOL bSTGM_SHARE_DENY_NONE =
5977 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5979 BOOL bSTGM_SHARE_DENY_READ =
5980 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5982 BOOL bSTGM_SHARE_DENY_WRITE =
5983 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5985 BOOL bSTGM_SHARE_EXCLUSIVE =
5986 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5988 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5989 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5991 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5992 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5995 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5997 if ( ! bSTGM_DIRECT )
5998 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5999 return E_FAIL;
6002 * STGM_WRITE | STGM_READWRITE | STGM_READ
6004 if ( ! bSTGM_READ )
6005 if( bSTGM_WRITE && bSTGM_READWRITE )
6006 return E_FAIL;
6009 * STGM_SHARE_DENY_NONE | others
6010 * (I assume here that DENY_READ implies DENY_WRITE)
6012 if ( bSTGM_SHARE_DENY_NONE )
6013 if ( bSTGM_SHARE_DENY_READ ||
6014 bSTGM_SHARE_DENY_WRITE ||
6015 bSTGM_SHARE_EXCLUSIVE)
6016 return E_FAIL;
6019 * STGM_CREATE | STGM_CONVERT
6020 * if both are false, STGM_FAILIFTHERE is set to TRUE
6022 if ( bSTGM_CREATE && bSTGM_CONVERT )
6023 return E_FAIL;
6026 * STGM_NOSCRATCH requires STGM_TRANSACTED
6028 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6029 return E_FAIL;
6032 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6033 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6035 if (bSTGM_NOSNAPSHOT)
6037 if ( ! ( bSTGM_TRANSACTED &&
6038 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6039 return E_FAIL;
6042 return S_OK;
6045 /****************************************************************************
6046 * GetShareModeFromSTGM
6048 * This method will return a share mode flag from a STGM value.
6049 * The STGM value is assumed valid.
6051 static DWORD GetShareModeFromSTGM(DWORD stgm)
6053 DWORD dwShareMode = 0;
6054 BOOL bSTGM_SHARE_DENY_NONE =
6055 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6057 BOOL bSTGM_SHARE_DENY_READ =
6058 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6060 BOOL bSTGM_SHARE_DENY_WRITE =
6061 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6063 BOOL bSTGM_SHARE_EXCLUSIVE =
6064 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6066 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6067 dwShareMode = 0;
6069 if (bSTGM_SHARE_DENY_NONE)
6070 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6072 if (bSTGM_SHARE_DENY_WRITE)
6073 dwShareMode = FILE_SHARE_READ;
6075 return dwShareMode;
6078 /****************************************************************************
6079 * GetAccessModeFromSTGM
6081 * This method will return an access mode flag from a STGM value.
6082 * The STGM value is assumed valid.
6084 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6086 DWORD dwDesiredAccess = GENERIC_READ;
6087 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6088 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6089 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6091 if (bSTGM_READ)
6092 dwDesiredAccess = GENERIC_READ;
6094 if (bSTGM_WRITE)
6095 dwDesiredAccess |= GENERIC_WRITE;
6097 if (bSTGM_READWRITE)
6098 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6100 return dwDesiredAccess;
6103 /****************************************************************************
6104 * GetCreationModeFromSTGM
6106 * This method will return a creation mode flag from a STGM value.
6107 * The STGM value is assumed valid.
6109 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6111 if ( stgm & STGM_CREATE)
6112 return CREATE_ALWAYS;
6113 if (stgm & STGM_CONVERT) {
6114 FIXME("STGM_CONVERT not implemented!\n");
6115 return CREATE_NEW;
6117 /* All other cases */
6118 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6119 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6120 return CREATE_NEW;
6124 /*************************************************************************
6125 * OLECONVERT_LoadOLE10 [Internal]
6127 * Loads the OLE10 STREAM to memory
6129 * PARAMS
6130 * pOleStream [I] The OLESTREAM
6131 * pData [I] Data Structure for the OLESTREAM Data
6133 * RETURNS
6134 * Success: S_OK
6135 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6136 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6138 * NOTES
6139 * This function is used by OleConvertOLESTREAMToIStorage only.
6141 * Memory allocated for pData must be freed by the caller
6143 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6145 DWORD dwSize;
6146 HRESULT hRes = S_OK;
6147 int nTryCnt=0;
6148 int max_try = 6;
6150 pData->pData = NULL;
6151 pData->pstrOleObjFileName = (CHAR *) NULL;
6153 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6155 /* Get the OleID */
6156 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6157 if(dwSize != sizeof(pData->dwOleID))
6159 hRes = CONVERT10_E_OLESTREAM_GET;
6161 else if(pData->dwOleID != OLESTREAM_ID)
6163 hRes = CONVERT10_E_OLESTREAM_FMT;
6165 else
6167 hRes = S_OK;
6168 break;
6172 if(hRes == S_OK)
6174 /* Get the TypeID...more info needed for this field */
6175 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6176 if(dwSize != sizeof(pData->dwTypeID))
6178 hRes = CONVERT10_E_OLESTREAM_GET;
6181 if(hRes == S_OK)
6183 if(pData->dwTypeID != 0)
6185 /* Get the length of the OleTypeName */
6186 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6187 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6189 hRes = CONVERT10_E_OLESTREAM_GET;
6192 if(hRes == S_OK)
6194 if(pData->dwOleTypeNameLength > 0)
6196 /* Get the OleTypeName */
6197 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6198 if(dwSize != pData->dwOleTypeNameLength)
6200 hRes = CONVERT10_E_OLESTREAM_GET;
6204 if(bStrem1)
6206 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6207 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6209 hRes = CONVERT10_E_OLESTREAM_GET;
6211 if(hRes == S_OK)
6213 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6214 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6215 pData->pstrOleObjFileName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6216 if(pData->pstrOleObjFileName)
6218 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6219 if(dwSize != pData->dwOleObjFileNameLength)
6221 hRes = CONVERT10_E_OLESTREAM_GET;
6224 else
6225 hRes = CONVERT10_E_OLESTREAM_GET;
6228 else
6230 /* Get the Width of the Metafile */
6231 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6232 if(dwSize != sizeof(pData->dwMetaFileWidth))
6234 hRes = CONVERT10_E_OLESTREAM_GET;
6236 if(hRes == S_OK)
6238 /* Get the Height of the Metafile */
6239 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6240 if(dwSize != sizeof(pData->dwMetaFileHeight))
6242 hRes = CONVERT10_E_OLESTREAM_GET;
6246 if(hRes == S_OK)
6248 /* Get the Length of the Data */
6249 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6250 if(dwSize != sizeof(pData->dwDataLength))
6252 hRes = CONVERT10_E_OLESTREAM_GET;
6256 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6258 if(!bStrem1) /* if it is a second OLE stream data */
6260 pData->dwDataLength -= 8;
6261 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6262 if(dwSize != sizeof(pData->strUnknown))
6264 hRes = CONVERT10_E_OLESTREAM_GET;
6268 if(hRes == S_OK)
6270 if(pData->dwDataLength > 0)
6272 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6274 /* Get Data (ex. IStorage, Metafile, or BMP) */
6275 if(pData->pData)
6277 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6278 if(dwSize != pData->dwDataLength)
6280 hRes = CONVERT10_E_OLESTREAM_GET;
6283 else
6285 hRes = CONVERT10_E_OLESTREAM_GET;
6291 return hRes;
6294 /*************************************************************************
6295 * OLECONVERT_SaveOLE10 [Internal]
6297 * Saves the OLE10 STREAM From memory
6299 * PARAMS
6300 * pData [I] Data Structure for the OLESTREAM Data
6301 * pOleStream [I] The OLESTREAM to save
6303 * RETURNS
6304 * Success: S_OK
6305 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6307 * NOTES
6308 * This function is used by OleConvertIStorageToOLESTREAM only.
6311 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6313 DWORD dwSize;
6314 HRESULT hRes = S_OK;
6317 /* Set the OleID */
6318 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6319 if(dwSize != sizeof(pData->dwOleID))
6321 hRes = CONVERT10_E_OLESTREAM_PUT;
6324 if(hRes == S_OK)
6326 /* Set the TypeID */
6327 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6328 if(dwSize != sizeof(pData->dwTypeID))
6330 hRes = CONVERT10_E_OLESTREAM_PUT;
6334 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6336 /* Set the Length of the OleTypeName */
6337 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6338 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6340 hRes = CONVERT10_E_OLESTREAM_PUT;
6343 if(hRes == S_OK)
6345 if(pData->dwOleTypeNameLength > 0)
6347 /* Set the OleTypeName */
6348 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6349 if(dwSize != pData->dwOleTypeNameLength)
6351 hRes = CONVERT10_E_OLESTREAM_PUT;
6356 if(hRes == S_OK)
6358 /* Set the width of the Metafile */
6359 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6360 if(dwSize != sizeof(pData->dwMetaFileWidth))
6362 hRes = CONVERT10_E_OLESTREAM_PUT;
6366 if(hRes == S_OK)
6368 /* Set the height of the Metafile */
6369 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6370 if(dwSize != sizeof(pData->dwMetaFileHeight))
6372 hRes = CONVERT10_E_OLESTREAM_PUT;
6376 if(hRes == S_OK)
6378 /* Set the length of the Data */
6379 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6380 if(dwSize != sizeof(pData->dwDataLength))
6382 hRes = CONVERT10_E_OLESTREAM_PUT;
6386 if(hRes == S_OK)
6388 if(pData->dwDataLength > 0)
6390 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6391 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6392 if(dwSize != pData->dwDataLength)
6394 hRes = CONVERT10_E_OLESTREAM_PUT;
6399 return hRes;
6402 /*************************************************************************
6403 * OLECONVERT_GetOLE20FromOLE10[Internal]
6405 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6406 * opens it, and copies the content to the dest IStorage for
6407 * OleConvertOLESTREAMToIStorage
6410 * PARAMS
6411 * pDestStorage [I] The IStorage to copy the data to
6412 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6413 * nBufferLength [I] The size of the buffer
6415 * RETURNS
6416 * Nothing
6418 * NOTES
6422 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6424 HRESULT hRes;
6425 HANDLE hFile;
6426 IStorage *pTempStorage;
6427 DWORD dwNumOfBytesWritten;
6428 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6429 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6431 /* Create a temp File */
6432 GetTempPathW(MAX_PATH, wstrTempDir);
6433 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6434 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6436 if(hFile != INVALID_HANDLE_VALUE)
6438 /* Write IStorage Data to File */
6439 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6440 CloseHandle(hFile);
6442 /* Open and copy temp storage to the Dest Storage */
6443 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6444 if(hRes == S_OK)
6446 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6447 StorageBaseImpl_Release(pTempStorage);
6449 DeleteFileW(wstrTempFile);
6454 /*************************************************************************
6455 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6457 * Saves the OLE10 STREAM From memory
6459 * PARAMS
6460 * pStorage [I] The Src IStorage to copy
6461 * pData [I] The Dest Memory to write to.
6463 * RETURNS
6464 * The size in bytes allocated for pData
6466 * NOTES
6467 * Memory allocated for pData must be freed by the caller
6469 * Used by OleConvertIStorageToOLESTREAM only.
6472 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6474 HANDLE hFile;
6475 HRESULT hRes;
6476 DWORD nDataLength = 0;
6477 IStorage *pTempStorage;
6478 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6479 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6481 *pData = NULL;
6483 /* Create temp Storage */
6484 GetTempPathW(MAX_PATH, wstrTempDir);
6485 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6486 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6488 if(hRes == S_OK)
6490 /* Copy Src Storage to the Temp Storage */
6491 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6492 StorageBaseImpl_Release(pTempStorage);
6494 /* Open Temp Storage as a file and copy to memory */
6495 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6496 if(hFile != INVALID_HANDLE_VALUE)
6498 nDataLength = GetFileSize(hFile, NULL);
6499 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6500 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6501 CloseHandle(hFile);
6503 DeleteFileW(wstrTempFile);
6505 return nDataLength;
6508 /*************************************************************************
6509 * OLECONVERT_CreateOleStream [Internal]
6511 * Creates the "\001OLE" stream in the IStorage if necessary.
6513 * PARAMS
6514 * pStorage [I] Dest storage to create the stream in
6516 * RETURNS
6517 * Nothing
6519 * NOTES
6520 * This function is used by OleConvertOLESTREAMToIStorage only.
6522 * This stream is still unknown, MS Word seems to have extra data
6523 * but since the data is stored in the OLESTREAM there should be
6524 * no need to recreate the stream. If the stream is manually
6525 * deleted it will create it with this default data.
6528 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6530 HRESULT hRes;
6531 IStream *pStream;
6532 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6533 BYTE pOleStreamHeader [] =
6535 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6536 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6537 0x00, 0x00, 0x00, 0x00
6540 /* Create stream if not present */
6541 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6542 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6544 if(hRes == S_OK)
6546 /* Write default Data */
6547 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6548 IStream_Release(pStream);
6552 /* write a string to a stream, preceded by its length */
6553 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6555 HRESULT r;
6556 LPSTR str;
6557 DWORD len = 0;
6559 if( string )
6560 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6561 r = IStream_Write( stm, &len, sizeof(len), NULL);
6562 if( FAILED( r ) )
6563 return r;
6564 if(len == 0)
6565 return r;
6566 str = CoTaskMemAlloc( len );
6567 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6568 r = IStream_Write( stm, str, len, NULL);
6569 CoTaskMemFree( str );
6570 return r;
6573 /* read a string preceded by its length from a stream */
6574 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6576 HRESULT r;
6577 DWORD len, count = 0;
6578 LPSTR str;
6579 LPWSTR wstr;
6581 r = IStream_Read( stm, &len, sizeof(len), &count );
6582 if( FAILED( r ) )
6583 return r;
6584 if( count != sizeof(len) )
6585 return E_OUTOFMEMORY;
6587 TRACE("%ld bytes\n",len);
6589 str = CoTaskMemAlloc( len );
6590 if( !str )
6591 return E_OUTOFMEMORY;
6592 count = 0;
6593 r = IStream_Read( stm, str, len, &count );
6594 if( FAILED( r ) )
6595 return r;
6596 if( count != len )
6598 CoTaskMemFree( str );
6599 return E_OUTOFMEMORY;
6602 TRACE("Read string %s\n",debugstr_an(str,len));
6604 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6605 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6606 if( wstr )
6607 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6608 CoTaskMemFree( str );
6610 *string = wstr;
6612 return r;
6616 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6617 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6619 IStream *pstm;
6620 HRESULT r = S_OK;
6621 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6623 static const BYTE unknown1[12] =
6624 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6625 0xFF, 0xFF, 0xFF, 0xFF};
6626 static const BYTE unknown2[16] =
6627 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6628 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6630 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6631 debugstr_w(lpszUserType), debugstr_w(szClipName),
6632 debugstr_w(szProgIDName));
6634 /* Create a CompObj stream if it doesn't exist */
6635 r = IStorage_CreateStream(pstg, szwStreamName,
6636 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6637 if( FAILED (r) )
6638 return r;
6640 /* Write CompObj Structure to stream */
6641 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6643 if( SUCCEEDED( r ) )
6644 r = WriteClassStm( pstm, clsid );
6646 if( SUCCEEDED( r ) )
6647 r = STREAM_WriteString( pstm, lpszUserType );
6648 if( SUCCEEDED( r ) )
6649 r = STREAM_WriteString( pstm, szClipName );
6650 if( SUCCEEDED( r ) )
6651 r = STREAM_WriteString( pstm, szProgIDName );
6652 if( SUCCEEDED( r ) )
6653 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6655 IStream_Release( pstm );
6657 return r;
6660 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6661 static HRESULT CLSIDFromUserType(LPCWSTR lpszUserType, CLSID *clsid)
6663 LONG r, i, len;
6664 ULONG count;
6665 WCHAR szKey[0x40];
6666 HKEY hkey, hkeyclsid;
6667 LPWSTR buffer = NULL;
6668 BOOL found = FALSE;
6669 static const WCHAR szclsid[] = { 'C','L','S','I','D',0 };
6671 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType));
6673 r = RegOpenKeyW( HKEY_CLASSES_ROOT, szclsid, &hkeyclsid );
6674 if( r )
6675 return E_INVALIDARG;
6677 len = lstrlenW( lpszUserType ) + 1;
6678 buffer = CoTaskMemAlloc( len * sizeof (WCHAR) );
6679 if( !buffer )
6680 goto end;
6682 for(i=0; !found; i++ )
6684 r = RegEnumKeyW( hkeyclsid, i, szKey, sizeof(szKey)/sizeof(WCHAR));
6685 if( r != ERROR_SUCCESS )
6686 break;
6687 hkey = 0;
6688 r = RegOpenKeyW( hkeyclsid, szKey, &hkey );
6689 if( r != ERROR_SUCCESS )
6690 break;
6691 count = len * sizeof (WCHAR);
6692 r = RegQueryValueW( hkey, NULL, buffer, &count );
6693 found = ( r == ERROR_SUCCESS ) &&
6694 ( count == len*sizeof(WCHAR) ) &&
6695 !lstrcmpW( buffer, lpszUserType ) ;
6696 RegCloseKey( hkey );
6699 end:
6700 if( buffer )
6701 CoTaskMemFree( buffer );
6702 RegCloseKey( hkeyclsid );
6704 if ( !found )
6705 return E_INVALIDARG;
6707 TRACE("clsid is %s\n", debugstr_w( szKey ) );
6709 r = CLSIDFromString( szKey, clsid );
6711 return r;
6715 /***********************************************************************
6716 * WriteFmtUserTypeStg (OLE32.@)
6718 HRESULT WINAPI WriteFmtUserTypeStg(
6719 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6721 HRESULT r;
6722 WCHAR szwClipName[0x40];
6723 WCHAR szCLSIDName[OLESTREAM_MAX_STR_LEN];
6724 CLSID clsid;
6725 LPWSTR wstrProgID;
6726 DWORD n;
6727 LPMALLOC allocator = NULL;
6729 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6731 r = CoGetMalloc(0, &allocator);
6732 if( FAILED( r) )
6733 return E_OUTOFMEMORY;
6735 /* get the clipboard format name */
6736 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6737 szwClipName[n]=0;
6739 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6741 /* Get the CLSID */
6742 szCLSIDName[0]=0;
6743 r = CLSIDFromUserType(lpszUserType, &clsid);
6744 if( FAILED( r ) )
6745 return r;
6747 TRACE("CLSID is %s\n",debugstr_guid(&clsid));
6749 /* get the real program ID */
6750 r = ProgIDFromCLSID( &clsid, &wstrProgID);
6751 if( FAILED( r ) )
6752 return r;
6754 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6756 /* if we have a good string, write the stream */
6757 if( wstrProgID )
6758 r = STORAGE_WriteCompObj( pstg, &clsid,
6759 lpszUserType, szwClipName, wstrProgID );
6760 else
6761 r = E_OUTOFMEMORY;
6763 IMalloc_Free( allocator, wstrProgID);
6765 return r;
6769 /******************************************************************************
6770 * ReadFmtUserTypeStg [OLE32.@]
6772 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6774 HRESULT r;
6775 IStream *stm = 0;
6776 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6777 unsigned char unknown1[12];
6778 unsigned char unknown2[16];
6779 DWORD count;
6780 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6781 CLSID clsid;
6783 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6785 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6786 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6787 if( FAILED ( r ) )
6789 ERR("Failed to open stream\n");
6790 return r;
6793 /* read the various parts of the structure */
6794 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6795 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6796 goto end;
6797 r = ReadClassStm( stm, &clsid );
6798 if( FAILED( r ) )
6799 goto end;
6801 r = STREAM_ReadString( stm, &szCLSIDName );
6802 if( FAILED( r ) )
6803 goto end;
6805 r = STREAM_ReadString( stm, &szOleTypeName );
6806 if( FAILED( r ) )
6807 goto end;
6809 r = STREAM_ReadString( stm, &szProgIDName );
6810 if( FAILED( r ) )
6811 goto end;
6813 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6814 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6815 goto end;
6817 /* ok, success... now we just need to store what we found */
6818 if( pcf )
6819 *pcf = RegisterClipboardFormatW( szOleTypeName );
6820 CoTaskMemFree( szOleTypeName );
6822 if( lplpszUserType )
6823 *lplpszUserType = szCLSIDName;
6824 CoTaskMemFree( szProgIDName );
6826 end:
6827 IStream_Release( stm );
6829 return r;
6833 /*************************************************************************
6834 * OLECONVERT_CreateCompObjStream [Internal]
6836 * Creates a "\001CompObj" is the destination IStorage if necessary.
6838 * PARAMS
6839 * pStorage [I] The dest IStorage to create the CompObj Stream
6840 * if necessary.
6841 * strOleTypeName [I] The ProgID
6843 * RETURNS
6844 * Success: S_OK
6845 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6847 * NOTES
6848 * This function is used by OleConvertOLESTREAMToIStorage only.
6850 * The stream data is stored in the OLESTREAM and there should be
6851 * no need to recreate the stream. If the stream is manually
6852 * deleted it will attempt to create it by querying the registry.
6856 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6858 IStream *pStream;
6859 HRESULT hStorageRes, hRes = S_OK;
6860 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6861 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6862 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
6864 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6865 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6867 /* Initialize the CompObj structure */
6868 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6869 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6870 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6873 /* Create a CompObj stream if it doesn't exist */
6874 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6875 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6876 if(hStorageRes == S_OK)
6878 /* copy the OleTypeName to the compobj struct */
6879 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6880 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6882 /* copy the OleTypeName to the compobj struct */
6883 /* Note: in the test made, these were Identical */
6884 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6885 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6887 /* Get the CLSID */
6888 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
6889 bufferW, OLESTREAM_MAX_STR_LEN );
6890 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
6892 if(hRes == S_OK)
6894 HKEY hKey;
6895 LONG hErr;
6896 /* Get the CLSID Default Name from the Registry */
6897 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6898 if(hErr == ERROR_SUCCESS)
6900 char strTemp[OLESTREAM_MAX_STR_LEN];
6901 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6902 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6903 if(hErr == ERROR_SUCCESS)
6905 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6907 RegCloseKey(hKey);
6911 /* Write CompObj Structure to stream */
6912 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6914 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6916 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6917 if(IStorageCompObj.dwCLSIDNameLength > 0)
6919 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6921 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6922 if(IStorageCompObj.dwOleTypeNameLength > 0)
6924 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6926 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6927 if(IStorageCompObj.dwProgIDNameLength > 0)
6929 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6931 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6932 IStream_Release(pStream);
6934 return hRes;
6938 /*************************************************************************
6939 * OLECONVERT_CreateOlePresStream[Internal]
6941 * Creates the "\002OlePres000" Stream with the Metafile data
6943 * PARAMS
6944 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6945 * dwExtentX [I] Width of the Metafile
6946 * dwExtentY [I] Height of the Metafile
6947 * pData [I] Metafile data
6948 * dwDataLength [I] Size of the Metafile data
6950 * RETURNS
6951 * Success: S_OK
6952 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6954 * NOTES
6955 * This function is used by OleConvertOLESTREAMToIStorage only.
6958 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6960 HRESULT hRes;
6961 IStream *pStream;
6962 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6963 BYTE pOlePresStreamHeader [] =
6965 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6966 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6967 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6968 0x00, 0x00, 0x00, 0x00
6971 BYTE pOlePresStreamHeaderEmpty [] =
6973 0x00, 0x00, 0x00, 0x00,
6974 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6975 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6976 0x00, 0x00, 0x00, 0x00
6979 /* Create the OlePres000 Stream */
6980 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6981 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6983 if(hRes == S_OK)
6985 DWORD nHeaderSize;
6986 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6988 memset(&OlePres, 0, sizeof(OlePres));
6989 /* Do we have any metafile data to save */
6990 if(dwDataLength > 0)
6992 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6993 nHeaderSize = sizeof(pOlePresStreamHeader);
6995 else
6997 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6998 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7000 /* Set width and height of the metafile */
7001 OlePres.dwExtentX = dwExtentX;
7002 OlePres.dwExtentY = -dwExtentY;
7004 /* Set Data and Length */
7005 if(dwDataLength > sizeof(METAFILEPICT16))
7007 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7008 OlePres.pData = &(pData[8]);
7010 /* Save OlePres000 Data to Stream */
7011 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7012 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7013 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7014 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7015 if(OlePres.dwSize > 0)
7017 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7019 IStream_Release(pStream);
7023 /*************************************************************************
7024 * OLECONVERT_CreateOle10NativeStream [Internal]
7026 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7028 * PARAMS
7029 * pStorage [I] Dest storage to create the stream in
7030 * pData [I] Ole10 Native Data (ex. bmp)
7031 * dwDataLength [I] Size of the Ole10 Native Data
7033 * RETURNS
7034 * Nothing
7036 * NOTES
7037 * This function is used by OleConvertOLESTREAMToIStorage only.
7039 * Might need to verify the data and return appropriate error message
7042 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7044 HRESULT hRes;
7045 IStream *pStream;
7046 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7048 /* Create the Ole10Native Stream */
7049 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7050 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7052 if(hRes == S_OK)
7054 /* Write info to stream */
7055 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7056 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7057 IStream_Release(pStream);
7062 /*************************************************************************
7063 * OLECONVERT_GetOLE10ProgID [Internal]
7065 * Finds the ProgID (or OleTypeID) from the IStorage
7067 * PARAMS
7068 * pStorage [I] The Src IStorage to get the ProgID
7069 * strProgID [I] the ProgID string to get
7070 * dwSize [I] the size of the string
7072 * RETURNS
7073 * Success: S_OK
7074 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7076 * NOTES
7077 * This function is used by OleConvertIStorageToOLESTREAM only.
7081 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7083 HRESULT hRes;
7084 IStream *pStream;
7085 LARGE_INTEGER iSeekPos;
7086 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7087 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7089 /* Open the CompObj Stream */
7090 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7091 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7092 if(hRes == S_OK)
7095 /*Get the OleType from the CompObj Stream */
7096 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7097 iSeekPos.u.HighPart = 0;
7099 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7100 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7101 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7102 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7103 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7104 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7105 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7107 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7108 if(*dwSize > 0)
7110 IStream_Read(pStream, strProgID, *dwSize, NULL);
7112 IStream_Release(pStream);
7114 else
7116 STATSTG stat;
7117 LPOLESTR wstrProgID;
7119 /* Get the OleType from the registry */
7120 REFCLSID clsid = &(stat.clsid);
7121 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7122 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7123 if(hRes == S_OK)
7125 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7129 return hRes;
7132 /*************************************************************************
7133 * OLECONVERT_GetOle10PresData [Internal]
7135 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7137 * PARAMS
7138 * pStorage [I] Src IStroage
7139 * pOleStream [I] Dest OleStream Mem Struct
7141 * RETURNS
7142 * Nothing
7144 * NOTES
7145 * This function is used by OleConvertIStorageToOLESTREAM only.
7147 * Memory allocated for pData must be freed by the caller
7151 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7154 HRESULT hRes;
7155 IStream *pStream;
7156 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7158 /* Initialize Default data for OLESTREAM */
7159 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7160 pOleStreamData[0].dwTypeID = 2;
7161 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7162 pOleStreamData[1].dwTypeID = 0;
7163 pOleStreamData[0].dwMetaFileWidth = 0;
7164 pOleStreamData[0].dwMetaFileHeight = 0;
7165 pOleStreamData[0].pData = NULL;
7166 pOleStreamData[1].pData = NULL;
7168 /* Open Ole10Native Stream */
7169 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7170 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7171 if(hRes == S_OK)
7174 /* Read Size and Data */
7175 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7176 if(pOleStreamData->dwDataLength > 0)
7178 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7179 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7181 IStream_Release(pStream);
7187 /*************************************************************************
7188 * OLECONVERT_GetOle20PresData[Internal]
7190 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7192 * PARAMS
7193 * pStorage [I] Src IStroage
7194 * pOleStreamData [I] Dest OleStream Mem Struct
7196 * RETURNS
7197 * Nothing
7199 * NOTES
7200 * This function is used by OleConvertIStorageToOLESTREAM only.
7202 * Memory allocated for pData must be freed by the caller
7204 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7206 HRESULT hRes;
7207 IStream *pStream;
7208 OLECONVERT_ISTORAGE_OLEPRES olePress;
7209 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7211 /* Initialize Default data for OLESTREAM */
7212 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7213 pOleStreamData[0].dwTypeID = 2;
7214 pOleStreamData[0].dwMetaFileWidth = 0;
7215 pOleStreamData[0].dwMetaFileHeight = 0;
7216 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7217 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7218 pOleStreamData[1].dwTypeID = 0;
7219 pOleStreamData[1].dwOleTypeNameLength = 0;
7220 pOleStreamData[1].strOleTypeName[0] = 0;
7221 pOleStreamData[1].dwMetaFileWidth = 0;
7222 pOleStreamData[1].dwMetaFileHeight = 0;
7223 pOleStreamData[1].pData = NULL;
7224 pOleStreamData[1].dwDataLength = 0;
7227 /* Open OlePress000 stream */
7228 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7229 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7230 if(hRes == S_OK)
7232 LARGE_INTEGER iSeekPos;
7233 METAFILEPICT16 MetaFilePict;
7234 static const char strMetafilePictName[] = "METAFILEPICT";
7236 /* Set the TypeID for a Metafile */
7237 pOleStreamData[1].dwTypeID = 5;
7239 /* Set the OleTypeName to Metafile */
7240 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7241 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7243 iSeekPos.u.HighPart = 0;
7244 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7246 /* Get Presentation Data */
7247 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7248 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7249 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7250 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7252 /*Set width and Height */
7253 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7254 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7255 if(olePress.dwSize > 0)
7257 /* Set Length */
7258 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7260 /* Set MetaFilePict struct */
7261 MetaFilePict.mm = 8;
7262 MetaFilePict.xExt = olePress.dwExtentX;
7263 MetaFilePict.yExt = olePress.dwExtentY;
7264 MetaFilePict.hMF = 0;
7266 /* Get Metafile Data */
7267 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7268 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7269 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7271 IStream_Release(pStream);
7275 /*************************************************************************
7276 * OleConvertOLESTREAMToIStorage [OLE32.@]
7278 * Read info on MSDN
7280 * TODO
7281 * DVTARGETDEVICE paramenter is not handled
7282 * Still unsure of some mem fields for OLE 10 Stream
7283 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7284 * and "\001OLE" streams
7287 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7288 LPOLESTREAM pOleStream,
7289 LPSTORAGE pstg,
7290 const DVTARGETDEVICE* ptd)
7292 int i;
7293 HRESULT hRes=S_OK;
7294 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7296 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7298 if(ptd != NULL)
7300 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7303 if(pstg == NULL || pOleStream == NULL)
7305 hRes = E_INVALIDARG;
7308 if(hRes == S_OK)
7310 /* Load the OLESTREAM to Memory */
7311 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7314 if(hRes == S_OK)
7316 /* Load the OLESTREAM to Memory (part 2)*/
7317 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7320 if(hRes == S_OK)
7323 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7325 /* Do we have the IStorage Data in the OLESTREAM */
7326 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7328 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7329 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7331 else
7333 /* It must be an original OLE 1.0 source */
7334 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7337 else
7339 /* It must be an original OLE 1.0 source */
7340 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7343 /* Create CompObj Stream if necessary */
7344 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7345 if(hRes == S_OK)
7347 /*Create the Ole Stream if necessary */
7348 OLECONVERT_CreateOleStream(pstg);
7353 /* Free allocated memory */
7354 for(i=0; i < 2; i++)
7356 if(pOleStreamData[i].pData != NULL)
7358 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7360 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7362 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7363 pOleStreamData[i].pstrOleObjFileName = NULL;
7366 return hRes;
7369 /*************************************************************************
7370 * OleConvertIStorageToOLESTREAM [OLE32.@]
7372 * Read info on MSDN
7374 * Read info on MSDN
7376 * TODO
7377 * Still unsure of some mem fields for OLE 10 Stream
7378 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7379 * and "\001OLE" streams.
7382 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7383 LPSTORAGE pstg,
7384 LPOLESTREAM pOleStream)
7386 int i;
7387 HRESULT hRes = S_OK;
7388 IStream *pStream;
7389 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7390 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7393 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7395 if(pstg == NULL || pOleStream == NULL)
7397 hRes = E_INVALIDARG;
7399 if(hRes == S_OK)
7401 /* Get the ProgID */
7402 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7403 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7405 if(hRes == S_OK)
7407 /* Was it originally Ole10 */
7408 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7409 if(hRes == S_OK)
7411 IStream_Release(pStream);
7412 /* Get Presentation Data for Ole10Native */
7413 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7415 else
7417 /* Get Presentation Data (OLE20) */
7418 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7421 /* Save OLESTREAM */
7422 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7423 if(hRes == S_OK)
7425 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7430 /* Free allocated memory */
7431 for(i=0; i < 2; i++)
7433 if(pOleStreamData[i].pData != NULL)
7435 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7439 return hRes;
7442 /***********************************************************************
7443 * GetConvertStg (OLE32.@)
7445 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7446 FIXME("unimplemented stub!\n");
7447 return E_FAIL;
7450 /******************************************************************************
7451 * StgIsStorageFile [OLE32.@]
7453 HRESULT WINAPI
7454 StgIsStorageFile(LPCOLESTR fn)
7456 HANDLE hf;
7457 BYTE magic[8];
7458 DWORD bytes_read;
7460 TRACE("(\'%s\')\n", debugstr_w(fn));
7461 hf = CreateFileW(fn, GENERIC_READ,
7462 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7463 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7465 if (hf == INVALID_HANDLE_VALUE)
7466 return STG_E_FILENOTFOUND;
7468 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7470 WARN(" unable to read file\n");
7471 CloseHandle(hf);
7472 return S_FALSE;
7475 CloseHandle(hf);
7477 if (bytes_read != 8) {
7478 WARN(" too short\n");
7479 return S_FALSE;
7482 if (!memcmp(magic,STORAGE_magic,8)) {
7483 WARN(" -> YES\n");
7484 return S_OK;
7487 WARN(" -> Invalid header.\n");
7488 return S_FALSE;