ole32: Ensure that a returned free block is valid in storage.
[wine/wine-kai.git] / dlls / ole32 / storage32.c
blobabf944ac7f07350e137042a967d8b7d21ab3d58c
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
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
32 * MSDN
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
42 #define COBJMACROS
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
46 #include "windef.h"
47 #include "winbase.h"
48 #include "winnls.h"
49 #include "winuser.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
56 #include "winreg.h"
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
65 static const char rootPropertyName[] = "Root Entry";
67 /****************************************************************************
68 * Storage32InternalImpl definitions.
70 * Definition of the implementation structure for the IStorage32 interface.
71 * This one implements the IStorage32 interface for storage that are
72 * inside another storage.
74 struct StorageInternalImpl
76 struct StorageBaseImpl base;
78 * There is no specific data for this class.
81 typedef struct StorageInternalImpl StorageInternalImpl;
83 /* Method definitions for the Storage32InternalImpl class. */
84 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
85 DWORD openFlags, ULONG rootTropertyIndex);
86 static void StorageImpl_Destroy(StorageBaseImpl* iface);
87 static void* StorageImpl_GetBigBlock(StorageImpl* This, ULONG blockIndex);
88 static void* StorageImpl_GetROBigBlock(StorageImpl* This, ULONG blockIndex);
89 static void StorageImpl_ReleaseBigBlock(StorageImpl* This, void* pBigBlock);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
92 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
93 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
94 static void StorageImpl_SaveFileHeader(StorageImpl* This);
96 static void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm);
98 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
100 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
101 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
102 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
104 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
105 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
106 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
108 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
111 /* OLESTREAM memory structure to use for Get and Put Routines */
112 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
113 typedef struct
115 DWORD dwOleID;
116 DWORD dwTypeID;
117 DWORD dwOleTypeNameLength;
118 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
119 CHAR *pstrOleObjFileName;
120 DWORD dwOleObjFileNameLength;
121 DWORD dwMetaFileWidth;
122 DWORD dwMetaFileHeight;
123 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
124 DWORD dwDataLength;
125 BYTE *pData;
126 }OLECONVERT_OLESTREAM_DATA;
128 /* CompObj Stream structure */
129 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
130 typedef struct
132 BYTE byUnknown1[12];
133 CLSID clsid;
134 DWORD dwCLSIDNameLength;
135 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
136 DWORD dwOleTypeNameLength;
137 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
138 DWORD dwProgIDNameLength;
139 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
140 BYTE byUnknown2[16];
141 }OLECONVERT_ISTORAGE_COMPOBJ;
144 /* Ole Presention Stream structure */
145 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
146 typedef struct
148 BYTE byUnknown1[28];
149 DWORD dwExtentX;
150 DWORD dwExtentY;
151 DWORD dwSize;
152 BYTE *pData;
153 }OLECONVERT_ISTORAGE_OLEPRES;
157 /***********************************************************************
158 * Forward declaration of internal functions used by the method DestroyElement
160 static HRESULT deleteStorageProperty(
161 StorageImpl *parentStorage,
162 ULONG foundPropertyIndexToDelete,
163 StgProperty propertyToDelete);
165 static HRESULT deleteStreamProperty(
166 StorageImpl *parentStorage,
167 ULONG foundPropertyIndexToDelete,
168 StgProperty propertyToDelete);
170 static HRESULT findPlaceholder(
171 StorageImpl *storage,
172 ULONG propertyIndexToStore,
173 ULONG storagePropertyIndex,
174 INT typeOfRelation);
176 static HRESULT adjustPropertyChain(
177 StorageImpl *This,
178 StgProperty propertyToDelete,
179 StgProperty parentProperty,
180 ULONG parentPropertyId,
181 INT typeOfRelation);
183 /***********************************************************************
184 * Declaration of the functions used to manipulate StgProperty
187 static ULONG getFreeProperty(
188 StorageImpl *storage);
190 static void updatePropertyChain(
191 StorageImpl *storage,
192 ULONG newPropertyIndex,
193 StgProperty newProperty);
195 static LONG propertyNameCmp(
196 const OLECHAR *newProperty,
197 const OLECHAR *currentProperty);
200 /***********************************************************************
201 * Declaration of miscellaneous functions...
203 static HRESULT validateSTGM(DWORD stgmValue);
205 static DWORD GetShareModeFromSTGM(DWORD stgm);
206 static DWORD GetAccessModeFromSTGM(DWORD stgm);
207 static DWORD GetCreationModeFromSTGM(DWORD stgm);
209 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
212 /****************************************************************************
213 * IEnumSTATSTGImpl definitions.
215 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
216 * This class allows iterating through the content of a storage and to find
217 * specific items inside it.
219 struct IEnumSTATSTGImpl
221 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
222 * since we want to cast this in an IEnumSTATSTG pointer */
224 LONG ref; /* Reference count */
225 StorageImpl* parentStorage; /* Reference to the parent storage */
226 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
229 * The current implementation of the IEnumSTATSTGImpl class uses a stack
230 * to walk the property sets to get the content of a storage. This stack
231 * is implemented by the following 3 data members
233 ULONG stackSize;
234 ULONG stackMaxSize;
235 ULONG* stackToVisit;
237 #define ENUMSTATSGT_SIZE_INCREMENT 10
241 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
242 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
243 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
244 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
245 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
246 StgProperty* buffer);
247 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
248 StgProperty *currentProperty, ULONG *propertyId);
251 /************************************************************************
252 ** Storage32BaseImpl implementatiion
255 /************************************************************************
256 * Storage32BaseImpl_QueryInterface (IUnknown)
258 * This method implements the common QueryInterface for all IStorage32
259 * implementations contained in this file.
261 * See Windows documentation for more details on IUnknown methods.
263 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
264 IStorage* iface,
265 REFIID riid,
266 void** ppvObject)
268 StorageBaseImpl *This = (StorageBaseImpl *)iface;
270 * Perform a sanity check on the parameters.
272 if ( (This==0) || (ppvObject==0) )
273 return E_INVALIDARG;
276 * Initialize the return parameter.
278 *ppvObject = 0;
281 * Compare the riid with the interface IDs implemented by this object.
283 if (IsEqualGUID(&IID_IUnknown, riid) ||
284 IsEqualGUID(&IID_IStorage, riid))
286 *ppvObject = (IStorage*)This;
288 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
290 *ppvObject = (IStorage*)&This->pssVtbl;
294 * Check that we obtained an interface.
296 if ((*ppvObject)==0)
297 return E_NOINTERFACE;
300 * Query Interface always increases the reference count by one when it is
301 * successful
303 IStorage_AddRef(iface);
305 return S_OK;
308 /************************************************************************
309 * Storage32BaseImpl_AddRef (IUnknown)
311 * This method implements the common AddRef for all IStorage32
312 * implementations contained in this file.
314 * See Windows documentation for more details on IUnknown methods.
316 static ULONG WINAPI StorageBaseImpl_AddRef(
317 IStorage* iface)
319 StorageBaseImpl *This = (StorageBaseImpl *)iface;
320 ULONG ref = InterlockedIncrement(&This->ref);
322 TRACE("(%p) AddRef to %ld\n", This, ref);
324 return ref;
327 /************************************************************************
328 * Storage32BaseImpl_Release (IUnknown)
330 * This method implements the common Release for all IStorage32
331 * implementations contained in this file.
333 * See Windows documentation for more details on IUnknown methods.
335 static ULONG WINAPI StorageBaseImpl_Release(
336 IStorage* iface)
338 StorageBaseImpl *This = (StorageBaseImpl *)iface;
340 * Decrease the reference count on this object.
342 ULONG ref = InterlockedDecrement(&This->ref);
344 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
347 * If the reference count goes down to 0, perform suicide.
349 if (ref == 0)
352 * Since we are using a system of base-classes, we want to call the
353 * destructor of the appropriate derived class. To do this, we are
354 * using virtual functions to implement the destructor.
356 This->v_destructor(This);
359 return ref;
362 /************************************************************************
363 * Storage32BaseImpl_OpenStream (IStorage)
365 * This method will open the specified stream object from the current storage.
367 * See Windows documentation for more details on IStorage methods.
369 static HRESULT WINAPI StorageBaseImpl_OpenStream(
370 IStorage* iface,
371 const OLECHAR* pwcsName, /* [string][in] */
372 void* reserved1, /* [unique][in] */
373 DWORD grfMode, /* [in] */
374 DWORD reserved2, /* [in] */
375 IStream** ppstm) /* [out] */
377 StorageBaseImpl *This = (StorageBaseImpl *)iface;
378 IEnumSTATSTGImpl* propertyEnumeration;
379 StgStreamImpl* newStream;
380 StgProperty currentProperty;
381 ULONG foundPropertyIndex;
382 HRESULT res = STG_E_UNKNOWN;
384 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
385 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
388 * Perform a sanity check on the parameters.
390 if ( (pwcsName==NULL) || (ppstm==0) )
392 res = E_INVALIDARG;
393 goto end;
397 * Initialize the out parameter
399 *ppstm = NULL;
402 * Validate the STGM flags
404 if ( FAILED( validateSTGM(grfMode) ) ||
405 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
407 res = STG_E_INVALIDFLAG;
408 goto end;
412 * As documented.
414 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
416 res = STG_E_INVALIDFUNCTION;
417 goto end;
421 * Check that we're compatible with the parent's storage mode, but
422 * only if we are not in transacted mode
424 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
425 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
427 res = STG_E_ACCESSDENIED;
428 goto end;
433 * Create a property enumeration to search the properties
435 propertyEnumeration = IEnumSTATSTGImpl_Construct(
436 This->ancestorStorage,
437 This->rootPropertySetIndex);
440 * Search the enumeration for the property with the given name
442 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
443 propertyEnumeration,
444 pwcsName,
445 &currentProperty);
448 * Delete the property enumeration since we don't need it anymore
450 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
453 * If it was found, construct the stream object and return a pointer to it.
455 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
456 (currentProperty.propertyType==PROPTYPE_STREAM) )
458 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
460 if (newStream!=0)
462 newStream->grfMode = grfMode;
463 *ppstm = (IStream*)newStream;
466 * Since we are returning a pointer to the interface, we have to
467 * nail down the reference.
469 IStream_AddRef(*ppstm);
472 * add us to the storage's list of active streams
475 StorageBaseImpl_AddStream(This,newStream);
477 res = S_OK;
478 goto end;
481 res = E_OUTOFMEMORY;
482 goto end;
485 res = STG_E_FILENOTFOUND;
487 end:
488 if (res == S_OK)
489 TRACE("<-- IStream %p\n", *ppstm);
490 TRACE("<-- %08lx\n", res);
491 return res;
494 /************************************************************************
495 * Storage32BaseImpl_OpenStorage (IStorage)
497 * This method will open a new storage object from the current storage.
499 * See Windows documentation for more details on IStorage methods.
501 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
502 IStorage* iface,
503 const OLECHAR* pwcsName, /* [string][unique][in] */
504 IStorage* pstgPriority, /* [unique][in] */
505 DWORD grfMode, /* [in] */
506 SNB snbExclude, /* [unique][in] */
507 DWORD reserved, /* [in] */
508 IStorage** ppstg) /* [out] */
510 StorageBaseImpl *This = (StorageBaseImpl *)iface;
511 StorageInternalImpl* newStorage;
512 IEnumSTATSTGImpl* propertyEnumeration;
513 StgProperty currentProperty;
514 ULONG foundPropertyIndex;
515 HRESULT res = STG_E_UNKNOWN;
517 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
518 iface, debugstr_w(pwcsName), pstgPriority,
519 grfMode, snbExclude, reserved, ppstg);
522 * Perform a sanity check on the parameters.
524 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
526 res = E_INVALIDARG;
527 goto end;
530 /* as documented */
531 if (snbExclude != NULL)
533 res = STG_E_INVALIDPARAMETER;
534 goto end;
538 * Validate the STGM flags
540 if ( FAILED( validateSTGM(grfMode) ))
542 res = STG_E_INVALIDFLAG;
543 goto end;
547 * As documented.
549 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
550 (grfMode & STGM_DELETEONRELEASE) ||
551 (grfMode & STGM_PRIORITY) )
553 res = STG_E_INVALIDFUNCTION;
554 goto end;
558 * Check that we're compatible with the parent's storage mode,
559 * but only if we are not transacted
561 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
562 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
564 res = STG_E_ACCESSDENIED;
565 goto end;
570 * Initialize the out parameter
572 *ppstg = NULL;
575 * Create a property enumeration to search the properties
577 propertyEnumeration = IEnumSTATSTGImpl_Construct(
578 This->ancestorStorage,
579 This->rootPropertySetIndex);
582 * Search the enumeration for the property with the given name
584 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
585 propertyEnumeration,
586 pwcsName,
587 &currentProperty);
590 * Delete the property enumeration since we don't need it anymore
592 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
595 * If it was found, construct the stream object and return a pointer to it.
597 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
598 (currentProperty.propertyType==PROPTYPE_STORAGE) )
601 * Construct a new Storage object
603 newStorage = StorageInternalImpl_Construct(
604 This->ancestorStorage,
605 grfMode,
606 foundPropertyIndex);
608 if (newStorage != 0)
610 *ppstg = (IStorage*)newStorage;
613 * Since we are returning a pointer to the interface,
614 * we have to nail down the reference.
616 StorageBaseImpl_AddRef(*ppstg);
618 res = S_OK;
619 goto end;
622 res = STG_E_INSUFFICIENTMEMORY;
623 goto end;
626 res = STG_E_FILENOTFOUND;
628 end:
629 TRACE("<-- %08lx\n", res);
630 return res;
633 /************************************************************************
634 * Storage32BaseImpl_EnumElements (IStorage)
636 * This method will create an enumerator object that can be used to
637 * retrieve informatino about all the properties in the storage object.
639 * See Windows documentation for more details on IStorage methods.
641 static HRESULT WINAPI StorageBaseImpl_EnumElements(
642 IStorage* iface,
643 DWORD reserved1, /* [in] */
644 void* reserved2, /* [size_is][unique][in] */
645 DWORD reserved3, /* [in] */
646 IEnumSTATSTG** ppenum) /* [out] */
648 StorageBaseImpl *This = (StorageBaseImpl *)iface;
649 IEnumSTATSTGImpl* newEnum;
651 TRACE("(%p, %ld, %p, %ld, %p)\n",
652 iface, reserved1, reserved2, reserved3, ppenum);
655 * Perform a sanity check on the parameters.
657 if ( (This==0) || (ppenum==0))
658 return E_INVALIDARG;
661 * Construct the enumerator.
663 newEnum = IEnumSTATSTGImpl_Construct(
664 This->ancestorStorage,
665 This->rootPropertySetIndex);
667 if (newEnum!=0)
669 *ppenum = (IEnumSTATSTG*)newEnum;
672 * Don't forget to nail down a reference to the new object before
673 * returning it.
675 IEnumSTATSTG_AddRef(*ppenum);
677 return S_OK;
680 return E_OUTOFMEMORY;
683 /************************************************************************
684 * Storage32BaseImpl_Stat (IStorage)
686 * This method will retrieve information about this storage object.
688 * See Windows documentation for more details on IStorage methods.
690 static HRESULT WINAPI StorageBaseImpl_Stat(
691 IStorage* iface,
692 STATSTG* pstatstg, /* [out] */
693 DWORD grfStatFlag) /* [in] */
695 StorageBaseImpl *This = (StorageBaseImpl *)iface;
696 StgProperty curProperty;
697 BOOL readSuccessful;
698 HRESULT res = STG_E_UNKNOWN;
700 TRACE("(%p, %p, %lx)\n",
701 iface, pstatstg, grfStatFlag);
704 * Perform a sanity check on the parameters.
706 if ( (This==0) || (pstatstg==0))
708 res = E_INVALIDARG;
709 goto end;
713 * Read the information from the property.
715 readSuccessful = StorageImpl_ReadProperty(
716 This->ancestorStorage,
717 This->rootPropertySetIndex,
718 &curProperty);
720 if (readSuccessful)
722 StorageUtl_CopyPropertyToSTATSTG(
723 pstatstg,
724 &curProperty,
725 grfStatFlag);
727 pstatstg->grfMode = This->openFlags;
729 res = S_OK;
730 goto end;
733 res = E_FAIL;
735 end:
736 if (res == S_OK)
738 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);
740 TRACE("<-- %08lx\n", res);
741 return res;
744 /************************************************************************
745 * Storage32BaseImpl_RenameElement (IStorage)
747 * This method will rename the specified element.
749 * See Windows documentation for more details on IStorage methods.
751 * Implementation notes: The method used to rename consists of creating a clone
752 * of the deleted StgProperty object setting it with the new name and to
753 * perform a DestroyElement of the old StgProperty.
755 static HRESULT WINAPI StorageBaseImpl_RenameElement(
756 IStorage* iface,
757 const OLECHAR* pwcsOldName, /* [in] */
758 const OLECHAR* pwcsNewName) /* [in] */
760 StorageBaseImpl *This = (StorageBaseImpl *)iface;
761 IEnumSTATSTGImpl* propertyEnumeration;
762 StgProperty currentProperty;
763 ULONG foundPropertyIndex;
765 TRACE("(%p, %s, %s)\n",
766 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
769 * Create a property enumeration to search the properties
771 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
772 This->rootPropertySetIndex);
775 * Search the enumeration for the new property name
777 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
778 pwcsNewName,
779 &currentProperty);
781 if (foundPropertyIndex != PROPERTY_NULL)
784 * There is already a property with the new name
786 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
787 return STG_E_FILEALREADYEXISTS;
790 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
793 * Search the enumeration for the old property name
795 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
796 pwcsOldName,
797 &currentProperty);
800 * Delete the property enumeration since we don't need it anymore
802 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
804 if (foundPropertyIndex != PROPERTY_NULL)
806 StgProperty renamedProperty;
807 ULONG renamedPropertyIndex;
810 * Setup a new property for the renamed property
812 renamedProperty.sizeOfNameString =
813 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
815 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
816 return STG_E_INVALIDNAME;
818 strcpyW(renamedProperty.name, pwcsNewName);
820 renamedProperty.propertyType = currentProperty.propertyType;
821 renamedProperty.startingBlock = currentProperty.startingBlock;
822 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
823 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
825 renamedProperty.previousProperty = PROPERTY_NULL;
826 renamedProperty.nextProperty = PROPERTY_NULL;
829 * Bring the dirProperty link in case it is a storage and in which
830 * case the renamed storage elements don't require to be reorganized.
832 renamedProperty.dirProperty = currentProperty.dirProperty;
834 /* call CoFileTime to get the current time
835 renamedProperty.timeStampS1
836 renamedProperty.timeStampD1
837 renamedProperty.timeStampS2
838 renamedProperty.timeStampD2
839 renamedProperty.propertyUniqueID
843 * Obtain a free property in the property chain
845 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
848 * Save the new property into the new property spot
850 StorageImpl_WriteProperty(
851 This->ancestorStorage,
852 renamedPropertyIndex,
853 &renamedProperty);
856 * Find a spot in the property chain for our newly created property.
858 updatePropertyChain(
859 (StorageImpl*)This,
860 renamedPropertyIndex,
861 renamedProperty);
864 * At this point the renamed property has been inserted in the tree,
865 * now, before Destroying the old property we must zero its dirProperty
866 * otherwise the DestroyProperty below will zap it all and we do not want
867 * this to happen.
868 * Also, we fake that the old property is a storage so the DestroyProperty
869 * will not do a SetSize(0) on the stream data.
871 * This means that we need to tweak the StgProperty if it is a stream or a
872 * non empty storage.
874 StorageImpl_ReadProperty(This->ancestorStorage,
875 foundPropertyIndex,
876 &currentProperty);
878 currentProperty.dirProperty = PROPERTY_NULL;
879 currentProperty.propertyType = PROPTYPE_STORAGE;
880 StorageImpl_WriteProperty(
881 This->ancestorStorage,
882 foundPropertyIndex,
883 &currentProperty);
886 * Invoke Destroy to get rid of the ole property and automatically redo
887 * the linking of its previous and next members...
889 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
892 else
895 * There is no property with the old name
897 return STG_E_FILENOTFOUND;
900 return S_OK;
903 /************************************************************************
904 * Storage32BaseImpl_CreateStream (IStorage)
906 * This method will create a stream object within this storage
908 * See Windows documentation for more details on IStorage methods.
910 static HRESULT WINAPI StorageBaseImpl_CreateStream(
911 IStorage* iface,
912 const OLECHAR* pwcsName, /* [string][in] */
913 DWORD grfMode, /* [in] */
914 DWORD reserved1, /* [in] */
915 DWORD reserved2, /* [in] */
916 IStream** ppstm) /* [out] */
918 StorageBaseImpl *This = (StorageBaseImpl *)iface;
919 IEnumSTATSTGImpl* propertyEnumeration;
920 StgStreamImpl* newStream;
921 StgProperty currentProperty, newStreamProperty;
922 ULONG foundPropertyIndex, newPropertyIndex;
924 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
925 iface, debugstr_w(pwcsName), grfMode,
926 reserved1, reserved2, ppstm);
929 * Validate parameters
931 if (ppstm == 0)
932 return STG_E_INVALIDPOINTER;
934 if (pwcsName == 0)
935 return STG_E_INVALIDNAME;
937 if (reserved1 || reserved2)
938 return STG_E_INVALIDPARAMETER;
941 * Validate the STGM flags
943 if ( FAILED( validateSTGM(grfMode) ))
944 return STG_E_INVALIDFLAG;
946 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
947 return STG_E_INVALIDFLAG;
950 * As documented.
952 if ((grfMode & STGM_DELETEONRELEASE) ||
953 (grfMode & STGM_TRANSACTED))
954 return STG_E_INVALIDFUNCTION;
957 * Check that we're compatible with the parent's storage mode
958 * if not in transacted mode
960 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
961 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
962 return STG_E_ACCESSDENIED;
966 * Initialize the out parameter
968 *ppstm = 0;
971 * Create a property enumeration to search the properties
973 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
974 This->rootPropertySetIndex);
976 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
977 pwcsName,
978 &currentProperty);
980 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
982 if (foundPropertyIndex != PROPERTY_NULL)
985 * An element with this name already exists
987 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
989 IStorage_DestroyElement(iface, pwcsName);
991 else
992 return STG_E_FILEALREADYEXISTS;
994 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
996 WARN("read-only storage\n");
997 return STG_E_ACCESSDENIED;
1001 * memset the empty property
1003 memset(&newStreamProperty, 0, sizeof(StgProperty));
1005 newStreamProperty.sizeOfNameString =
1006 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1008 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1009 return STG_E_INVALIDNAME;
1011 strcpyW(newStreamProperty.name, pwcsName);
1013 newStreamProperty.propertyType = PROPTYPE_STREAM;
1014 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1015 newStreamProperty.size.u.LowPart = 0;
1016 newStreamProperty.size.u.HighPart = 0;
1018 newStreamProperty.previousProperty = PROPERTY_NULL;
1019 newStreamProperty.nextProperty = PROPERTY_NULL;
1020 newStreamProperty.dirProperty = PROPERTY_NULL;
1022 /* call CoFileTime to get the current time
1023 newStreamProperty.timeStampS1
1024 newStreamProperty.timeStampD1
1025 newStreamProperty.timeStampS2
1026 newStreamProperty.timeStampD2
1029 /* newStreamProperty.propertyUniqueID */
1032 * Get a free property or create a new one
1034 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1037 * Save the new property into the new property spot
1039 StorageImpl_WriteProperty(
1040 This->ancestorStorage,
1041 newPropertyIndex,
1042 &newStreamProperty);
1045 * Find a spot in the property chain for our newly created property.
1047 updatePropertyChain(
1048 (StorageImpl*)This,
1049 newPropertyIndex,
1050 newStreamProperty);
1053 * Open the stream to return it.
1055 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1057 if (newStream != 0)
1059 *ppstm = (IStream*)newStream;
1062 * Since we are returning a pointer to the interface, we have to nail down
1063 * the reference.
1065 IStream_AddRef(*ppstm);
1067 /* add us to the storage's list of active streams
1069 StorageBaseImpl_AddStream(This,newStream);
1072 else
1074 return STG_E_INSUFFICIENTMEMORY;
1077 return S_OK;
1080 /************************************************************************
1081 * Storage32BaseImpl_SetClass (IStorage)
1083 * This method will write the specified CLSID in the property of this
1084 * storage.
1086 * See Windows documentation for more details on IStorage methods.
1088 static HRESULT WINAPI StorageBaseImpl_SetClass(
1089 IStorage* iface,
1090 REFCLSID clsid) /* [in] */
1092 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1093 HRESULT hRes = E_FAIL;
1094 StgProperty curProperty;
1095 BOOL success;
1097 TRACE("(%p, %p)\n", iface, clsid);
1099 success = StorageImpl_ReadProperty(This->ancestorStorage,
1100 This->rootPropertySetIndex,
1101 &curProperty);
1102 if (success)
1104 curProperty.propertyUniqueID = *clsid;
1106 success = StorageImpl_WriteProperty(This->ancestorStorage,
1107 This->rootPropertySetIndex,
1108 &curProperty);
1109 if (success)
1110 hRes = S_OK;
1113 return hRes;
1116 /************************************************************************
1117 ** Storage32Impl implementation
1120 /************************************************************************
1121 * Storage32Impl_CreateStorage (IStorage)
1123 * This method will create the storage object within the provided storage.
1125 * See Windows documentation for more details on IStorage methods.
1127 static HRESULT WINAPI StorageImpl_CreateStorage(
1128 IStorage* iface,
1129 const OLECHAR *pwcsName, /* [string][in] */
1130 DWORD grfMode, /* [in] */
1131 DWORD reserved1, /* [in] */
1132 DWORD reserved2, /* [in] */
1133 IStorage **ppstg) /* [out] */
1135 StorageImpl* const This=(StorageImpl*)iface;
1137 IEnumSTATSTGImpl *propertyEnumeration;
1138 StgProperty currentProperty;
1139 StgProperty newProperty;
1140 ULONG foundPropertyIndex;
1141 ULONG newPropertyIndex;
1142 HRESULT hr;
1144 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1145 iface, debugstr_w(pwcsName), grfMode,
1146 reserved1, reserved2, ppstg);
1149 * Validate parameters
1151 if (ppstg == 0)
1152 return STG_E_INVALIDPOINTER;
1154 if (pwcsName == 0)
1155 return STG_E_INVALIDNAME;
1158 * Initialize the out parameter
1160 *ppstg = NULL;
1163 * Validate the STGM flags
1165 if ( FAILED( validateSTGM(grfMode) ) ||
1166 (grfMode & STGM_DELETEONRELEASE) )
1168 WARN("bad grfMode: 0x%lx\n", grfMode);
1169 return STG_E_INVALIDFLAG;
1173 * Check that we're compatible with the parent's storage mode
1175 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1177 WARN("access denied\n");
1178 return STG_E_ACCESSDENIED;
1182 * Create a property enumeration and search the properties
1184 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1185 This->base.rootPropertySetIndex);
1187 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1188 pwcsName,
1189 &currentProperty);
1190 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1192 if (foundPropertyIndex != PROPERTY_NULL)
1195 * An element with this name already exists
1197 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1198 IStorage_DestroyElement(iface, pwcsName);
1199 else
1201 WARN("file already exists\n");
1202 return STG_E_FILEALREADYEXISTS;
1205 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1207 WARN("read-only storage\n");
1208 return STG_E_ACCESSDENIED;
1212 * memset the empty property
1214 memset(&newProperty, 0, sizeof(StgProperty));
1216 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1218 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1220 FIXME("name too long\n");
1221 return STG_E_INVALIDNAME;
1224 strcpyW(newProperty.name, pwcsName);
1226 newProperty.propertyType = PROPTYPE_STORAGE;
1227 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1228 newProperty.size.u.LowPart = 0;
1229 newProperty.size.u.HighPart = 0;
1231 newProperty.previousProperty = PROPERTY_NULL;
1232 newProperty.nextProperty = PROPERTY_NULL;
1233 newProperty.dirProperty = PROPERTY_NULL;
1235 /* call CoFileTime to get the current time
1236 newProperty.timeStampS1
1237 newProperty.timeStampD1
1238 newProperty.timeStampS2
1239 newProperty.timeStampD2
1242 /* newStorageProperty.propertyUniqueID */
1245 * Obtain a free property in the property chain
1247 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1250 * Save the new property into the new property spot
1252 StorageImpl_WriteProperty(
1253 This->base.ancestorStorage,
1254 newPropertyIndex,
1255 &newProperty);
1258 * Find a spot in the property chain for our newly created property.
1260 updatePropertyChain(
1261 This,
1262 newPropertyIndex,
1263 newProperty);
1266 * Open it to get a pointer to return.
1268 hr = IStorage_OpenStorage(
1269 iface,
1270 (const OLECHAR*)pwcsName,
1272 grfMode,
1275 ppstg);
1277 if( (hr != S_OK) || (*ppstg == NULL))
1279 return hr;
1283 return S_OK;
1287 /***************************************************************************
1289 * Internal Method
1291 * Get a free property or create a new one.
1293 static ULONG getFreeProperty(
1294 StorageImpl *storage)
1296 ULONG currentPropertyIndex = 0;
1297 ULONG newPropertyIndex = PROPERTY_NULL;
1298 BOOL readSuccessful = TRUE;
1299 StgProperty currentProperty;
1304 * Start by reading the root property
1306 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1307 currentPropertyIndex,
1308 &currentProperty);
1309 if (readSuccessful)
1311 if (currentProperty.sizeOfNameString == 0)
1314 * The property existis and is available, we found it.
1316 newPropertyIndex = currentPropertyIndex;
1319 else
1322 * We exhausted the property list, we will create more space below
1324 newPropertyIndex = currentPropertyIndex;
1326 currentPropertyIndex++;
1328 } while (newPropertyIndex == PROPERTY_NULL);
1331 * grow the property chain
1333 if (! readSuccessful)
1335 StgProperty emptyProperty;
1336 ULARGE_INTEGER newSize;
1337 ULONG propertyIndex;
1338 ULONG lastProperty = 0;
1339 ULONG blockCount = 0;
1342 * obtain the new count of property blocks
1344 blockCount = BlockChainStream_GetCount(
1345 storage->base.ancestorStorage->rootBlockChain)+1;
1348 * initialize the size used by the property stream
1350 newSize.u.HighPart = 0;
1351 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1354 * add a property block to the property chain
1356 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1359 * memset the empty property in order to initialize the unused newly
1360 * created property
1362 memset(&emptyProperty, 0, sizeof(StgProperty));
1365 * initialize them
1367 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1369 for(
1370 propertyIndex = newPropertyIndex;
1371 propertyIndex < lastProperty;
1372 propertyIndex++)
1374 StorageImpl_WriteProperty(
1375 storage->base.ancestorStorage,
1376 propertyIndex,
1377 &emptyProperty);
1381 return newPropertyIndex;
1384 /****************************************************************************
1386 * Internal Method
1388 * Case insensitive comparaison of StgProperty.name by first considering
1389 * their size.
1391 * Returns <0 when newPrpoerty < currentProperty
1392 * >0 when newPrpoerty > currentProperty
1393 * 0 when newPrpoerty == currentProperty
1395 static LONG propertyNameCmp(
1396 const OLECHAR *newProperty,
1397 const OLECHAR *currentProperty)
1399 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1401 if (diff == 0)
1404 * We compare the string themselves only when they are of the same length
1406 diff = lstrcmpiW( newProperty, currentProperty);
1409 return diff;
1412 /****************************************************************************
1414 * Internal Method
1416 * Properly link this new element in the property chain.
1418 static void updatePropertyChain(
1419 StorageImpl *storage,
1420 ULONG newPropertyIndex,
1421 StgProperty newProperty)
1423 StgProperty currentProperty;
1426 * Read the root property
1428 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1429 storage->base.rootPropertySetIndex,
1430 &currentProperty);
1432 if (currentProperty.dirProperty != PROPERTY_NULL)
1435 * The root storage contains some element, therefore, start the research
1436 * for the appropriate location.
1438 BOOL found = 0;
1439 ULONG current, next, previous, currentPropertyId;
1442 * Keep the StgProperty sequence number of the storage first property
1444 currentPropertyId = currentProperty.dirProperty;
1447 * Read
1449 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1450 currentProperty.dirProperty,
1451 &currentProperty);
1453 previous = currentProperty.previousProperty;
1454 next = currentProperty.nextProperty;
1455 current = currentPropertyId;
1457 while (found == 0)
1459 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1461 if (diff < 0)
1463 if (previous != PROPERTY_NULL)
1465 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1466 previous,
1467 &currentProperty);
1468 current = previous;
1470 else
1472 currentProperty.previousProperty = newPropertyIndex;
1473 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1474 current,
1475 &currentProperty);
1476 found = 1;
1479 else if (diff > 0)
1481 if (next != PROPERTY_NULL)
1483 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1484 next,
1485 &currentProperty);
1486 current = next;
1488 else
1490 currentProperty.nextProperty = newPropertyIndex;
1491 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1492 current,
1493 &currentProperty);
1494 found = 1;
1497 else
1500 * Trying to insert an item with the same name in the
1501 * subtree structure.
1503 assert(FALSE);
1506 previous = currentProperty.previousProperty;
1507 next = currentProperty.nextProperty;
1510 else
1513 * The root storage is empty, link the new property to its dir property
1515 currentProperty.dirProperty = newPropertyIndex;
1516 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1517 storage->base.rootPropertySetIndex,
1518 &currentProperty);
1523 /*************************************************************************
1524 * CopyTo (IStorage)
1526 static HRESULT WINAPI StorageImpl_CopyTo(
1527 IStorage* iface,
1528 DWORD ciidExclude, /* [in] */
1529 const IID* rgiidExclude, /* [size_is][unique][in] */
1530 SNB snbExclude, /* [unique][in] */
1531 IStorage* pstgDest) /* [unique][in] */
1533 IEnumSTATSTG *elements = 0;
1534 STATSTG curElement, strStat;
1535 HRESULT hr;
1536 IStorage *pstgTmp, *pstgChild;
1537 IStream *pstrTmp, *pstrChild;
1539 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1540 FIXME("Exclude option not implemented\n");
1542 TRACE("(%p, %ld, %p, %p, %p)\n",
1543 iface, ciidExclude, rgiidExclude,
1544 snbExclude, pstgDest);
1547 * Perform a sanity check
1549 if ( pstgDest == 0 )
1550 return STG_E_INVALIDPOINTER;
1553 * Enumerate the elements
1555 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1557 if ( hr != S_OK )
1558 return hr;
1561 * set the class ID
1563 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1564 IStorage_SetClass( pstgDest, &curElement.clsid );
1569 * Obtain the next element
1571 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1573 if ( hr == S_FALSE )
1575 hr = S_OK; /* done, every element has been copied */
1576 break;
1579 if (curElement.type == STGTY_STORAGE)
1582 * open child source storage
1584 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1585 STGM_READ|STGM_SHARE_EXCLUSIVE,
1586 NULL, 0, &pstgChild );
1588 if (hr != S_OK)
1589 break;
1592 * Check if destination storage is not a child of the source
1593 * storage, which will cause an infinite loop
1595 if (pstgChild == pstgDest)
1597 IEnumSTATSTG_Release(elements);
1599 return STG_E_ACCESSDENIED;
1603 * create a new storage in destination storage
1605 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1606 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1607 0, 0,
1608 &pstgTmp );
1610 * if it already exist, don't create a new one use this one
1612 if (hr == STG_E_FILEALREADYEXISTS)
1614 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1615 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1616 NULL, 0, &pstgTmp );
1619 if (hr != S_OK)
1620 break;
1624 * do the copy recursively
1626 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1627 snbExclude, pstgTmp );
1629 IStorage_Release( pstgTmp );
1630 IStorage_Release( pstgChild );
1632 else if (curElement.type == STGTY_STREAM)
1635 * create a new stream in destination storage. If the stream already
1636 * exist, it will be deleted and a new one will be created.
1638 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1639 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1640 0, 0, &pstrTmp );
1642 if (hr != S_OK)
1643 break;
1646 * open child stream storage
1648 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1649 STGM_READ|STGM_SHARE_EXCLUSIVE,
1650 0, &pstrChild );
1652 if (hr != S_OK)
1653 break;
1656 * Get the size of the source stream
1658 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1661 * Set the size of the destination stream.
1663 IStream_SetSize(pstrTmp, strStat.cbSize);
1666 * do the copy
1668 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1669 NULL, NULL );
1671 IStream_Release( pstrTmp );
1672 IStream_Release( pstrChild );
1674 else
1676 WARN("unknown element type: %ld\n", curElement.type);
1679 } while (hr == S_OK);
1682 * Clean-up
1684 IEnumSTATSTG_Release(elements);
1686 return hr;
1689 /*************************************************************************
1690 * MoveElementTo (IStorage)
1692 static HRESULT WINAPI StorageImpl_MoveElementTo(
1693 IStorage* iface,
1694 const OLECHAR *pwcsName, /* [string][in] */
1695 IStorage *pstgDest, /* [unique][in] */
1696 const OLECHAR *pwcsNewName,/* [string][in] */
1697 DWORD grfFlags) /* [in] */
1699 FIXME("(%p %s %p %s %lu): stub\n", iface,
1700 debugstr_w(pwcsName), pstgDest,
1701 debugstr_w(pwcsNewName), grfFlags);
1702 return E_NOTIMPL;
1705 /*************************************************************************
1706 * Commit (IStorage)
1708 * Ensures that any changes made to a storage object open in transacted mode
1709 * are reflected in the parent storage
1711 * NOTES
1712 * Wine doesn't implement transacted mode, which seems to be a basic
1713 * optimization, so we can ignore this stub for now.
1715 static HRESULT WINAPI StorageImpl_Commit(
1716 IStorage* iface,
1717 DWORD grfCommitFlags)/* [in] */
1719 FIXME("(%p %ld): stub\n", iface, grfCommitFlags);
1720 return S_OK;
1723 /*************************************************************************
1724 * Revert (IStorage)
1726 * Discard all changes that have been made since the last commit operation
1728 static HRESULT WINAPI StorageImpl_Revert(
1729 IStorage* iface)
1731 FIXME("(%p): stub\n", iface);
1732 return E_NOTIMPL;
1735 /*************************************************************************
1736 * DestroyElement (IStorage)
1738 * Strategy: This implementation is built this way for simplicity not for speed.
1739 * I always delete the topmost element of the enumeration and adjust
1740 * the deleted element pointer all the time. This takes longer to
1741 * do but allow to reinvoke DestroyElement whenever we encounter a
1742 * storage object. The optimisation resides in the usage of another
1743 * enumeration strategy that would give all the leaves of a storage
1744 * first. (postfix order)
1746 static HRESULT WINAPI StorageImpl_DestroyElement(
1747 IStorage* iface,
1748 const OLECHAR *pwcsName)/* [string][in] */
1750 StorageImpl* const This=(StorageImpl*)iface;
1752 IEnumSTATSTGImpl* propertyEnumeration;
1753 HRESULT hr = S_OK;
1754 BOOL res;
1755 StgProperty propertyToDelete;
1756 StgProperty parentProperty;
1757 ULONG foundPropertyIndexToDelete;
1758 ULONG typeOfRelation;
1759 ULONG parentPropertyId = 0;
1761 TRACE("(%p, %s)\n",
1762 iface, debugstr_w(pwcsName));
1765 * Perform a sanity check on the parameters.
1767 if (pwcsName==NULL)
1768 return STG_E_INVALIDPOINTER;
1771 * Create a property enumeration to search the property with the given name
1773 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1774 This->base.ancestorStorage,
1775 This->base.rootPropertySetIndex);
1777 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1778 propertyEnumeration,
1779 pwcsName,
1780 &propertyToDelete);
1782 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1784 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1786 return STG_E_FILENOTFOUND;
1790 * Find the parent property of the property to delete (the one that
1791 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1792 * the parent is This. Otherwise, the parent is one of its sibling...
1796 * First, read This's StgProperty..
1798 res = StorageImpl_ReadProperty(
1799 This->base.ancestorStorage,
1800 This->base.rootPropertySetIndex,
1801 &parentProperty);
1803 assert(res);
1806 * Second, check to see if by any chance the actual storage (This) is not
1807 * the parent of the property to delete... We never know...
1809 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1812 * Set data as it would have been done in the else part...
1814 typeOfRelation = PROPERTY_RELATION_DIR;
1815 parentPropertyId = This->base.rootPropertySetIndex;
1817 else
1820 * Create a property enumeration to search the parent properties, and
1821 * delete it once done.
1823 IEnumSTATSTGImpl* propertyEnumeration2;
1825 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1826 This->base.ancestorStorage,
1827 This->base.rootPropertySetIndex);
1829 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1830 propertyEnumeration2,
1831 foundPropertyIndexToDelete,
1832 &parentProperty,
1833 &parentPropertyId);
1835 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1838 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1840 hr = deleteStorageProperty(
1841 This,
1842 foundPropertyIndexToDelete,
1843 propertyToDelete);
1845 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1847 hr = deleteStreamProperty(
1848 This,
1849 foundPropertyIndexToDelete,
1850 propertyToDelete);
1853 if (hr!=S_OK)
1854 return hr;
1857 * Adjust the property chain
1859 hr = adjustPropertyChain(
1860 This,
1861 propertyToDelete,
1862 parentProperty,
1863 parentPropertyId,
1864 typeOfRelation);
1866 return hr;
1870 /************************************************************************
1871 * StorageImpl_Stat (IStorage)
1873 * This method will retrieve information about this storage object.
1875 * See Windows documentation for more details on IStorage methods.
1877 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1878 STATSTG* pstatstg, /* [out] */
1879 DWORD grfStatFlag) /* [in] */
1881 StorageImpl* const This = (StorageImpl*)iface;
1882 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1884 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1886 CoTaskMemFree(pstatstg->pwcsName);
1887 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1888 strcpyW(pstatstg->pwcsName, This->pwcsName);
1891 return result;
1894 /******************************************************************************
1895 * Internal stream list handlers
1898 static void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1900 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1901 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1904 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1906 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1907 list_remove(&(strm->StrmListEntry));
1910 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1912 struct list *cur, *cur2;
1913 StgStreamImpl *strm=NULL;
1915 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1916 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1917 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1918 strm->parentStorage = NULL;
1919 list_remove(cur);
1924 /*********************************************************************
1926 * Internal Method
1928 * Perform the deletion of a complete storage node
1931 static HRESULT deleteStorageProperty(
1932 StorageImpl *parentStorage,
1933 ULONG indexOfPropertyToDelete,
1934 StgProperty propertyToDelete)
1936 IEnumSTATSTG *elements = 0;
1937 IStorage *childStorage = 0;
1938 STATSTG currentElement;
1939 HRESULT hr;
1940 HRESULT destroyHr = S_OK;
1943 * Open the storage and enumerate it
1945 hr = StorageBaseImpl_OpenStorage(
1946 (IStorage*)parentStorage,
1947 propertyToDelete.name,
1949 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1952 &childStorage);
1954 if (hr != S_OK)
1956 return hr;
1960 * Enumerate the elements
1962 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1967 * Obtain the next element
1969 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1970 if (hr==S_OK)
1972 destroyHr = StorageImpl_DestroyElement(
1973 (IStorage*)childStorage,
1974 (OLECHAR*)currentElement.pwcsName);
1976 CoTaskMemFree(currentElement.pwcsName);
1980 * We need to Reset the enumeration every time because we delete elements
1981 * and the enumeration could be invalid
1983 IEnumSTATSTG_Reset(elements);
1985 } while ((hr == S_OK) && (destroyHr == S_OK));
1988 * Invalidate the property by zeroing its name member.
1990 propertyToDelete.sizeOfNameString = 0;
1992 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1993 indexOfPropertyToDelete,
1994 &propertyToDelete);
1996 IStorage_Release(childStorage);
1997 IEnumSTATSTG_Release(elements);
1999 return destroyHr;
2002 /*********************************************************************
2004 * Internal Method
2006 * Perform the deletion of a stream node
2009 static HRESULT deleteStreamProperty(
2010 StorageImpl *parentStorage,
2011 ULONG indexOfPropertyToDelete,
2012 StgProperty propertyToDelete)
2014 IStream *pis;
2015 HRESULT hr;
2016 ULARGE_INTEGER size;
2018 size.u.HighPart = 0;
2019 size.u.LowPart = 0;
2021 hr = StorageBaseImpl_OpenStream(
2022 (IStorage*)parentStorage,
2023 (OLECHAR*)propertyToDelete.name,
2024 NULL,
2025 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2027 &pis);
2029 if (hr!=S_OK)
2031 return(hr);
2035 * Zap the stream
2037 hr = IStream_SetSize(pis, size);
2039 if(hr != S_OK)
2041 return hr;
2045 * Release the stream object.
2047 IStream_Release(pis);
2050 * Invalidate the property by zeroing its name member.
2052 propertyToDelete.sizeOfNameString = 0;
2055 * Here we should re-read the property so we get the updated pointer
2056 * but since we are here to zap it, I don't do it...
2058 StorageImpl_WriteProperty(
2059 parentStorage->base.ancestorStorage,
2060 indexOfPropertyToDelete,
2061 &propertyToDelete);
2063 return S_OK;
2066 /*********************************************************************
2068 * Internal Method
2070 * Finds a placeholder for the StgProperty within the Storage
2073 static HRESULT findPlaceholder(
2074 StorageImpl *storage,
2075 ULONG propertyIndexToStore,
2076 ULONG storePropertyIndex,
2077 INT typeOfRelation)
2079 StgProperty storeProperty;
2080 HRESULT hr = S_OK;
2081 BOOL res = TRUE;
2084 * Read the storage property
2086 res = StorageImpl_ReadProperty(
2087 storage->base.ancestorStorage,
2088 storePropertyIndex,
2089 &storeProperty);
2091 if(! res)
2093 return E_FAIL;
2096 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2098 if (storeProperty.previousProperty != PROPERTY_NULL)
2100 return findPlaceholder(
2101 storage,
2102 propertyIndexToStore,
2103 storeProperty.previousProperty,
2104 typeOfRelation);
2106 else
2108 storeProperty.previousProperty = propertyIndexToStore;
2111 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2113 if (storeProperty.nextProperty != PROPERTY_NULL)
2115 return findPlaceholder(
2116 storage,
2117 propertyIndexToStore,
2118 storeProperty.nextProperty,
2119 typeOfRelation);
2121 else
2123 storeProperty.nextProperty = propertyIndexToStore;
2126 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2128 if (storeProperty.dirProperty != PROPERTY_NULL)
2130 return findPlaceholder(
2131 storage,
2132 propertyIndexToStore,
2133 storeProperty.dirProperty,
2134 typeOfRelation);
2136 else
2138 storeProperty.dirProperty = propertyIndexToStore;
2142 hr = StorageImpl_WriteProperty(
2143 storage->base.ancestorStorage,
2144 storePropertyIndex,
2145 &storeProperty);
2147 if(! hr)
2149 return E_FAIL;
2152 return S_OK;
2155 /*************************************************************************
2157 * Internal Method
2159 * This method takes the previous and the next property link of a property
2160 * to be deleted and find them a place in the Storage.
2162 static HRESULT adjustPropertyChain(
2163 StorageImpl *This,
2164 StgProperty propertyToDelete,
2165 StgProperty parentProperty,
2166 ULONG parentPropertyId,
2167 INT typeOfRelation)
2169 ULONG newLinkProperty = PROPERTY_NULL;
2170 BOOL needToFindAPlaceholder = FALSE;
2171 ULONG storeNode = PROPERTY_NULL;
2172 ULONG toStoreNode = PROPERTY_NULL;
2173 INT relationType = 0;
2174 HRESULT hr = S_OK;
2175 BOOL res = TRUE;
2177 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2179 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2182 * Set the parent previous to the property to delete previous
2184 newLinkProperty = propertyToDelete.previousProperty;
2186 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2189 * We also need to find a storage for the other link, setup variables
2190 * to do this at the end...
2192 needToFindAPlaceholder = TRUE;
2193 storeNode = propertyToDelete.previousProperty;
2194 toStoreNode = propertyToDelete.nextProperty;
2195 relationType = PROPERTY_RELATION_NEXT;
2198 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2201 * Set the parent previous to the property to delete next
2203 newLinkProperty = propertyToDelete.nextProperty;
2207 * Link it for real...
2209 parentProperty.previousProperty = newLinkProperty;
2212 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2214 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2217 * Set the parent next to the property to delete next previous
2219 newLinkProperty = propertyToDelete.previousProperty;
2221 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2224 * We also need to find a storage for the other link, setup variables
2225 * to do this at the end...
2227 needToFindAPlaceholder = TRUE;
2228 storeNode = propertyToDelete.previousProperty;
2229 toStoreNode = propertyToDelete.nextProperty;
2230 relationType = PROPERTY_RELATION_NEXT;
2233 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2236 * Set the parent next to the property to delete next
2238 newLinkProperty = propertyToDelete.nextProperty;
2242 * Link it for real...
2244 parentProperty.nextProperty = newLinkProperty;
2246 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2248 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2251 * Set the parent dir to the property to delete previous
2253 newLinkProperty = propertyToDelete.previousProperty;
2255 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2258 * We also need to find a storage for the other link, setup variables
2259 * to do this at the end...
2261 needToFindAPlaceholder = TRUE;
2262 storeNode = propertyToDelete.previousProperty;
2263 toStoreNode = propertyToDelete.nextProperty;
2264 relationType = PROPERTY_RELATION_NEXT;
2267 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2270 * Set the parent dir to the property to delete next
2272 newLinkProperty = propertyToDelete.nextProperty;
2276 * Link it for real...
2278 parentProperty.dirProperty = newLinkProperty;
2282 * Write back the parent property
2284 res = StorageImpl_WriteProperty(
2285 This->base.ancestorStorage,
2286 parentPropertyId,
2287 &parentProperty);
2288 if(! res)
2290 return E_FAIL;
2294 * If a placeholder is required for the other link, then, find one and
2295 * get out of here...
2297 if (needToFindAPlaceholder)
2299 hr = findPlaceholder(
2300 This,
2301 toStoreNode,
2302 storeNode,
2303 relationType);
2306 return hr;
2310 /******************************************************************************
2311 * SetElementTimes (IStorage)
2313 static HRESULT WINAPI StorageImpl_SetElementTimes(
2314 IStorage* iface,
2315 const OLECHAR *pwcsName,/* [string][in] */
2316 const FILETIME *pctime, /* [in] */
2317 const FILETIME *patime, /* [in] */
2318 const FILETIME *pmtime) /* [in] */
2320 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2321 return S_OK;
2324 /******************************************************************************
2325 * SetStateBits (IStorage)
2327 static HRESULT WINAPI StorageImpl_SetStateBits(
2328 IStorage* iface,
2329 DWORD grfStateBits,/* [in] */
2330 DWORD grfMask) /* [in] */
2332 FIXME("not implemented!\n");
2333 return E_NOTIMPL;
2337 * Virtual function table for the IStorage32Impl class.
2339 static const IStorageVtbl Storage32Impl_Vtbl =
2341 StorageBaseImpl_QueryInterface,
2342 StorageBaseImpl_AddRef,
2343 StorageBaseImpl_Release,
2344 StorageBaseImpl_CreateStream,
2345 StorageBaseImpl_OpenStream,
2346 StorageImpl_CreateStorage,
2347 StorageBaseImpl_OpenStorage,
2348 StorageImpl_CopyTo,
2349 StorageImpl_MoveElementTo,
2350 StorageImpl_Commit,
2351 StorageImpl_Revert,
2352 StorageBaseImpl_EnumElements,
2353 StorageImpl_DestroyElement,
2354 StorageBaseImpl_RenameElement,
2355 StorageImpl_SetElementTimes,
2356 StorageBaseImpl_SetClass,
2357 StorageImpl_SetStateBits,
2358 StorageImpl_Stat
2361 static HRESULT StorageImpl_Construct(
2362 StorageImpl* This,
2363 HANDLE hFile,
2364 LPCOLESTR pwcsName,
2365 ILockBytes* pLkbyt,
2366 DWORD openFlags,
2367 BOOL fileBased,
2368 BOOL fileCreate)
2370 HRESULT hr = S_OK;
2371 StgProperty currentProperty;
2372 BOOL readSuccessful;
2373 ULONG currentPropertyIndex;
2375 if ( FAILED( validateSTGM(openFlags) ))
2376 return STG_E_INVALIDFLAG;
2378 memset(This, 0, sizeof(StorageImpl));
2381 * Initialize stream list
2384 list_init(&This->base.strmHead);
2387 * Initialize the virtual function table.
2389 This->base.lpVtbl = &Storage32Impl_Vtbl;
2390 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2391 This->base.v_destructor = &StorageImpl_Destroy;
2392 This->base.openFlags = (openFlags & ~STGM_CREATE);
2395 * This is the top-level storage so initialize the ancestor pointer
2396 * to this.
2398 This->base.ancestorStorage = This;
2401 * Initialize the physical support of the storage.
2403 This->hFile = hFile;
2406 * Store copy of file path.
2408 if(pwcsName) {
2409 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2410 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2411 if (!This->pwcsName)
2412 return STG_E_INSUFFICIENTMEMORY;
2413 strcpyW(This->pwcsName, pwcsName);
2417 * Initialize the big block cache.
2419 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2420 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2421 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2422 pLkbyt,
2423 openFlags,
2424 This->bigBlockSize,
2425 fileBased);
2427 if (This->bigBlockFile == 0)
2428 return E_FAIL;
2430 if (fileCreate)
2432 ULARGE_INTEGER size;
2433 BYTE* bigBlockBuffer;
2436 * Initialize all header variables:
2437 * - The big block depot consists of one block and it is at block 0
2438 * - The properties start at block 1
2439 * - There is no small block depot
2441 memset( This->bigBlockDepotStart,
2442 BLOCK_UNUSED,
2443 sizeof(This->bigBlockDepotStart));
2445 This->bigBlockDepotCount = 1;
2446 This->bigBlockDepotStart[0] = 0;
2447 This->rootStartBlock = 1;
2448 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2449 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2450 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2451 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2452 This->extBigBlockDepotCount = 0;
2454 StorageImpl_SaveFileHeader(This);
2457 * Add one block for the big block depot and one block for the properties
2459 size.u.HighPart = 0;
2460 size.u.LowPart = This->bigBlockSize * 3;
2461 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2464 * Initialize the big block depot
2466 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2467 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2468 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2469 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2470 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2472 else
2475 * Load the header for the file.
2477 hr = StorageImpl_LoadFileHeader(This);
2479 if (FAILED(hr))
2481 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2483 return hr;
2488 * There is no block depot cached yet.
2490 This->indexBlockDepotCached = 0xFFFFFFFF;
2493 * Start searching for free blocks with block 0.
2495 This->prevFreeBlock = 0;
2498 * Create the block chain abstractions.
2500 if(!(This->rootBlockChain =
2501 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2502 return STG_E_READFAULT;
2504 if(!(This->smallBlockDepotChain =
2505 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2506 PROPERTY_NULL)))
2507 return STG_E_READFAULT;
2510 * Write the root property (memory only)
2512 if (fileCreate)
2514 StgProperty rootProp;
2516 * Initialize the property chain
2518 memset(&rootProp, 0, sizeof(rootProp));
2519 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2520 sizeof(rootProp.name)/sizeof(WCHAR) );
2521 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2522 rootProp.propertyType = PROPTYPE_ROOT;
2523 rootProp.previousProperty = PROPERTY_NULL;
2524 rootProp.nextProperty = PROPERTY_NULL;
2525 rootProp.dirProperty = PROPERTY_NULL;
2526 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2527 rootProp.size.u.HighPart = 0;
2528 rootProp.size.u.LowPart = 0;
2530 StorageImpl_WriteProperty(This, 0, &rootProp);
2534 * Find the ID of the root in the property sets.
2536 currentPropertyIndex = 0;
2540 readSuccessful = StorageImpl_ReadProperty(
2541 This,
2542 currentPropertyIndex,
2543 &currentProperty);
2545 if (readSuccessful)
2547 if ( (currentProperty.sizeOfNameString != 0 ) &&
2548 (currentProperty.propertyType == PROPTYPE_ROOT) )
2550 This->base.rootPropertySetIndex = currentPropertyIndex;
2554 currentPropertyIndex++;
2556 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2558 if (!readSuccessful)
2560 /* TODO CLEANUP */
2561 return STG_E_READFAULT;
2565 * Create the block chain abstraction for the small block root chain.
2567 if(!(This->smallBlockRootChain =
2568 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2569 return STG_E_READFAULT;
2571 return hr;
2574 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2576 StorageImpl *This = (StorageImpl*) iface;
2577 TRACE("(%p)\n", This);
2579 StorageBaseImpl_DeleteAll(&This->base);
2581 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2583 BlockChainStream_Destroy(This->smallBlockRootChain);
2584 BlockChainStream_Destroy(This->rootBlockChain);
2585 BlockChainStream_Destroy(This->smallBlockDepotChain);
2587 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2588 HeapFree(GetProcessHeap(), 0, This);
2591 /******************************************************************************
2592 * Storage32Impl_GetNextFreeBigBlock
2594 * Returns the index of the next free big block.
2595 * If the big block depot is filled, this method will enlarge it.
2598 static ULONG StorageImpl_GetNextFreeBigBlock(
2599 StorageImpl* This)
2601 ULONG depotBlockIndexPos;
2602 void *depotBuffer;
2603 ULONG depotBlockOffset;
2604 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2605 ULONG nextBlockIndex = BLOCK_SPECIAL;
2606 int depotIndex = 0;
2607 ULONG freeBlock = BLOCK_UNUSED;
2609 depotIndex = This->prevFreeBlock / blocksPerDepot;
2610 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2613 * Scan the entire big block depot until we find a block marked free
2615 while (nextBlockIndex != BLOCK_UNUSED)
2617 if (depotIndex < COUNT_BBDEPOTINHEADER)
2619 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2622 * Grow the primary depot.
2624 if (depotBlockIndexPos == BLOCK_UNUSED)
2626 depotBlockIndexPos = depotIndex*blocksPerDepot;
2629 * Add a block depot.
2631 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2632 This->bigBlockDepotCount++;
2633 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2636 * Flag it as a block depot.
2638 StorageImpl_SetNextBlockInChain(This,
2639 depotBlockIndexPos,
2640 BLOCK_SPECIAL);
2642 /* Save new header information.
2644 StorageImpl_SaveFileHeader(This);
2647 else
2649 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2651 if (depotBlockIndexPos == BLOCK_UNUSED)
2654 * Grow the extended depot.
2656 ULONG extIndex = BLOCK_UNUSED;
2657 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2658 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2660 if (extBlockOffset == 0)
2662 /* We need an extended block.
2664 extIndex = Storage32Impl_AddExtBlockDepot(This);
2665 This->extBigBlockDepotCount++;
2666 depotBlockIndexPos = extIndex + 1;
2668 else
2669 depotBlockIndexPos = depotIndex * blocksPerDepot;
2672 * Add a block depot and mark it in the extended block.
2674 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2675 This->bigBlockDepotCount++;
2676 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2678 /* Flag the block depot.
2680 StorageImpl_SetNextBlockInChain(This,
2681 depotBlockIndexPos,
2682 BLOCK_SPECIAL);
2684 /* If necessary, flag the extended depot block.
2686 if (extIndex != BLOCK_UNUSED)
2687 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2689 /* Save header information.
2691 StorageImpl_SaveFileHeader(This);
2695 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2697 if (depotBuffer != 0)
2699 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2700 ( nextBlockIndex != BLOCK_UNUSED))
2702 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2704 if (nextBlockIndex == BLOCK_UNUSED)
2706 freeBlock = (depotIndex * blocksPerDepot) +
2707 (depotBlockOffset/sizeof(ULONG));
2710 depotBlockOffset += sizeof(ULONG);
2713 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2716 depotIndex++;
2717 depotBlockOffset = 0;
2721 * make sure that the block physically exists before using it
2723 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2725 This->prevFreeBlock = freeBlock;
2727 return freeBlock;
2730 /******************************************************************************
2731 * Storage32Impl_AddBlockDepot
2733 * This will create a depot block, essentially it is a block initialized
2734 * to BLOCK_UNUSEDs.
2736 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2738 BYTE* blockBuffer;
2740 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2743 * Initialize blocks as free
2745 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2747 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2750 /******************************************************************************
2751 * Storage32Impl_GetExtDepotBlock
2753 * Returns the index of the block that corresponds to the specified depot
2754 * index. This method is only for depot indexes equal or greater than
2755 * COUNT_BBDEPOTINHEADER.
2757 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2759 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2760 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2761 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2762 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2763 ULONG blockIndex = BLOCK_UNUSED;
2764 ULONG extBlockIndex = This->extBigBlockDepotStart;
2766 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2768 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2769 return BLOCK_UNUSED;
2771 while (extBlockCount > 0)
2773 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2774 extBlockCount--;
2777 if (extBlockIndex != BLOCK_UNUSED)
2779 BYTE* depotBuffer;
2781 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2783 if (depotBuffer != 0)
2785 StorageUtl_ReadDWord(depotBuffer,
2786 extBlockOffset * sizeof(ULONG),
2787 &blockIndex);
2789 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2793 return blockIndex;
2796 /******************************************************************************
2797 * Storage32Impl_SetExtDepotBlock
2799 * Associates the specified block index to the specified depot index.
2800 * This method is only for depot indexes equal or greater than
2801 * COUNT_BBDEPOTINHEADER.
2803 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2805 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2806 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2807 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2808 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2809 ULONG extBlockIndex = This->extBigBlockDepotStart;
2811 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2813 while (extBlockCount > 0)
2815 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2816 extBlockCount--;
2819 if (extBlockIndex != BLOCK_UNUSED)
2821 BYTE* depotBuffer;
2823 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2825 if (depotBuffer != 0)
2827 StorageUtl_WriteDWord(depotBuffer,
2828 extBlockOffset * sizeof(ULONG),
2829 blockIndex);
2831 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2836 /******************************************************************************
2837 * Storage32Impl_AddExtBlockDepot
2839 * Creates an extended depot block.
2841 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2843 ULONG numExtBlocks = This->extBigBlockDepotCount;
2844 ULONG nextExtBlock = This->extBigBlockDepotStart;
2845 BYTE* depotBuffer = NULL;
2846 ULONG index = BLOCK_UNUSED;
2847 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2848 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2849 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2851 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2852 blocksPerDepotBlock;
2854 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2857 * The first extended block.
2859 This->extBigBlockDepotStart = index;
2861 else
2863 unsigned int i;
2865 * Follow the chain to the last one.
2867 for (i = 0; i < (numExtBlocks - 1); i++)
2869 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2873 * Add the new extended block to the chain.
2875 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2876 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2877 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2881 * Initialize this block.
2883 depotBuffer = StorageImpl_GetBigBlock(This, index);
2884 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2885 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2887 return index;
2890 /******************************************************************************
2891 * Storage32Impl_FreeBigBlock
2893 * This method will flag the specified block as free in the big block depot.
2895 static void StorageImpl_FreeBigBlock(
2896 StorageImpl* This,
2897 ULONG blockIndex)
2899 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2901 if (blockIndex < This->prevFreeBlock)
2902 This->prevFreeBlock = blockIndex;
2905 /************************************************************************
2906 * Storage32Impl_GetNextBlockInChain
2908 * This method will retrieve the block index of the next big block in
2909 * in the chain.
2911 * Params: This - Pointer to the Storage object.
2912 * blockIndex - Index of the block to retrieve the chain
2913 * for.
2914 * nextBlockIndex - receives the return value.
2916 * Returns: This method returns the index of the next block in the chain.
2917 * It will return the constants:
2918 * BLOCK_SPECIAL - If the block given was not part of a
2919 * chain.
2920 * BLOCK_END_OF_CHAIN - If the block given was the last in
2921 * a chain.
2922 * BLOCK_UNUSED - If the block given was not past of a chain
2923 * and is available.
2924 * BLOCK_EXTBBDEPOT - This block is part of the extended
2925 * big block depot.
2927 * See Windows documentation for more details on IStorage methods.
2929 static HRESULT StorageImpl_GetNextBlockInChain(
2930 StorageImpl* This,
2931 ULONG blockIndex,
2932 ULONG* nextBlockIndex)
2934 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2935 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2936 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2937 void* depotBuffer;
2938 ULONG depotBlockIndexPos;
2939 int index;
2941 *nextBlockIndex = BLOCK_SPECIAL;
2943 if(depotBlockCount >= This->bigBlockDepotCount)
2945 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2946 This->bigBlockDepotCount);
2947 return STG_E_READFAULT;
2951 * Cache the currently accessed depot block.
2953 if (depotBlockCount != This->indexBlockDepotCached)
2955 This->indexBlockDepotCached = depotBlockCount;
2957 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2959 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2961 else
2964 * We have to look in the extended depot.
2966 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2969 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2971 if (!depotBuffer)
2972 return STG_E_READFAULT;
2974 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2976 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2977 This->blockDepotCached[index] = *nextBlockIndex;
2979 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2982 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2984 return S_OK;
2987 /******************************************************************************
2988 * Storage32Impl_GetNextExtendedBlock
2990 * Given an extended block this method will return the next extended block.
2992 * NOTES:
2993 * The last ULONG of an extended block is the block index of the next
2994 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2995 * depot.
2997 * Return values:
2998 * - The index of the next extended block
2999 * - BLOCK_UNUSED: there is no next extended block.
3000 * - Any other return values denotes failure.
3002 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3004 ULONG nextBlockIndex = BLOCK_SPECIAL;
3005 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3006 void* depotBuffer;
3008 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3010 if (depotBuffer!=0)
3012 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3014 StorageImpl_ReleaseBigBlock(This, depotBuffer);
3017 return nextBlockIndex;
3020 /******************************************************************************
3021 * Storage32Impl_SetNextBlockInChain
3023 * This method will write the index of the specified block's next block
3024 * in the big block depot.
3026 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3027 * do the following
3029 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3030 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3031 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3034 static void StorageImpl_SetNextBlockInChain(
3035 StorageImpl* This,
3036 ULONG blockIndex,
3037 ULONG nextBlock)
3039 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3040 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3041 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3042 ULONG depotBlockIndexPos;
3043 void* depotBuffer;
3045 assert(depotBlockCount < This->bigBlockDepotCount);
3046 assert(blockIndex != nextBlock);
3048 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3050 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3052 else
3055 * We have to look in the extended depot.
3057 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3060 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
3062 if (depotBuffer!=0)
3064 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
3065 StorageImpl_ReleaseBigBlock(This, depotBuffer);
3069 * Update the cached block depot, if necessary.
3071 if (depotBlockCount == This->indexBlockDepotCached)
3073 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3077 /******************************************************************************
3078 * Storage32Impl_LoadFileHeader
3080 * This method will read in the file header, i.e. big block index -1.
3082 static HRESULT StorageImpl_LoadFileHeader(
3083 StorageImpl* This)
3085 HRESULT hr = STG_E_FILENOTFOUND;
3086 void* headerBigBlock = NULL;
3087 int index;
3089 TRACE("\n");
3091 * Get a pointer to the big block of data containing the header.
3093 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
3096 * Extract the information from the header.
3098 if (headerBigBlock!=0)
3101 * Check for the "magic number" signature and return an error if it is not
3102 * found.
3104 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3106 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3107 return STG_E_OLDFORMAT;
3110 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3112 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3113 return STG_E_INVALIDHEADER;
3116 StorageUtl_ReadWord(
3117 headerBigBlock,
3118 OFFSET_BIGBLOCKSIZEBITS,
3119 &This->bigBlockSizeBits);
3121 StorageUtl_ReadWord(
3122 headerBigBlock,
3123 OFFSET_SMALLBLOCKSIZEBITS,
3124 &This->smallBlockSizeBits);
3126 StorageUtl_ReadDWord(
3127 headerBigBlock,
3128 OFFSET_BBDEPOTCOUNT,
3129 &This->bigBlockDepotCount);
3131 StorageUtl_ReadDWord(
3132 headerBigBlock,
3133 OFFSET_ROOTSTARTBLOCK,
3134 &This->rootStartBlock);
3136 StorageUtl_ReadDWord(
3137 headerBigBlock,
3138 OFFSET_SBDEPOTSTART,
3139 &This->smallBlockDepotStart);
3141 StorageUtl_ReadDWord(
3142 headerBigBlock,
3143 OFFSET_EXTBBDEPOTSTART,
3144 &This->extBigBlockDepotStart);
3146 StorageUtl_ReadDWord(
3147 headerBigBlock,
3148 OFFSET_EXTBBDEPOTCOUNT,
3149 &This->extBigBlockDepotCount);
3151 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3153 StorageUtl_ReadDWord(
3154 headerBigBlock,
3155 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3156 &(This->bigBlockDepotStart[index]));
3160 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3162 if ((1 << 2) == 4)
3164 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3165 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3167 else
3169 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3170 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3174 * Right now, the code is making some assumptions about the size of the
3175 * blocks, just make sure they are what we're expecting.
3177 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3178 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3180 WARN("Broken OLE storage file\n");
3181 hr = STG_E_INVALIDHEADER;
3183 else
3184 hr = S_OK;
3187 * Release the block.
3189 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3192 return hr;
3195 /******************************************************************************
3196 * Storage32Impl_SaveFileHeader
3198 * This method will save to the file the header, i.e. big block -1.
3200 static void StorageImpl_SaveFileHeader(
3201 StorageImpl* This)
3203 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3204 int index;
3205 BOOL success;
3208 * Get a pointer to the big block of data containing the header.
3210 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3213 * If the block read failed, the file is probably new.
3215 if (!success)
3218 * Initialize for all unknown fields.
3220 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3223 * Initialize the magic number.
3225 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3228 * And a bunch of things we don't know what they mean
3230 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3231 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3232 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3233 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3237 * Write the information to the header.
3239 StorageUtl_WriteWord(
3240 headerBigBlock,
3241 OFFSET_BIGBLOCKSIZEBITS,
3242 This->bigBlockSizeBits);
3244 StorageUtl_WriteWord(
3245 headerBigBlock,
3246 OFFSET_SMALLBLOCKSIZEBITS,
3247 This->smallBlockSizeBits);
3249 StorageUtl_WriteDWord(
3250 headerBigBlock,
3251 OFFSET_BBDEPOTCOUNT,
3252 This->bigBlockDepotCount);
3254 StorageUtl_WriteDWord(
3255 headerBigBlock,
3256 OFFSET_ROOTSTARTBLOCK,
3257 This->rootStartBlock);
3259 StorageUtl_WriteDWord(
3260 headerBigBlock,
3261 OFFSET_SBDEPOTSTART,
3262 This->smallBlockDepotStart);
3264 StorageUtl_WriteDWord(
3265 headerBigBlock,
3266 OFFSET_SBDEPOTCOUNT,
3267 This->smallBlockDepotChain ?
3268 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3270 StorageUtl_WriteDWord(
3271 headerBigBlock,
3272 OFFSET_EXTBBDEPOTSTART,
3273 This->extBigBlockDepotStart);
3275 StorageUtl_WriteDWord(
3276 headerBigBlock,
3277 OFFSET_EXTBBDEPOTCOUNT,
3278 This->extBigBlockDepotCount);
3280 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3282 StorageUtl_WriteDWord(
3283 headerBigBlock,
3284 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3285 (This->bigBlockDepotStart[index]));
3289 * Write the big block back to the file.
3291 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3294 /******************************************************************************
3295 * Storage32Impl_ReadProperty
3297 * This method will read the specified property from the property chain.
3299 BOOL StorageImpl_ReadProperty(
3300 StorageImpl* This,
3301 ULONG index,
3302 StgProperty* buffer)
3304 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3305 ULARGE_INTEGER offsetInPropSet;
3306 BOOL readSuccessful;
3307 ULONG bytesRead;
3309 offsetInPropSet.u.HighPart = 0;
3310 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3312 readSuccessful = BlockChainStream_ReadAt(
3313 This->rootBlockChain,
3314 offsetInPropSet,
3315 PROPSET_BLOCK_SIZE,
3316 currentProperty,
3317 &bytesRead);
3319 if (readSuccessful)
3321 /* replace the name of root entry (often "Root Entry") by the file name */
3322 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3323 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3325 memset(buffer->name, 0, sizeof(buffer->name));
3326 memcpy(
3327 buffer->name,
3328 propName,
3329 PROPERTY_NAME_BUFFER_LEN );
3330 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3332 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3334 StorageUtl_ReadWord(
3335 currentProperty,
3336 OFFSET_PS_NAMELENGTH,
3337 &buffer->sizeOfNameString);
3339 StorageUtl_ReadDWord(
3340 currentProperty,
3341 OFFSET_PS_PREVIOUSPROP,
3342 &buffer->previousProperty);
3344 StorageUtl_ReadDWord(
3345 currentProperty,
3346 OFFSET_PS_NEXTPROP,
3347 &buffer->nextProperty);
3349 StorageUtl_ReadDWord(
3350 currentProperty,
3351 OFFSET_PS_DIRPROP,
3352 &buffer->dirProperty);
3354 StorageUtl_ReadGUID(
3355 currentProperty,
3356 OFFSET_PS_GUID,
3357 &buffer->propertyUniqueID);
3359 StorageUtl_ReadDWord(
3360 currentProperty,
3361 OFFSET_PS_TSS1,
3362 &buffer->timeStampS1);
3364 StorageUtl_ReadDWord(
3365 currentProperty,
3366 OFFSET_PS_TSD1,
3367 &buffer->timeStampD1);
3369 StorageUtl_ReadDWord(
3370 currentProperty,
3371 OFFSET_PS_TSS2,
3372 &buffer->timeStampS2);
3374 StorageUtl_ReadDWord(
3375 currentProperty,
3376 OFFSET_PS_TSD2,
3377 &buffer->timeStampD2);
3379 StorageUtl_ReadDWord(
3380 currentProperty,
3381 OFFSET_PS_STARTBLOCK,
3382 &buffer->startingBlock);
3384 StorageUtl_ReadDWord(
3385 currentProperty,
3386 OFFSET_PS_SIZE,
3387 &buffer->size.u.LowPart);
3389 buffer->size.u.HighPart = 0;
3392 return readSuccessful;
3395 /*********************************************************************
3396 * Write the specified property into the property chain
3398 BOOL StorageImpl_WriteProperty(
3399 StorageImpl* This,
3400 ULONG index,
3401 StgProperty* buffer)
3403 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3404 ULARGE_INTEGER offsetInPropSet;
3405 BOOL writeSuccessful;
3406 ULONG bytesWritten;
3408 offsetInPropSet.u.HighPart = 0;
3409 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3411 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3413 memcpy(
3414 currentProperty + OFFSET_PS_NAME,
3415 buffer->name,
3416 PROPERTY_NAME_BUFFER_LEN );
3418 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3420 StorageUtl_WriteWord(
3421 currentProperty,
3422 OFFSET_PS_NAMELENGTH,
3423 buffer->sizeOfNameString);
3425 StorageUtl_WriteDWord(
3426 currentProperty,
3427 OFFSET_PS_PREVIOUSPROP,
3428 buffer->previousProperty);
3430 StorageUtl_WriteDWord(
3431 currentProperty,
3432 OFFSET_PS_NEXTPROP,
3433 buffer->nextProperty);
3435 StorageUtl_WriteDWord(
3436 currentProperty,
3437 OFFSET_PS_DIRPROP,
3438 buffer->dirProperty);
3440 StorageUtl_WriteGUID(
3441 currentProperty,
3442 OFFSET_PS_GUID,
3443 &buffer->propertyUniqueID);
3445 StorageUtl_WriteDWord(
3446 currentProperty,
3447 OFFSET_PS_TSS1,
3448 buffer->timeStampS1);
3450 StorageUtl_WriteDWord(
3451 currentProperty,
3452 OFFSET_PS_TSD1,
3453 buffer->timeStampD1);
3455 StorageUtl_WriteDWord(
3456 currentProperty,
3457 OFFSET_PS_TSS2,
3458 buffer->timeStampS2);
3460 StorageUtl_WriteDWord(
3461 currentProperty,
3462 OFFSET_PS_TSD2,
3463 buffer->timeStampD2);
3465 StorageUtl_WriteDWord(
3466 currentProperty,
3467 OFFSET_PS_STARTBLOCK,
3468 buffer->startingBlock);
3470 StorageUtl_WriteDWord(
3471 currentProperty,
3472 OFFSET_PS_SIZE,
3473 buffer->size.u.LowPart);
3475 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3476 offsetInPropSet,
3477 PROPSET_BLOCK_SIZE,
3478 currentProperty,
3479 &bytesWritten);
3480 return writeSuccessful;
3483 static BOOL StorageImpl_ReadBigBlock(
3484 StorageImpl* This,
3485 ULONG blockIndex,
3486 void* buffer)
3488 void* bigBlockBuffer;
3490 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3492 if (bigBlockBuffer!=0)
3494 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3496 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3498 return TRUE;
3501 return FALSE;
3504 static BOOL StorageImpl_WriteBigBlock(
3505 StorageImpl* This,
3506 ULONG blockIndex,
3507 void* buffer)
3509 void* bigBlockBuffer;
3511 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3513 if (bigBlockBuffer!=0)
3515 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3517 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3519 return TRUE;
3522 return FALSE;
3525 static void* StorageImpl_GetROBigBlock(
3526 StorageImpl* This,
3527 ULONG blockIndex)
3529 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3532 static void* StorageImpl_GetBigBlock(
3533 StorageImpl* This,
3534 ULONG blockIndex)
3536 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3539 static void StorageImpl_ReleaseBigBlock(
3540 StorageImpl* This,
3541 void* pBigBlock)
3543 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3546 /******************************************************************************
3547 * Storage32Impl_SmallBlocksToBigBlocks
3549 * This method will convert a small block chain to a big block chain.
3550 * The small block chain will be destroyed.
3552 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3553 StorageImpl* This,
3554 SmallBlockChainStream** ppsbChain)
3556 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3557 ULARGE_INTEGER size, offset;
3558 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3559 ULONG propertyIndex;
3560 BOOL successWrite;
3561 HRESULT successRead;
3562 StgProperty chainProperty;
3563 BYTE *buffer;
3564 BlockChainStream *bbTempChain = NULL;
3565 BlockChainStream *bigBlockChain = NULL;
3568 * Create a temporary big block chain that doesn't have
3569 * an associated property. This temporary chain will be
3570 * used to copy data from small blocks to big blocks.
3572 bbTempChain = BlockChainStream_Construct(This,
3573 &bbHeadOfChain,
3574 PROPERTY_NULL);
3575 if(!bbTempChain) return NULL;
3577 * Grow the big block chain.
3579 size = SmallBlockChainStream_GetSize(*ppsbChain);
3580 BlockChainStream_SetSize(bbTempChain, size);
3583 * Copy the contents of the small block chain to the big block chain
3584 * by small block size increments.
3586 offset.u.LowPart = 0;
3587 offset.u.HighPart = 0;
3588 cbTotalRead = 0;
3589 cbTotalWritten = 0;
3591 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3594 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3595 offset,
3596 DEF_SMALL_BLOCK_SIZE,
3597 buffer,
3598 &cbRead);
3599 if (FAILED(successRead))
3600 break;
3602 if (cbRead > 0)
3604 cbTotalRead += cbRead;
3606 successWrite = BlockChainStream_WriteAt(bbTempChain,
3607 offset,
3608 cbRead,
3609 buffer,
3610 &cbWritten);
3612 if (!successWrite)
3613 break;
3615 cbTotalWritten += cbWritten;
3616 offset.u.LowPart += This->smallBlockSize;
3618 } while (cbRead > 0);
3619 HeapFree(GetProcessHeap(),0,buffer);
3621 assert(cbTotalRead == cbTotalWritten);
3624 * Destroy the small block chain.
3626 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3627 size.u.HighPart = 0;
3628 size.u.LowPart = 0;
3629 SmallBlockChainStream_SetSize(*ppsbChain, size);
3630 SmallBlockChainStream_Destroy(*ppsbChain);
3631 *ppsbChain = 0;
3634 * Change the property information. This chain is now a big block chain
3635 * and it doesn't reside in the small blocks chain anymore.
3637 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3639 chainProperty.startingBlock = bbHeadOfChain;
3641 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3644 * Destroy the temporary propertyless big block chain.
3645 * Create a new big block chain associated with this property.
3647 BlockChainStream_Destroy(bbTempChain);
3648 bigBlockChain = BlockChainStream_Construct(This,
3649 NULL,
3650 propertyIndex);
3652 return bigBlockChain;
3655 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3657 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3659 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3660 HeapFree(GetProcessHeap(), 0, This);
3663 /******************************************************************************
3665 ** Storage32InternalImpl_Commit
3667 ** The non-root storages cannot be opened in transacted mode thus this function
3668 ** does nothing.
3670 static HRESULT WINAPI StorageInternalImpl_Commit(
3671 IStorage* iface,
3672 DWORD grfCommitFlags) /* [in] */
3674 return S_OK;
3677 /******************************************************************************
3679 ** Storage32InternalImpl_Revert
3681 ** The non-root storages cannot be opened in transacted mode thus this function
3682 ** does nothing.
3684 static HRESULT WINAPI StorageInternalImpl_Revert(
3685 IStorage* iface)
3687 return S_OK;
3690 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3692 IStorage_Release((IStorage*)This->parentStorage);
3693 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3694 HeapFree(GetProcessHeap(), 0, This);
3697 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3698 IEnumSTATSTG* iface,
3699 REFIID riid,
3700 void** ppvObject)
3702 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3705 * Perform a sanity check on the parameters.
3707 if (ppvObject==0)
3708 return E_INVALIDARG;
3711 * Initialize the return parameter.
3713 *ppvObject = 0;
3716 * Compare the riid with the interface IDs implemented by this object.
3718 if (IsEqualGUID(&IID_IUnknown, riid) ||
3719 IsEqualGUID(&IID_IStorage, riid))
3721 *ppvObject = (IEnumSTATSTG*)This;
3722 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3723 return S_OK;
3726 return E_NOINTERFACE;
3729 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3730 IEnumSTATSTG* iface)
3732 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3733 return InterlockedIncrement(&This->ref);
3736 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3737 IEnumSTATSTG* iface)
3739 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3741 ULONG newRef;
3743 newRef = InterlockedDecrement(&This->ref);
3746 * If the reference count goes down to 0, perform suicide.
3748 if (newRef==0)
3750 IEnumSTATSTGImpl_Destroy(This);
3753 return newRef;
3756 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3757 IEnumSTATSTG* iface,
3758 ULONG celt,
3759 STATSTG* rgelt,
3760 ULONG* pceltFetched)
3762 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3764 StgProperty currentProperty;
3765 STATSTG* currentReturnStruct = rgelt;
3766 ULONG objectFetched = 0;
3767 ULONG currentSearchNode;
3770 * Perform a sanity check on the parameters.
3772 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3773 return E_INVALIDARG;
3776 * To avoid the special case, get another pointer to a ULONG value if
3777 * the caller didn't supply one.
3779 if (pceltFetched==0)
3780 pceltFetched = &objectFetched;
3783 * Start the iteration, we will iterate until we hit the end of the
3784 * linked list or until we hit the number of items to iterate through
3786 *pceltFetched = 0;
3789 * Start with the node at the top of the stack.
3791 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3793 while ( ( *pceltFetched < celt) &&
3794 ( currentSearchNode!=PROPERTY_NULL) )
3797 * Remove the top node from the stack
3799 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3802 * Read the property from the storage.
3804 StorageImpl_ReadProperty(This->parentStorage,
3805 currentSearchNode,
3806 &currentProperty);
3809 * Copy the information to the return buffer.
3811 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3812 &currentProperty,
3813 STATFLAG_DEFAULT);
3816 * Step to the next item in the iteration
3818 (*pceltFetched)++;
3819 currentReturnStruct++;
3822 * Push the next search node in the search stack.
3824 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3827 * continue the iteration.
3829 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3832 if (*pceltFetched == celt)
3833 return S_OK;
3835 return S_FALSE;
3839 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3840 IEnumSTATSTG* iface,
3841 ULONG celt)
3843 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3845 StgProperty currentProperty;
3846 ULONG objectFetched = 0;
3847 ULONG currentSearchNode;
3850 * Start with the node at the top of the stack.
3852 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3854 while ( (objectFetched < celt) &&
3855 (currentSearchNode!=PROPERTY_NULL) )
3858 * Remove the top node from the stack
3860 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3863 * Read the property from the storage.
3865 StorageImpl_ReadProperty(This->parentStorage,
3866 currentSearchNode,
3867 &currentProperty);
3870 * Step to the next item in the iteration
3872 objectFetched++;
3875 * Push the next search node in the search stack.
3877 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3880 * continue the iteration.
3882 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3885 if (objectFetched == celt)
3886 return S_OK;
3888 return S_FALSE;
3891 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3892 IEnumSTATSTG* iface)
3894 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3896 StgProperty rootProperty;
3897 BOOL readSuccessful;
3900 * Re-initialize the search stack to an empty stack
3902 This->stackSize = 0;
3905 * Read the root property from the storage.
3907 readSuccessful = StorageImpl_ReadProperty(
3908 This->parentStorage,
3909 This->firstPropertyNode,
3910 &rootProperty);
3912 if (readSuccessful)
3914 assert(rootProperty.sizeOfNameString!=0);
3917 * Push the search node in the search stack.
3919 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3922 return S_OK;
3925 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3926 IEnumSTATSTG* iface,
3927 IEnumSTATSTG** ppenum)
3929 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3931 IEnumSTATSTGImpl* newClone;
3934 * Perform a sanity check on the parameters.
3936 if (ppenum==0)
3937 return E_INVALIDARG;
3939 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3940 This->firstPropertyNode);
3944 * The new clone enumeration must point to the same current node as
3945 * the ole one.
3947 newClone->stackSize = This->stackSize ;
3948 newClone->stackMaxSize = This->stackMaxSize ;
3949 newClone->stackToVisit =
3950 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3952 memcpy(
3953 newClone->stackToVisit,
3954 This->stackToVisit,
3955 sizeof(ULONG) * newClone->stackSize);
3957 *ppenum = (IEnumSTATSTG*)newClone;
3960 * Don't forget to nail down a reference to the clone before
3961 * returning it.
3963 IEnumSTATSTGImpl_AddRef(*ppenum);
3965 return S_OK;
3968 static INT IEnumSTATSTGImpl_FindParentProperty(
3969 IEnumSTATSTGImpl *This,
3970 ULONG childProperty,
3971 StgProperty *currentProperty,
3972 ULONG *thisNodeId)
3974 ULONG currentSearchNode;
3975 ULONG foundNode;
3978 * To avoid the special case, get another pointer to a ULONG value if
3979 * the caller didn't supply one.
3981 if (thisNodeId==0)
3982 thisNodeId = &foundNode;
3985 * Start with the node at the top of the stack.
3987 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3990 while (currentSearchNode!=PROPERTY_NULL)
3993 * Store the current node in the returned parameters
3995 *thisNodeId = currentSearchNode;
3998 * Remove the top node from the stack
4000 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4003 * Read the property from the storage.
4005 StorageImpl_ReadProperty(
4006 This->parentStorage,
4007 currentSearchNode,
4008 currentProperty);
4010 if (currentProperty->previousProperty == childProperty)
4011 return PROPERTY_RELATION_PREVIOUS;
4013 else if (currentProperty->nextProperty == childProperty)
4014 return PROPERTY_RELATION_NEXT;
4016 else if (currentProperty->dirProperty == childProperty)
4017 return PROPERTY_RELATION_DIR;
4020 * Push the next search node in the search stack.
4022 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4025 * continue the iteration.
4027 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4030 return PROPERTY_NULL;
4033 static ULONG IEnumSTATSTGImpl_FindProperty(
4034 IEnumSTATSTGImpl* This,
4035 const OLECHAR* lpszPropName,
4036 StgProperty* currentProperty)
4038 ULONG currentSearchNode;
4041 * Start with the node at the top of the stack.
4043 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4045 while (currentSearchNode!=PROPERTY_NULL)
4048 * Remove the top node from the stack
4050 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4053 * Read the property from the storage.
4055 StorageImpl_ReadProperty(This->parentStorage,
4056 currentSearchNode,
4057 currentProperty);
4059 if ( propertyNameCmp(
4060 (const OLECHAR*)currentProperty->name,
4061 (const OLECHAR*)lpszPropName) == 0)
4062 return currentSearchNode;
4065 * Push the next search node in the search stack.
4067 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4070 * continue the iteration.
4072 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4075 return PROPERTY_NULL;
4078 static void IEnumSTATSTGImpl_PushSearchNode(
4079 IEnumSTATSTGImpl* This,
4080 ULONG nodeToPush)
4082 StgProperty rootProperty;
4083 BOOL readSuccessful;
4086 * First, make sure we're not trying to push an unexisting node.
4088 if (nodeToPush==PROPERTY_NULL)
4089 return;
4092 * First push the node to the stack
4094 if (This->stackSize == This->stackMaxSize)
4096 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4098 This->stackToVisit = HeapReAlloc(
4099 GetProcessHeap(),
4101 This->stackToVisit,
4102 sizeof(ULONG) * This->stackMaxSize);
4105 This->stackToVisit[This->stackSize] = nodeToPush;
4106 This->stackSize++;
4109 * Read the root property from the storage.
4111 readSuccessful = StorageImpl_ReadProperty(
4112 This->parentStorage,
4113 nodeToPush,
4114 &rootProperty);
4116 if (readSuccessful)
4118 assert(rootProperty.sizeOfNameString!=0);
4121 * Push the previous search node in the search stack.
4123 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4127 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4128 IEnumSTATSTGImpl* This,
4129 BOOL remove)
4131 ULONG topNode;
4133 if (This->stackSize == 0)
4134 return PROPERTY_NULL;
4136 topNode = This->stackToVisit[This->stackSize-1];
4138 if (remove)
4139 This->stackSize--;
4141 return topNode;
4145 * Virtual function table for the IEnumSTATSTGImpl class.
4147 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4149 IEnumSTATSTGImpl_QueryInterface,
4150 IEnumSTATSTGImpl_AddRef,
4151 IEnumSTATSTGImpl_Release,
4152 IEnumSTATSTGImpl_Next,
4153 IEnumSTATSTGImpl_Skip,
4154 IEnumSTATSTGImpl_Reset,
4155 IEnumSTATSTGImpl_Clone
4158 /******************************************************************************
4159 ** IEnumSTATSTGImpl implementation
4162 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4163 StorageImpl* parentStorage,
4164 ULONG firstPropertyNode)
4166 IEnumSTATSTGImpl* newEnumeration;
4168 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4170 if (newEnumeration!=0)
4173 * Set-up the virtual function table and reference count.
4175 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4176 newEnumeration->ref = 0;
4179 * We want to nail-down the reference to the storage in case the
4180 * enumeration out-lives the storage in the client application.
4182 newEnumeration->parentStorage = parentStorage;
4183 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4185 newEnumeration->firstPropertyNode = firstPropertyNode;
4188 * Initialize the search stack
4190 newEnumeration->stackSize = 0;
4191 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4192 newEnumeration->stackToVisit =
4193 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4196 * Make sure the current node of the iterator is the first one.
4198 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4201 return newEnumeration;
4205 * Virtual function table for the Storage32InternalImpl class.
4207 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4209 StorageBaseImpl_QueryInterface,
4210 StorageBaseImpl_AddRef,
4211 StorageBaseImpl_Release,
4212 StorageBaseImpl_CreateStream,
4213 StorageBaseImpl_OpenStream,
4214 StorageImpl_CreateStorage,
4215 StorageBaseImpl_OpenStorage,
4216 StorageImpl_CopyTo,
4217 StorageImpl_MoveElementTo,
4218 StorageInternalImpl_Commit,
4219 StorageInternalImpl_Revert,
4220 StorageBaseImpl_EnumElements,
4221 StorageImpl_DestroyElement,
4222 StorageBaseImpl_RenameElement,
4223 StorageImpl_SetElementTimes,
4224 StorageBaseImpl_SetClass,
4225 StorageImpl_SetStateBits,
4226 StorageBaseImpl_Stat
4229 /******************************************************************************
4230 ** Storage32InternalImpl implementation
4233 static StorageInternalImpl* StorageInternalImpl_Construct(
4234 StorageImpl* ancestorStorage,
4235 DWORD openFlags,
4236 ULONG rootPropertyIndex)
4238 StorageInternalImpl* newStorage;
4241 * Allocate space for the new storage object
4243 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4245 if (newStorage!=0)
4247 memset(newStorage, 0, sizeof(StorageInternalImpl));
4250 * Initialize the stream list
4253 list_init(&newStorage->base.strmHead);
4256 * Initialize the virtual function table.
4258 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4259 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4260 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4263 * Keep the ancestor storage pointer and nail a reference to it.
4265 newStorage->base.ancestorStorage = ancestorStorage;
4266 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4269 * Keep the index of the root property set for this storage,
4271 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4273 return newStorage;
4276 return 0;
4279 /******************************************************************************
4280 ** StorageUtl implementation
4283 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4285 WORD tmp;
4287 memcpy(&tmp, buffer+offset, sizeof(WORD));
4288 *value = le16toh(tmp);
4291 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4293 value = htole16(value);
4294 memcpy(buffer+offset, &value, sizeof(WORD));
4297 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4299 DWORD tmp;
4301 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4302 *value = le32toh(tmp);
4305 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4307 value = htole32(value);
4308 memcpy(buffer+offset, &value, sizeof(DWORD));
4311 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4312 ULARGE_INTEGER* value)
4314 #ifdef WORDS_BIGENDIAN
4315 ULARGE_INTEGER tmp;
4317 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4318 value->u.LowPart = htole32(tmp.u.HighPart);
4319 value->u.HighPart = htole32(tmp.u.LowPart);
4320 #else
4321 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4322 #endif
4325 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4326 const ULARGE_INTEGER *value)
4328 #ifdef WORDS_BIGENDIAN
4329 ULARGE_INTEGER tmp;
4331 tmp.u.LowPart = htole32(value->u.HighPart);
4332 tmp.u.HighPart = htole32(value->u.LowPart);
4333 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4334 #else
4335 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4336 #endif
4339 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4341 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4342 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4343 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4345 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4348 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4350 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4351 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4352 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4354 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4357 void StorageUtl_CopyPropertyToSTATSTG(
4358 STATSTG* destination,
4359 StgProperty* source,
4360 int statFlags)
4363 * The copy of the string occurs only when the flag is not set
4365 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4366 (source->name == NULL) ||
4367 (source->name[0] == 0) )
4369 destination->pwcsName = 0;
4371 else
4373 destination->pwcsName =
4374 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4376 strcpyW((LPWSTR)destination->pwcsName, source->name);
4379 switch (source->propertyType)
4381 case PROPTYPE_STORAGE:
4382 case PROPTYPE_ROOT:
4383 destination->type = STGTY_STORAGE;
4384 break;
4385 case PROPTYPE_STREAM:
4386 destination->type = STGTY_STREAM;
4387 break;
4388 default:
4389 destination->type = STGTY_STREAM;
4390 break;
4393 destination->cbSize = source->size;
4395 currentReturnStruct->mtime = {0}; TODO
4396 currentReturnStruct->ctime = {0};
4397 currentReturnStruct->atime = {0};
4399 destination->grfMode = 0;
4400 destination->grfLocksSupported = 0;
4401 destination->clsid = source->propertyUniqueID;
4402 destination->grfStateBits = 0;
4403 destination->reserved = 0;
4406 /******************************************************************************
4407 ** BlockChainStream implementation
4410 BlockChainStream* BlockChainStream_Construct(
4411 StorageImpl* parentStorage,
4412 ULONG* headOfStreamPlaceHolder,
4413 ULONG propertyIndex)
4415 BlockChainStream* newStream;
4416 ULONG blockIndex;
4418 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4420 newStream->parentStorage = parentStorage;
4421 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4422 newStream->ownerPropertyIndex = propertyIndex;
4423 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4424 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4425 newStream->numBlocks = 0;
4427 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4429 while (blockIndex != BLOCK_END_OF_CHAIN)
4431 newStream->numBlocks++;
4432 newStream->tailIndex = blockIndex;
4434 if(FAILED(StorageImpl_GetNextBlockInChain(
4435 parentStorage,
4436 blockIndex,
4437 &blockIndex)))
4439 HeapFree(GetProcessHeap(), 0, newStream);
4440 return NULL;
4444 return newStream;
4447 void BlockChainStream_Destroy(BlockChainStream* This)
4449 HeapFree(GetProcessHeap(), 0, This);
4452 /******************************************************************************
4453 * BlockChainStream_GetHeadOfChain
4455 * Returns the head of this stream chain.
4456 * Some special chains don't have properties, their heads are kept in
4457 * This->headOfStreamPlaceHolder.
4460 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4462 StgProperty chainProperty;
4463 BOOL readSuccessful;
4465 if (This->headOfStreamPlaceHolder != 0)
4466 return *(This->headOfStreamPlaceHolder);
4468 if (This->ownerPropertyIndex != PROPERTY_NULL)
4470 readSuccessful = StorageImpl_ReadProperty(
4471 This->parentStorage,
4472 This->ownerPropertyIndex,
4473 &chainProperty);
4475 if (readSuccessful)
4477 return chainProperty.startingBlock;
4481 return BLOCK_END_OF_CHAIN;
4484 /******************************************************************************
4485 * BlockChainStream_GetCount
4487 * Returns the number of blocks that comprises this chain.
4488 * This is not the size of the stream as the last block may not be full!
4491 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4493 ULONG blockIndex;
4494 ULONG count = 0;
4496 blockIndex = BlockChainStream_GetHeadOfChain(This);
4498 while (blockIndex != BLOCK_END_OF_CHAIN)
4500 count++;
4502 if(FAILED(StorageImpl_GetNextBlockInChain(
4503 This->parentStorage,
4504 blockIndex,
4505 &blockIndex)))
4506 return 0;
4509 return count;
4512 /******************************************************************************
4513 * BlockChainStream_ReadAt
4515 * Reads a specified number of bytes from this chain at the specified offset.
4516 * bytesRead may be NULL.
4517 * Failure will be returned if the specified number of bytes has not been read.
4519 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4520 ULARGE_INTEGER offset,
4521 ULONG size,
4522 void* buffer,
4523 ULONG* bytesRead)
4525 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4526 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4527 ULONG bytesToReadInBuffer;
4528 ULONG blockIndex;
4529 BYTE* bufferWalker;
4530 BYTE* bigBlockBuffer;
4533 * Find the first block in the stream that contains part of the buffer.
4535 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4536 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4537 (blockNoInSequence < This->lastBlockNoInSequence) )
4539 blockIndex = BlockChainStream_GetHeadOfChain(This);
4540 This->lastBlockNoInSequence = blockNoInSequence;
4542 else
4544 ULONG temp = blockNoInSequence;
4546 blockIndex = This->lastBlockNoInSequenceIndex;
4547 blockNoInSequence -= This->lastBlockNoInSequence;
4548 This->lastBlockNoInSequence = temp;
4551 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4553 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4554 return FALSE;
4555 blockNoInSequence--;
4558 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4559 return FALSE; /* We failed to find the starting block */
4561 This->lastBlockNoInSequenceIndex = blockIndex;
4564 * Start reading the buffer.
4566 *bytesRead = 0;
4567 bufferWalker = buffer;
4569 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4572 * Calculate how many bytes we can copy from this big block.
4574 bytesToReadInBuffer =
4575 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4578 * Copy those bytes to the buffer
4580 bigBlockBuffer =
4581 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4582 if (!bigBlockBuffer)
4583 return FALSE;
4585 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4587 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4590 * Step to the next big block.
4592 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4593 return FALSE;
4595 bufferWalker += bytesToReadInBuffer;
4596 size -= bytesToReadInBuffer;
4597 *bytesRead += bytesToReadInBuffer;
4598 offsetInBlock = 0; /* There is no offset on the next block */
4602 return (size == 0);
4605 /******************************************************************************
4606 * BlockChainStream_WriteAt
4608 * Writes the specified number of bytes to this chain at the specified offset.
4609 * bytesWritten may be NULL.
4610 * Will fail if not all specified number of bytes have been written.
4612 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4613 ULARGE_INTEGER offset,
4614 ULONG size,
4615 const void* buffer,
4616 ULONG* bytesWritten)
4618 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4619 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4620 ULONG bytesToWrite;
4621 ULONG blockIndex;
4622 const BYTE* bufferWalker;
4623 BYTE* bigBlockBuffer;
4626 * Find the first block in the stream that contains part of the buffer.
4628 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4629 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4630 (blockNoInSequence < This->lastBlockNoInSequence) )
4632 blockIndex = BlockChainStream_GetHeadOfChain(This);
4633 This->lastBlockNoInSequence = blockNoInSequence;
4635 else
4637 ULONG temp = blockNoInSequence;
4639 blockIndex = This->lastBlockNoInSequenceIndex;
4640 blockNoInSequence -= This->lastBlockNoInSequence;
4641 This->lastBlockNoInSequence = temp;
4644 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4646 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4647 &blockIndex)))
4648 return FALSE;
4649 blockNoInSequence--;
4652 This->lastBlockNoInSequenceIndex = blockIndex;
4655 * Here, I'm casting away the constness on the buffer variable
4656 * This is OK since we don't intend to modify that buffer.
4658 *bytesWritten = 0;
4659 bufferWalker = (const BYTE*)buffer;
4661 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4664 * Calculate how many bytes we can copy from this big block.
4666 bytesToWrite =
4667 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4670 * Copy those bytes to the buffer
4672 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4674 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4676 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4679 * Step to the next big block.
4681 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4682 &blockIndex)))
4683 return FALSE;
4684 bufferWalker += bytesToWrite;
4685 size -= bytesToWrite;
4686 *bytesWritten += bytesToWrite;
4687 offsetInBlock = 0; /* There is no offset on the next block */
4690 return (size == 0);
4693 /******************************************************************************
4694 * BlockChainStream_Shrink
4696 * Shrinks this chain in the big block depot.
4698 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4699 ULARGE_INTEGER newSize)
4701 ULONG blockIndex, extraBlock;
4702 ULONG numBlocks;
4703 ULONG count = 1;
4706 * Reset the last accessed block cache.
4708 This->lastBlockNoInSequence = 0xFFFFFFFF;
4709 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4712 * Figure out how many blocks are needed to contain the new size
4714 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4716 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4717 numBlocks++;
4719 blockIndex = BlockChainStream_GetHeadOfChain(This);
4722 * Go to the new end of chain
4724 while (count < numBlocks)
4726 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4727 &blockIndex)))
4728 return FALSE;
4729 count++;
4732 /* Get the next block before marking the new end */
4733 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4734 &extraBlock)))
4735 return FALSE;
4737 /* Mark the new end of chain */
4738 StorageImpl_SetNextBlockInChain(
4739 This->parentStorage,
4740 blockIndex,
4741 BLOCK_END_OF_CHAIN);
4743 This->tailIndex = blockIndex;
4744 This->numBlocks = numBlocks;
4747 * Mark the extra blocks as free
4749 while (extraBlock != BLOCK_END_OF_CHAIN)
4751 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4752 &blockIndex)))
4753 return FALSE;
4754 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4755 extraBlock = blockIndex;
4758 return TRUE;
4761 /******************************************************************************
4762 * BlockChainStream_Enlarge
4764 * Grows this chain in the big block depot.
4766 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4767 ULARGE_INTEGER newSize)
4769 ULONG blockIndex, currentBlock;
4770 ULONG newNumBlocks;
4771 ULONG oldNumBlocks = 0;
4773 blockIndex = BlockChainStream_GetHeadOfChain(This);
4776 * Empty chain. Create the head.
4778 if (blockIndex == BLOCK_END_OF_CHAIN)
4780 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4781 StorageImpl_SetNextBlockInChain(This->parentStorage,
4782 blockIndex,
4783 BLOCK_END_OF_CHAIN);
4785 if (This->headOfStreamPlaceHolder != 0)
4787 *(This->headOfStreamPlaceHolder) = blockIndex;
4789 else
4791 StgProperty chainProp;
4792 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4794 StorageImpl_ReadProperty(
4795 This->parentStorage,
4796 This->ownerPropertyIndex,
4797 &chainProp);
4799 chainProp.startingBlock = blockIndex;
4801 StorageImpl_WriteProperty(
4802 This->parentStorage,
4803 This->ownerPropertyIndex,
4804 &chainProp);
4807 This->tailIndex = blockIndex;
4808 This->numBlocks = 1;
4812 * Figure out how many blocks are needed to contain this stream
4814 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4816 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4817 newNumBlocks++;
4820 * Go to the current end of chain
4822 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4824 currentBlock = blockIndex;
4826 while (blockIndex != BLOCK_END_OF_CHAIN)
4828 This->numBlocks++;
4829 currentBlock = blockIndex;
4831 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4832 &blockIndex)))
4833 return FALSE;
4836 This->tailIndex = currentBlock;
4839 currentBlock = This->tailIndex;
4840 oldNumBlocks = This->numBlocks;
4843 * Add new blocks to the chain
4845 if (oldNumBlocks < newNumBlocks)
4847 while (oldNumBlocks < newNumBlocks)
4849 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4851 StorageImpl_SetNextBlockInChain(
4852 This->parentStorage,
4853 currentBlock,
4854 blockIndex);
4856 StorageImpl_SetNextBlockInChain(
4857 This->parentStorage,
4858 blockIndex,
4859 BLOCK_END_OF_CHAIN);
4861 currentBlock = blockIndex;
4862 oldNumBlocks++;
4865 This->tailIndex = blockIndex;
4866 This->numBlocks = newNumBlocks;
4869 return TRUE;
4872 /******************************************************************************
4873 * BlockChainStream_SetSize
4875 * Sets the size of this stream. The big block depot will be updated.
4876 * The file will grow if we grow the chain.
4878 * TODO: Free the actual blocks in the file when we shrink the chain.
4879 * Currently, the blocks are still in the file. So the file size
4880 * doesn't shrink even if we shrink streams.
4882 BOOL BlockChainStream_SetSize(
4883 BlockChainStream* This,
4884 ULARGE_INTEGER newSize)
4886 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4888 if (newSize.u.LowPart == size.u.LowPart)
4889 return TRUE;
4891 if (newSize.u.LowPart < size.u.LowPart)
4893 BlockChainStream_Shrink(This, newSize);
4895 else
4897 BlockChainStream_Enlarge(This, newSize);
4900 return TRUE;
4903 /******************************************************************************
4904 * BlockChainStream_GetSize
4906 * Returns the size of this chain.
4907 * Will return the block count if this chain doesn't have a property.
4909 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4911 StgProperty chainProperty;
4913 if(This->headOfStreamPlaceHolder == NULL)
4916 * This chain is a data stream read the property and return
4917 * the appropriate size
4919 StorageImpl_ReadProperty(
4920 This->parentStorage,
4921 This->ownerPropertyIndex,
4922 &chainProperty);
4924 return chainProperty.size;
4926 else
4929 * this chain is a chain that does not have a property, figure out the
4930 * size by making the product number of used blocks times the
4931 * size of them
4933 ULARGE_INTEGER result;
4934 result.u.HighPart = 0;
4936 result.u.LowPart =
4937 BlockChainStream_GetCount(This) *
4938 This->parentStorage->bigBlockSize;
4940 return result;
4944 /******************************************************************************
4945 ** SmallBlockChainStream implementation
4948 SmallBlockChainStream* SmallBlockChainStream_Construct(
4949 StorageImpl* parentStorage,
4950 ULONG propertyIndex)
4952 SmallBlockChainStream* newStream;
4954 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4956 newStream->parentStorage = parentStorage;
4957 newStream->ownerPropertyIndex = propertyIndex;
4959 return newStream;
4962 void SmallBlockChainStream_Destroy(
4963 SmallBlockChainStream* This)
4965 HeapFree(GetProcessHeap(), 0, This);
4968 /******************************************************************************
4969 * SmallBlockChainStream_GetHeadOfChain
4971 * Returns the head of this chain of small blocks.
4973 static ULONG SmallBlockChainStream_GetHeadOfChain(
4974 SmallBlockChainStream* This)
4976 StgProperty chainProperty;
4977 BOOL readSuccessful;
4979 if (This->ownerPropertyIndex)
4981 readSuccessful = StorageImpl_ReadProperty(
4982 This->parentStorage,
4983 This->ownerPropertyIndex,
4984 &chainProperty);
4986 if (readSuccessful)
4988 return chainProperty.startingBlock;
4993 return BLOCK_END_OF_CHAIN;
4996 /******************************************************************************
4997 * SmallBlockChainStream_GetNextBlockInChain
4999 * Returns the index of the next small block in this chain.
5001 * Return Values:
5002 * - BLOCK_END_OF_CHAIN: end of this chain
5003 * - BLOCK_UNUSED: small block 'blockIndex' is free
5005 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5006 SmallBlockChainStream* This,
5007 ULONG blockIndex,
5008 ULONG* nextBlockInChain)
5010 ULARGE_INTEGER offsetOfBlockInDepot;
5011 DWORD buffer;
5012 ULONG bytesRead;
5013 BOOL success;
5015 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5017 offsetOfBlockInDepot.u.HighPart = 0;
5018 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5021 * Read those bytes in the buffer from the small block file.
5023 success = BlockChainStream_ReadAt(
5024 This->parentStorage->smallBlockDepotChain,
5025 offsetOfBlockInDepot,
5026 sizeof(DWORD),
5027 &buffer,
5028 &bytesRead);
5030 if (success)
5032 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5033 return S_OK;
5036 return STG_E_READFAULT;
5039 /******************************************************************************
5040 * SmallBlockChainStream_SetNextBlockInChain
5042 * Writes the index of the next block of the specified block in the small
5043 * block depot.
5044 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5045 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5047 static void SmallBlockChainStream_SetNextBlockInChain(
5048 SmallBlockChainStream* This,
5049 ULONG blockIndex,
5050 ULONG nextBlock)
5052 ULARGE_INTEGER offsetOfBlockInDepot;
5053 DWORD buffer;
5054 ULONG bytesWritten;
5056 offsetOfBlockInDepot.u.HighPart = 0;
5057 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5059 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5062 * Read those bytes in the buffer from the small block file.
5064 BlockChainStream_WriteAt(
5065 This->parentStorage->smallBlockDepotChain,
5066 offsetOfBlockInDepot,
5067 sizeof(DWORD),
5068 &buffer,
5069 &bytesWritten);
5072 /******************************************************************************
5073 * SmallBlockChainStream_FreeBlock
5075 * Flag small block 'blockIndex' as free in the small block depot.
5077 static void SmallBlockChainStream_FreeBlock(
5078 SmallBlockChainStream* This,
5079 ULONG blockIndex)
5081 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5084 /******************************************************************************
5085 * SmallBlockChainStream_GetNextFreeBlock
5087 * Returns the index of a free small block. The small block depot will be
5088 * enlarged if necessary. The small block chain will also be enlarged if
5089 * necessary.
5091 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5092 SmallBlockChainStream* This)
5094 ULARGE_INTEGER offsetOfBlockInDepot;
5095 DWORD buffer;
5096 ULONG bytesRead;
5097 ULONG blockIndex = 0;
5098 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5099 BOOL success = TRUE;
5100 ULONG smallBlocksPerBigBlock;
5102 offsetOfBlockInDepot.u.HighPart = 0;
5105 * Scan the small block depot for a free block
5107 while (nextBlockIndex != BLOCK_UNUSED)
5109 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5111 success = BlockChainStream_ReadAt(
5112 This->parentStorage->smallBlockDepotChain,
5113 offsetOfBlockInDepot,
5114 sizeof(DWORD),
5115 &buffer,
5116 &bytesRead);
5119 * If we run out of space for the small block depot, enlarge it
5121 if (success)
5123 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5125 if (nextBlockIndex != BLOCK_UNUSED)
5126 blockIndex++;
5128 else
5130 ULONG count =
5131 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5133 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5134 ULONG nextBlock, newsbdIndex;
5135 BYTE* smallBlockDepot;
5137 nextBlock = sbdIndex;
5138 while (nextBlock != BLOCK_END_OF_CHAIN)
5140 sbdIndex = nextBlock;
5141 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5144 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5145 if (sbdIndex != BLOCK_END_OF_CHAIN)
5146 StorageImpl_SetNextBlockInChain(
5147 This->parentStorage,
5148 sbdIndex,
5149 newsbdIndex);
5151 StorageImpl_SetNextBlockInChain(
5152 This->parentStorage,
5153 newsbdIndex,
5154 BLOCK_END_OF_CHAIN);
5157 * Initialize all the small blocks to free
5159 smallBlockDepot =
5160 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5162 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5163 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5165 if (count == 0)
5168 * We have just created the small block depot.
5170 StgProperty rootProp;
5171 ULONG sbStartIndex;
5174 * Save it in the header
5176 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5177 StorageImpl_SaveFileHeader(This->parentStorage);
5180 * And allocate the first big block that will contain small blocks
5182 sbStartIndex =
5183 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5185 StorageImpl_SetNextBlockInChain(
5186 This->parentStorage,
5187 sbStartIndex,
5188 BLOCK_END_OF_CHAIN);
5190 StorageImpl_ReadProperty(
5191 This->parentStorage,
5192 This->parentStorage->base.rootPropertySetIndex,
5193 &rootProp);
5195 rootProp.startingBlock = sbStartIndex;
5196 rootProp.size.u.HighPart = 0;
5197 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5199 StorageImpl_WriteProperty(
5200 This->parentStorage,
5201 This->parentStorage->base.rootPropertySetIndex,
5202 &rootProp);
5207 smallBlocksPerBigBlock =
5208 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5211 * Verify if we have to allocate big blocks to contain small blocks
5213 if (blockIndex % smallBlocksPerBigBlock == 0)
5215 StgProperty rootProp;
5216 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5218 StorageImpl_ReadProperty(
5219 This->parentStorage,
5220 This->parentStorage->base.rootPropertySetIndex,
5221 &rootProp);
5223 if (rootProp.size.u.LowPart <
5224 (blocksRequired * This->parentStorage->bigBlockSize))
5226 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5228 BlockChainStream_SetSize(
5229 This->parentStorage->smallBlockRootChain,
5230 rootProp.size);
5232 StorageImpl_WriteProperty(
5233 This->parentStorage,
5234 This->parentStorage->base.rootPropertySetIndex,
5235 &rootProp);
5239 return blockIndex;
5242 /******************************************************************************
5243 * SmallBlockChainStream_ReadAt
5245 * Reads a specified number of bytes from this chain at the specified offset.
5246 * bytesRead may be NULL.
5247 * Failure will be returned if the specified number of bytes has not been read.
5249 HRESULT SmallBlockChainStream_ReadAt(
5250 SmallBlockChainStream* This,
5251 ULARGE_INTEGER offset,
5252 ULONG size,
5253 void* buffer,
5254 ULONG* bytesRead)
5256 HRESULT rc = S_OK;
5257 ULARGE_INTEGER offsetInBigBlockFile;
5258 ULONG blockNoInSequence =
5259 offset.u.LowPart / This->parentStorage->smallBlockSize;
5261 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5262 ULONG bytesToReadInBuffer;
5263 ULONG blockIndex;
5264 ULONG bytesReadFromBigBlockFile;
5265 BYTE* bufferWalker;
5268 * This should never happen on a small block file.
5270 assert(offset.u.HighPart==0);
5273 * Find the first block in the stream that contains part of the buffer.
5275 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5277 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5279 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5280 if(FAILED(rc))
5281 return rc;
5282 blockNoInSequence--;
5286 * Start reading the buffer.
5288 *bytesRead = 0;
5289 bufferWalker = buffer;
5291 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5294 * Calculate how many bytes we can copy from this small block.
5296 bytesToReadInBuffer =
5297 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5300 * Calculate the offset of the small block in the small block file.
5302 offsetInBigBlockFile.u.HighPart = 0;
5303 offsetInBigBlockFile.u.LowPart =
5304 blockIndex * This->parentStorage->smallBlockSize;
5306 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5309 * Read those bytes in the buffer from the small block file.
5310 * The small block has already been identified so it shouldn't fail
5311 * unless the file is corrupt.
5313 if (!BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5314 offsetInBigBlockFile,
5315 bytesToReadInBuffer,
5316 bufferWalker,
5317 &bytesReadFromBigBlockFile))
5318 return STG_E_DOCFILECORRUPT;
5320 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5323 * Step to the next big block.
5325 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5326 if(FAILED(rc))
5327 return rc;
5329 bufferWalker += bytesToReadInBuffer;
5330 size -= bytesToReadInBuffer;
5331 *bytesRead += bytesToReadInBuffer;
5332 offsetInBlock = 0; /* There is no offset on the next block */
5335 return rc;
5338 /******************************************************************************
5339 * SmallBlockChainStream_WriteAt
5341 * Writes the specified number of bytes to this chain at the specified offset.
5342 * bytesWritten may be NULL.
5343 * Will fail if not all specified number of bytes have been written.
5345 BOOL SmallBlockChainStream_WriteAt(
5346 SmallBlockChainStream* This,
5347 ULARGE_INTEGER offset,
5348 ULONG size,
5349 const void* buffer,
5350 ULONG* bytesWritten)
5352 ULARGE_INTEGER offsetInBigBlockFile;
5353 ULONG blockNoInSequence =
5354 offset.u.LowPart / This->parentStorage->smallBlockSize;
5356 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5357 ULONG bytesToWriteInBuffer;
5358 ULONG blockIndex;
5359 ULONG bytesWrittenFromBigBlockFile;
5360 const BYTE* bufferWalker;
5363 * This should never happen on a small block file.
5365 assert(offset.u.HighPart==0);
5368 * Find the first block in the stream that contains part of the buffer.
5370 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5372 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5374 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5375 return FALSE;
5376 blockNoInSequence--;
5380 * Start writing the buffer.
5382 * Here, I'm casting away the constness on the buffer variable
5383 * This is OK since we don't intend to modify that buffer.
5385 *bytesWritten = 0;
5386 bufferWalker = (const BYTE*)buffer;
5387 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5390 * Calculate how many bytes we can copy to this small block.
5392 bytesToWriteInBuffer =
5393 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5396 * Calculate the offset of the small block in the small block file.
5398 offsetInBigBlockFile.u.HighPart = 0;
5399 offsetInBigBlockFile.u.LowPart =
5400 blockIndex * This->parentStorage->smallBlockSize;
5402 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5405 * Write those bytes in the buffer to the small block file.
5407 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5408 offsetInBigBlockFile,
5409 bytesToWriteInBuffer,
5410 bufferWalker,
5411 &bytesWrittenFromBigBlockFile);
5413 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5416 * Step to the next big block.
5418 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5419 &blockIndex)))
5420 return FALSE;
5421 bufferWalker += bytesToWriteInBuffer;
5422 size -= bytesToWriteInBuffer;
5423 *bytesWritten += bytesToWriteInBuffer;
5424 offsetInBlock = 0; /* There is no offset on the next block */
5427 return (size == 0);
5430 /******************************************************************************
5431 * SmallBlockChainStream_Shrink
5433 * Shrinks this chain in the small block depot.
5435 static BOOL SmallBlockChainStream_Shrink(
5436 SmallBlockChainStream* This,
5437 ULARGE_INTEGER newSize)
5439 ULONG blockIndex, extraBlock;
5440 ULONG numBlocks;
5441 ULONG count = 0;
5443 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5445 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5446 numBlocks++;
5448 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5451 * Go to the new end of chain
5453 while (count < numBlocks)
5455 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5456 &blockIndex)))
5457 return FALSE;
5458 count++;
5462 * If the count is 0, we have a special case, the head of the chain was
5463 * just freed.
5465 if (count == 0)
5467 StgProperty chainProp;
5469 StorageImpl_ReadProperty(This->parentStorage,
5470 This->ownerPropertyIndex,
5471 &chainProp);
5473 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5475 StorageImpl_WriteProperty(This->parentStorage,
5476 This->ownerPropertyIndex,
5477 &chainProp);
5480 * We start freeing the chain at the head block.
5482 extraBlock = blockIndex;
5484 else
5486 /* Get the next block before marking the new end */
5487 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5488 &extraBlock)))
5489 return FALSE;
5491 /* Mark the new end of chain */
5492 SmallBlockChainStream_SetNextBlockInChain(
5493 This,
5494 blockIndex,
5495 BLOCK_END_OF_CHAIN);
5499 * Mark the extra blocks as free
5501 while (extraBlock != BLOCK_END_OF_CHAIN)
5503 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5504 &blockIndex)))
5505 return FALSE;
5506 SmallBlockChainStream_FreeBlock(This, extraBlock);
5507 extraBlock = blockIndex;
5510 return TRUE;
5513 /******************************************************************************
5514 * SmallBlockChainStream_Enlarge
5516 * Grows this chain in the small block depot.
5518 static BOOL SmallBlockChainStream_Enlarge(
5519 SmallBlockChainStream* This,
5520 ULARGE_INTEGER newSize)
5522 ULONG blockIndex, currentBlock;
5523 ULONG newNumBlocks;
5524 ULONG oldNumBlocks = 0;
5526 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5529 * Empty chain
5531 if (blockIndex == BLOCK_END_OF_CHAIN)
5534 StgProperty chainProp;
5536 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5537 &chainProp);
5539 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5541 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5542 &chainProp);
5544 blockIndex = chainProp.startingBlock;
5545 SmallBlockChainStream_SetNextBlockInChain(
5546 This,
5547 blockIndex,
5548 BLOCK_END_OF_CHAIN);
5551 currentBlock = blockIndex;
5554 * Figure out how many blocks are needed to contain this stream
5556 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5558 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5559 newNumBlocks++;
5562 * Go to the current end of chain
5564 while (blockIndex != BLOCK_END_OF_CHAIN)
5566 oldNumBlocks++;
5567 currentBlock = blockIndex;
5568 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5569 return FALSE;
5573 * Add new blocks to the chain
5575 while (oldNumBlocks < newNumBlocks)
5577 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5578 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5580 SmallBlockChainStream_SetNextBlockInChain(
5581 This,
5582 blockIndex,
5583 BLOCK_END_OF_CHAIN);
5585 currentBlock = blockIndex;
5586 oldNumBlocks++;
5589 return TRUE;
5592 /******************************************************************************
5593 * SmallBlockChainStream_SetSize
5595 * Sets the size of this stream.
5596 * The file will grow if we grow the chain.
5598 * TODO: Free the actual blocks in the file when we shrink the chain.
5599 * Currently, the blocks are still in the file. So the file size
5600 * doesn't shrink even if we shrink streams.
5602 BOOL SmallBlockChainStream_SetSize(
5603 SmallBlockChainStream* This,
5604 ULARGE_INTEGER newSize)
5606 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5608 if (newSize.u.LowPart == size.u.LowPart)
5609 return TRUE;
5611 if (newSize.u.LowPart < size.u.LowPart)
5613 SmallBlockChainStream_Shrink(This, newSize);
5615 else
5617 SmallBlockChainStream_Enlarge(This, newSize);
5620 return TRUE;
5623 /******************************************************************************
5624 * SmallBlockChainStream_GetSize
5626 * Returns the size of this chain.
5628 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5630 StgProperty chainProperty;
5632 StorageImpl_ReadProperty(
5633 This->parentStorage,
5634 This->ownerPropertyIndex,
5635 &chainProperty);
5637 return chainProperty.size;
5640 /******************************************************************************
5641 * StgCreateDocfile [OLE32.@]
5642 * Creates a new compound file storage object
5644 * PARAMS
5645 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5646 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5647 * reserved [ ?] unused?, usually 0
5648 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5650 * RETURNS
5651 * S_OK if the file was successfully created
5652 * some STG_E_ value if error
5653 * NOTES
5654 * if pwcsName is NULL, create file with new unique name
5655 * the function can returns
5656 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5657 * (unrealized now)
5659 HRESULT WINAPI StgCreateDocfile(
5660 LPCOLESTR pwcsName,
5661 DWORD grfMode,
5662 DWORD reserved,
5663 IStorage **ppstgOpen)
5665 StorageImpl* newStorage = 0;
5666 HANDLE hFile = INVALID_HANDLE_VALUE;
5667 HRESULT hr = STG_E_INVALIDFLAG;
5668 DWORD shareMode;
5669 DWORD accessMode;
5670 DWORD creationMode;
5671 DWORD fileAttributes;
5672 WCHAR tempFileName[MAX_PATH];
5674 TRACE("(%s, %lx, %ld, %p)\n",
5675 debugstr_w(pwcsName), grfMode,
5676 reserved, ppstgOpen);
5679 * Validate the parameters
5681 if (ppstgOpen == 0)
5682 return STG_E_INVALIDPOINTER;
5683 if (reserved != 0)
5684 return STG_E_INVALIDPARAMETER;
5687 * Validate the STGM flags
5689 if ( FAILED( validateSTGM(grfMode) ))
5690 goto end;
5692 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5693 switch(STGM_ACCESS_MODE(grfMode))
5695 case STGM_WRITE:
5696 case STGM_READWRITE:
5697 break;
5698 default:
5699 goto end;
5702 /* if no share mode given then DENY_NONE is the default */
5703 if (STGM_SHARE_MODE(grfMode) == 0)
5704 grfMode |= STGM_SHARE_DENY_NONE;
5706 /* must have at least one access mode */
5707 if (STGM_ACCESS_MODE(grfMode) == 0)
5708 goto end;
5710 /* in direct mode, can only use SHARE_EXCLUSIVE */
5711 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5712 goto end;
5714 /* but in transacted mode, any share mode is valid */
5717 * Generate a unique name.
5719 if (pwcsName == 0)
5721 WCHAR tempPath[MAX_PATH];
5722 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5724 memset(tempPath, 0, sizeof(tempPath));
5725 memset(tempFileName, 0, sizeof(tempFileName));
5727 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5728 tempPath[0] = '.';
5730 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5731 pwcsName = tempFileName;
5732 else
5734 hr = STG_E_INSUFFICIENTMEMORY;
5735 goto end;
5738 creationMode = TRUNCATE_EXISTING;
5740 else
5742 creationMode = GetCreationModeFromSTGM(grfMode);
5746 * Interpret the STGM value grfMode
5748 shareMode = GetShareModeFromSTGM(grfMode);
5749 accessMode = GetAccessModeFromSTGM(grfMode);
5751 if (grfMode & STGM_DELETEONRELEASE)
5752 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5753 else
5754 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5756 if (grfMode & STGM_TRANSACTED)
5757 FIXME("Transacted mode not implemented.\n");
5760 * Initialize the "out" parameter.
5762 *ppstgOpen = 0;
5764 hFile = CreateFileW(pwcsName,
5765 accessMode,
5766 shareMode,
5767 NULL,
5768 creationMode,
5769 fileAttributes,
5772 if (hFile == INVALID_HANDLE_VALUE)
5774 if(GetLastError() == ERROR_FILE_EXISTS)
5775 hr = STG_E_FILEALREADYEXISTS;
5776 else
5777 hr = E_FAIL;
5778 goto end;
5782 * Allocate and initialize the new IStorage32object.
5784 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5786 if (newStorage == 0)
5788 hr = STG_E_INSUFFICIENTMEMORY;
5789 goto end;
5792 hr = StorageImpl_Construct(
5793 newStorage,
5794 hFile,
5795 pwcsName,
5796 NULL,
5797 grfMode,
5798 TRUE,
5799 TRUE);
5801 if (FAILED(hr))
5803 HeapFree(GetProcessHeap(), 0, newStorage);
5804 goto end;
5808 * Get an "out" pointer for the caller.
5810 hr = StorageBaseImpl_QueryInterface(
5811 (IStorage*)newStorage,
5812 (REFIID)&IID_IStorage,
5813 (void**)ppstgOpen);
5814 end:
5815 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5817 return hr;
5820 /******************************************************************************
5821 * StgCreateStorageEx [OLE32.@]
5823 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5825 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5826 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5828 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5830 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5831 return STG_E_INVALIDPARAMETER;
5834 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5836 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5837 return STG_E_INVALIDPARAMETER;
5840 if (stgfmt == STGFMT_FILE)
5842 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5843 return STG_E_INVALIDPARAMETER;
5846 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5848 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5849 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5852 ERR("Invalid stgfmt argument\n");
5853 return STG_E_INVALIDPARAMETER;
5856 /******************************************************************************
5857 * StgCreatePropSetStg [OLE32.@]
5859 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5860 IPropertySetStorage **ppPropSetStg)
5862 HRESULT hr;
5864 TRACE("(%p, 0x%lx, %p)\n", pstg, reserved, ppPropSetStg);
5865 if (reserved)
5866 hr = STG_E_INVALIDPARAMETER;
5867 else
5868 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5869 (void**)ppPropSetStg);
5870 return hr;
5873 /******************************************************************************
5874 * StgOpenStorageEx [OLE32.@]
5876 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5878 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5879 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5881 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5883 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5884 return STG_E_INVALIDPARAMETER;
5887 switch (stgfmt)
5889 case STGFMT_FILE:
5890 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5891 return STG_E_INVALIDPARAMETER;
5893 case STGFMT_STORAGE:
5894 break;
5896 case STGFMT_DOCFILE:
5897 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5899 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5900 return STG_E_INVALIDPARAMETER;
5902 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5903 break;
5905 case STGFMT_ANY:
5906 WARN("STGFMT_ANY assuming storage\n");
5907 break;
5909 default:
5910 return STG_E_INVALIDPARAMETER;
5913 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5917 /******************************************************************************
5918 * StgOpenStorage [OLE32.@]
5920 HRESULT WINAPI StgOpenStorage(
5921 const OLECHAR *pwcsName,
5922 IStorage *pstgPriority,
5923 DWORD grfMode,
5924 SNB snbExclude,
5925 DWORD reserved,
5926 IStorage **ppstgOpen)
5928 StorageImpl* newStorage = 0;
5929 HRESULT hr = S_OK;
5930 HANDLE hFile = 0;
5931 DWORD shareMode;
5932 DWORD accessMode;
5933 WCHAR fullname[MAX_PATH];
5934 DWORD length;
5936 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5937 debugstr_w(pwcsName), pstgPriority, grfMode,
5938 snbExclude, reserved, ppstgOpen);
5941 * Perform sanity checks
5943 if (pwcsName == 0)
5945 hr = STG_E_INVALIDNAME;
5946 goto end;
5949 if (ppstgOpen == 0)
5951 hr = STG_E_INVALIDPOINTER;
5952 goto end;
5955 if (reserved)
5957 hr = STG_E_INVALIDPARAMETER;
5958 goto end;
5961 if (grfMode & STGM_PRIORITY)
5963 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5964 return STG_E_INVALIDFLAG;
5965 if (grfMode & STGM_DELETEONRELEASE)
5966 return STG_E_INVALIDFUNCTION;
5967 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5968 return STG_E_INVALIDFLAG;
5969 grfMode &= ~0xf0; /* remove the existing sharing mode */
5970 grfMode |= STGM_SHARE_DENY_NONE;
5972 /* STGM_PRIORITY stops other IStorage objects on the same file from
5973 * committing until the STGM_PRIORITY IStorage is closed. it also
5974 * stops non-transacted mode StgOpenStorage calls with write access from
5975 * succeeding. obviously, both of these cannot be achieved through just
5976 * file share flags */
5977 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5981 * Validate the sharing mode
5983 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5984 switch(STGM_SHARE_MODE(grfMode))
5986 case STGM_SHARE_EXCLUSIVE:
5987 case STGM_SHARE_DENY_WRITE:
5988 break;
5989 default:
5990 hr = STG_E_INVALIDFLAG;
5991 goto end;
5995 * Validate the STGM flags
5997 if ( FAILED( validateSTGM(grfMode) ) ||
5998 (grfMode&STGM_CREATE))
6000 hr = STG_E_INVALIDFLAG;
6001 goto end;
6004 /* shared reading requires transacted mode */
6005 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6006 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6007 !(grfMode&STGM_TRANSACTED) )
6009 hr = STG_E_INVALIDFLAG;
6010 goto end;
6014 * Interpret the STGM value grfMode
6016 shareMode = GetShareModeFromSTGM(grfMode);
6017 accessMode = GetAccessModeFromSTGM(grfMode);
6020 * Initialize the "out" parameter.
6022 *ppstgOpen = 0;
6024 hFile = CreateFileW( pwcsName,
6025 accessMode,
6026 shareMode,
6027 NULL,
6028 OPEN_EXISTING,
6029 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6032 if (hFile==INVALID_HANDLE_VALUE)
6034 DWORD last_error = GetLastError();
6036 hr = E_FAIL;
6038 switch (last_error)
6040 case ERROR_FILE_NOT_FOUND:
6041 hr = STG_E_FILENOTFOUND;
6042 break;
6044 case ERROR_PATH_NOT_FOUND:
6045 hr = STG_E_PATHNOTFOUND;
6046 break;
6048 case ERROR_ACCESS_DENIED:
6049 case ERROR_WRITE_PROTECT:
6050 hr = STG_E_ACCESSDENIED;
6051 break;
6053 case ERROR_SHARING_VIOLATION:
6054 hr = STG_E_SHAREVIOLATION;
6055 break;
6057 default:
6058 hr = E_FAIL;
6061 goto end;
6065 * Refuse to open the file if it's too small to be a structured storage file
6066 * FIXME: verify the file when reading instead of here
6068 length = GetFileSize(hFile, NULL);
6069 if (length < 0x100)
6071 CloseHandle(hFile);
6072 hr = STG_E_FILEALREADYEXISTS;
6073 goto end;
6077 * Allocate and initialize the new IStorage32object.
6079 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6081 if (newStorage == 0)
6083 hr = STG_E_INSUFFICIENTMEMORY;
6084 goto end;
6087 /* if the file's length was zero, initialize the storage */
6088 hr = StorageImpl_Construct(
6089 newStorage,
6090 hFile,
6091 pwcsName,
6092 NULL,
6093 grfMode,
6094 TRUE,
6095 FALSE );
6097 if (FAILED(hr))
6099 HeapFree(GetProcessHeap(), 0, newStorage);
6101 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6103 if(hr == STG_E_INVALIDHEADER)
6104 hr = STG_E_FILEALREADYEXISTS;
6105 goto end;
6108 /* prepare the file name string given in lieu of the root property name */
6109 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6110 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6111 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6114 * Get an "out" pointer for the caller.
6116 hr = StorageBaseImpl_QueryInterface(
6117 (IStorage*)newStorage,
6118 (REFIID)&IID_IStorage,
6119 (void**)ppstgOpen);
6121 end:
6122 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6123 return hr;
6126 /******************************************************************************
6127 * StgCreateDocfileOnILockBytes [OLE32.@]
6129 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6130 ILockBytes *plkbyt,
6131 DWORD grfMode,
6132 DWORD reserved,
6133 IStorage** ppstgOpen)
6135 StorageImpl* newStorage = 0;
6136 HRESULT hr = S_OK;
6139 * Validate the parameters
6141 if ((ppstgOpen == 0) || (plkbyt == 0))
6142 return STG_E_INVALIDPOINTER;
6145 * Allocate and initialize the new IStorage object.
6147 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6149 if (newStorage == 0)
6150 return STG_E_INSUFFICIENTMEMORY;
6152 hr = StorageImpl_Construct(
6153 newStorage,
6156 plkbyt,
6157 grfMode,
6158 FALSE,
6159 TRUE);
6161 if (FAILED(hr))
6163 HeapFree(GetProcessHeap(), 0, newStorage);
6164 return hr;
6168 * Get an "out" pointer for the caller.
6170 hr = StorageBaseImpl_QueryInterface(
6171 (IStorage*)newStorage,
6172 (REFIID)&IID_IStorage,
6173 (void**)ppstgOpen);
6175 return hr;
6178 /******************************************************************************
6179 * StgOpenStorageOnILockBytes [OLE32.@]
6181 HRESULT WINAPI StgOpenStorageOnILockBytes(
6182 ILockBytes *plkbyt,
6183 IStorage *pstgPriority,
6184 DWORD grfMode,
6185 SNB snbExclude,
6186 DWORD reserved,
6187 IStorage **ppstgOpen)
6189 StorageImpl* newStorage = 0;
6190 HRESULT hr = S_OK;
6193 * Perform a sanity check
6195 if ((plkbyt == 0) || (ppstgOpen == 0))
6196 return STG_E_INVALIDPOINTER;
6199 * Validate the STGM flags
6201 if ( FAILED( validateSTGM(grfMode) ))
6202 return STG_E_INVALIDFLAG;
6205 * Initialize the "out" parameter.
6207 *ppstgOpen = 0;
6210 * Allocate and initialize the new IStorage object.
6212 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6214 if (newStorage == 0)
6215 return STG_E_INSUFFICIENTMEMORY;
6217 hr = StorageImpl_Construct(
6218 newStorage,
6221 plkbyt,
6222 grfMode,
6223 FALSE,
6224 FALSE);
6226 if (FAILED(hr))
6228 HeapFree(GetProcessHeap(), 0, newStorage);
6229 return hr;
6233 * Get an "out" pointer for the caller.
6235 hr = StorageBaseImpl_QueryInterface(
6236 (IStorage*)newStorage,
6237 (REFIID)&IID_IStorage,
6238 (void**)ppstgOpen);
6240 return hr;
6243 /******************************************************************************
6244 * StgSetTimes [ole32.@]
6245 * StgSetTimes [OLE32.@]
6249 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6250 FILETIME const *patime, FILETIME const *pmtime)
6252 IStorage *stg = NULL;
6253 HRESULT r;
6255 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6257 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6258 0, 0, &stg);
6259 if( SUCCEEDED(r) )
6261 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6262 IStorage_Release(stg);
6265 return r;
6268 /******************************************************************************
6269 * StgIsStorageILockBytes [OLE32.@]
6271 * Determines if the ILockBytes contains a storage object.
6273 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6275 BYTE sig[8];
6276 ULARGE_INTEGER offset;
6278 offset.u.HighPart = 0;
6279 offset.u.LowPart = 0;
6281 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6283 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6284 return S_OK;
6286 return S_FALSE;
6289 /******************************************************************************
6290 * WriteClassStg [OLE32.@]
6292 * This method will store the specified CLSID in the specified storage object
6294 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6296 HRESULT hRes;
6298 if(!pStg)
6299 return E_INVALIDARG;
6301 hRes = IStorage_SetClass(pStg, rclsid);
6303 return hRes;
6306 /***********************************************************************
6307 * ReadClassStg (OLE32.@)
6309 * This method reads the CLSID previously written to a storage object with
6310 * the WriteClassStg.
6312 * PARAMS
6313 * pstg [I] IStorage pointer
6314 * pclsid [O] Pointer to where the CLSID is written
6316 * RETURNS
6317 * Success: S_OK.
6318 * Failure: HRESULT code.
6320 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6322 STATSTG pstatstg;
6323 HRESULT hRes;
6325 TRACE("(%p, %p)\n", pstg, pclsid);
6327 if(!pstg || !pclsid)
6328 return E_INVALIDARG;
6331 * read a STATSTG structure (contains the clsid) from the storage
6333 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6335 if(SUCCEEDED(hRes))
6336 *pclsid=pstatstg.clsid;
6338 return hRes;
6341 /***********************************************************************
6342 * OleLoadFromStream (OLE32.@)
6344 * This function loads an object from stream
6346 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6348 CLSID clsid;
6349 HRESULT res;
6350 LPPERSISTSTREAM xstm;
6352 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6354 res=ReadClassStm(pStm,&clsid);
6355 if (!SUCCEEDED(res))
6356 return res;
6357 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6358 if (!SUCCEEDED(res))
6359 return res;
6360 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6361 if (!SUCCEEDED(res)) {
6362 IUnknown_Release((IUnknown*)*ppvObj);
6363 return res;
6365 res=IPersistStream_Load(xstm,pStm);
6366 IPersistStream_Release(xstm);
6367 /* FIXME: all refcounts ok at this point? I think they should be:
6368 * pStm : unchanged
6369 * ppvObj : 1
6370 * xstm : 0 (released)
6372 return res;
6375 /***********************************************************************
6376 * OleSaveToStream (OLE32.@)
6378 * This function saves an object with the IPersistStream interface on it
6379 * to the specified stream.
6381 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6384 CLSID clsid;
6385 HRESULT res;
6387 TRACE("(%p,%p)\n",pPStm,pStm);
6389 res=IPersistStream_GetClassID(pPStm,&clsid);
6391 if (SUCCEEDED(res)){
6393 res=WriteClassStm(pStm,&clsid);
6395 if (SUCCEEDED(res))
6397 res=IPersistStream_Save(pPStm,pStm,TRUE);
6400 TRACE("Finished Save\n");
6401 return res;
6404 /****************************************************************************
6405 * This method validate a STGM parameter that can contain the values below
6407 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6408 * The stgm values contained in 0xffff0000 are bitmasks.
6410 * STGM_DIRECT 0x00000000
6411 * STGM_TRANSACTED 0x00010000
6412 * STGM_SIMPLE 0x08000000
6414 * STGM_READ 0x00000000
6415 * STGM_WRITE 0x00000001
6416 * STGM_READWRITE 0x00000002
6418 * STGM_SHARE_DENY_NONE 0x00000040
6419 * STGM_SHARE_DENY_READ 0x00000030
6420 * STGM_SHARE_DENY_WRITE 0x00000020
6421 * STGM_SHARE_EXCLUSIVE 0x00000010
6423 * STGM_PRIORITY 0x00040000
6424 * STGM_DELETEONRELEASE 0x04000000
6426 * STGM_CREATE 0x00001000
6427 * STGM_CONVERT 0x00020000
6428 * STGM_FAILIFTHERE 0x00000000
6430 * STGM_NOSCRATCH 0x00100000
6431 * STGM_NOSNAPSHOT 0x00200000
6433 static HRESULT validateSTGM(DWORD stgm)
6435 DWORD access = STGM_ACCESS_MODE(stgm);
6436 DWORD share = STGM_SHARE_MODE(stgm);
6437 DWORD create = STGM_CREATE_MODE(stgm);
6439 if (stgm&~STGM_KNOWN_FLAGS)
6441 ERR("unknown flags %08lx\n", stgm);
6442 return E_FAIL;
6445 switch (access)
6447 case STGM_READ:
6448 case STGM_WRITE:
6449 case STGM_READWRITE:
6450 break;
6451 default:
6452 return E_FAIL;
6455 switch (share)
6457 case STGM_SHARE_DENY_NONE:
6458 case STGM_SHARE_DENY_READ:
6459 case STGM_SHARE_DENY_WRITE:
6460 case STGM_SHARE_EXCLUSIVE:
6461 break;
6462 default:
6463 return E_FAIL;
6466 switch (create)
6468 case STGM_CREATE:
6469 case STGM_FAILIFTHERE:
6470 break;
6471 default:
6472 return E_FAIL;
6476 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6478 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6479 return E_FAIL;
6482 * STGM_CREATE | STGM_CONVERT
6483 * if both are false, STGM_FAILIFTHERE is set to TRUE
6485 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6486 return E_FAIL;
6489 * STGM_NOSCRATCH requires STGM_TRANSACTED
6491 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6492 return E_FAIL;
6495 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6496 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6498 if ( (stgm & STGM_NOSNAPSHOT) &&
6499 (!(stgm & STGM_TRANSACTED) ||
6500 share == STGM_SHARE_EXCLUSIVE ||
6501 share == STGM_SHARE_DENY_WRITE) )
6502 return E_FAIL;
6504 return S_OK;
6507 /****************************************************************************
6508 * GetShareModeFromSTGM
6510 * This method will return a share mode flag from a STGM value.
6511 * The STGM value is assumed valid.
6513 static DWORD GetShareModeFromSTGM(DWORD stgm)
6515 switch (STGM_SHARE_MODE(stgm))
6517 case STGM_SHARE_DENY_NONE:
6518 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6519 case STGM_SHARE_DENY_READ:
6520 return FILE_SHARE_WRITE;
6521 case STGM_SHARE_DENY_WRITE:
6522 return FILE_SHARE_READ;
6523 case STGM_SHARE_EXCLUSIVE:
6524 return 0;
6526 ERR("Invalid share mode!\n");
6527 assert(0);
6528 return 0;
6531 /****************************************************************************
6532 * GetAccessModeFromSTGM
6534 * This method will return an access mode flag from a STGM value.
6535 * The STGM value is assumed valid.
6537 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6539 switch (STGM_ACCESS_MODE(stgm))
6541 case STGM_READ:
6542 return GENERIC_READ;
6543 case STGM_WRITE:
6544 case STGM_READWRITE:
6545 return GENERIC_READ | GENERIC_WRITE;
6547 ERR("Invalid access mode!\n");
6548 assert(0);
6549 return 0;
6552 /****************************************************************************
6553 * GetCreationModeFromSTGM
6555 * This method will return a creation mode flag from a STGM value.
6556 * The STGM value is assumed valid.
6558 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6560 switch(STGM_CREATE_MODE(stgm))
6562 case STGM_CREATE:
6563 return CREATE_ALWAYS;
6564 case STGM_CONVERT:
6565 FIXME("STGM_CONVERT not implemented!\n");
6566 return CREATE_NEW;
6567 case STGM_FAILIFTHERE:
6568 return CREATE_NEW;
6570 ERR("Invalid create mode!\n");
6571 assert(0);
6572 return 0;
6576 /*************************************************************************
6577 * OLECONVERT_LoadOLE10 [Internal]
6579 * Loads the OLE10 STREAM to memory
6581 * PARAMS
6582 * pOleStream [I] The OLESTREAM
6583 * pData [I] Data Structure for the OLESTREAM Data
6585 * RETURNS
6586 * Success: S_OK
6587 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6588 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6590 * NOTES
6591 * This function is used by OleConvertOLESTREAMToIStorage only.
6593 * Memory allocated for pData must be freed by the caller
6595 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6597 DWORD dwSize;
6598 HRESULT hRes = S_OK;
6599 int nTryCnt=0;
6600 int max_try = 6;
6602 pData->pData = NULL;
6603 pData->pstrOleObjFileName = (CHAR *) NULL;
6605 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6607 /* Get the OleID */
6608 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6609 if(dwSize != sizeof(pData->dwOleID))
6611 hRes = CONVERT10_E_OLESTREAM_GET;
6613 else if(pData->dwOleID != OLESTREAM_ID)
6615 hRes = CONVERT10_E_OLESTREAM_FMT;
6617 else
6619 hRes = S_OK;
6620 break;
6624 if(hRes == S_OK)
6626 /* Get the TypeID...more info needed for this field */
6627 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6628 if(dwSize != sizeof(pData->dwTypeID))
6630 hRes = CONVERT10_E_OLESTREAM_GET;
6633 if(hRes == S_OK)
6635 if(pData->dwTypeID != 0)
6637 /* Get the length of the OleTypeName */
6638 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6639 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6641 hRes = CONVERT10_E_OLESTREAM_GET;
6644 if(hRes == S_OK)
6646 if(pData->dwOleTypeNameLength > 0)
6648 /* Get the OleTypeName */
6649 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6650 if(dwSize != pData->dwOleTypeNameLength)
6652 hRes = CONVERT10_E_OLESTREAM_GET;
6656 if(bStrem1)
6658 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6659 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6661 hRes = CONVERT10_E_OLESTREAM_GET;
6663 if(hRes == S_OK)
6665 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6666 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6667 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6668 if(pData->pstrOleObjFileName)
6670 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6671 if(dwSize != pData->dwOleObjFileNameLength)
6673 hRes = CONVERT10_E_OLESTREAM_GET;
6676 else
6677 hRes = CONVERT10_E_OLESTREAM_GET;
6680 else
6682 /* Get the Width of the Metafile */
6683 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6684 if(dwSize != sizeof(pData->dwMetaFileWidth))
6686 hRes = CONVERT10_E_OLESTREAM_GET;
6688 if(hRes == S_OK)
6690 /* Get the Height of the Metafile */
6691 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6692 if(dwSize != sizeof(pData->dwMetaFileHeight))
6694 hRes = CONVERT10_E_OLESTREAM_GET;
6698 if(hRes == S_OK)
6700 /* Get the Length of the Data */
6701 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6702 if(dwSize != sizeof(pData->dwDataLength))
6704 hRes = CONVERT10_E_OLESTREAM_GET;
6708 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6710 if(!bStrem1) /* if it is a second OLE stream data */
6712 pData->dwDataLength -= 8;
6713 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6714 if(dwSize != sizeof(pData->strUnknown))
6716 hRes = CONVERT10_E_OLESTREAM_GET;
6720 if(hRes == S_OK)
6722 if(pData->dwDataLength > 0)
6724 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6726 /* Get Data (ex. IStorage, Metafile, or BMP) */
6727 if(pData->pData)
6729 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6730 if(dwSize != pData->dwDataLength)
6732 hRes = CONVERT10_E_OLESTREAM_GET;
6735 else
6737 hRes = CONVERT10_E_OLESTREAM_GET;
6743 return hRes;
6746 /*************************************************************************
6747 * OLECONVERT_SaveOLE10 [Internal]
6749 * Saves the OLE10 STREAM From memory
6751 * PARAMS
6752 * pData [I] Data Structure for the OLESTREAM Data
6753 * pOleStream [I] The OLESTREAM to save
6755 * RETURNS
6756 * Success: S_OK
6757 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6759 * NOTES
6760 * This function is used by OleConvertIStorageToOLESTREAM only.
6763 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6765 DWORD dwSize;
6766 HRESULT hRes = S_OK;
6769 /* Set the OleID */
6770 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6771 if(dwSize != sizeof(pData->dwOleID))
6773 hRes = CONVERT10_E_OLESTREAM_PUT;
6776 if(hRes == S_OK)
6778 /* Set the TypeID */
6779 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6780 if(dwSize != sizeof(pData->dwTypeID))
6782 hRes = CONVERT10_E_OLESTREAM_PUT;
6786 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6788 /* Set the Length of the OleTypeName */
6789 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6790 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6792 hRes = CONVERT10_E_OLESTREAM_PUT;
6795 if(hRes == S_OK)
6797 if(pData->dwOleTypeNameLength > 0)
6799 /* Set the OleTypeName */
6800 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6801 if(dwSize != pData->dwOleTypeNameLength)
6803 hRes = CONVERT10_E_OLESTREAM_PUT;
6808 if(hRes == S_OK)
6810 /* Set the width of the Metafile */
6811 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6812 if(dwSize != sizeof(pData->dwMetaFileWidth))
6814 hRes = CONVERT10_E_OLESTREAM_PUT;
6818 if(hRes == S_OK)
6820 /* Set the height of the Metafile */
6821 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6822 if(dwSize != sizeof(pData->dwMetaFileHeight))
6824 hRes = CONVERT10_E_OLESTREAM_PUT;
6828 if(hRes == S_OK)
6830 /* Set the length of the Data */
6831 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6832 if(dwSize != sizeof(pData->dwDataLength))
6834 hRes = CONVERT10_E_OLESTREAM_PUT;
6838 if(hRes == S_OK)
6840 if(pData->dwDataLength > 0)
6842 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6843 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6844 if(dwSize != pData->dwDataLength)
6846 hRes = CONVERT10_E_OLESTREAM_PUT;
6851 return hRes;
6854 /*************************************************************************
6855 * OLECONVERT_GetOLE20FromOLE10[Internal]
6857 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6858 * opens it, and copies the content to the dest IStorage for
6859 * OleConvertOLESTREAMToIStorage
6862 * PARAMS
6863 * pDestStorage [I] The IStorage to copy the data to
6864 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6865 * nBufferLength [I] The size of the buffer
6867 * RETURNS
6868 * Nothing
6870 * NOTES
6874 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6876 HRESULT hRes;
6877 HANDLE hFile;
6878 IStorage *pTempStorage;
6879 DWORD dwNumOfBytesWritten;
6880 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6881 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6883 /* Create a temp File */
6884 GetTempPathW(MAX_PATH, wstrTempDir);
6885 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6886 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6888 if(hFile != INVALID_HANDLE_VALUE)
6890 /* Write IStorage Data to File */
6891 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6892 CloseHandle(hFile);
6894 /* Open and copy temp storage to the Dest Storage */
6895 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6896 if(hRes == S_OK)
6898 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6899 StorageBaseImpl_Release(pTempStorage);
6901 DeleteFileW(wstrTempFile);
6906 /*************************************************************************
6907 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6909 * Saves the OLE10 STREAM From memory
6911 * PARAMS
6912 * pStorage [I] The Src IStorage to copy
6913 * pData [I] The Dest Memory to write to.
6915 * RETURNS
6916 * The size in bytes allocated for pData
6918 * NOTES
6919 * Memory allocated for pData must be freed by the caller
6921 * Used by OleConvertIStorageToOLESTREAM only.
6924 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6926 HANDLE hFile;
6927 HRESULT hRes;
6928 DWORD nDataLength = 0;
6929 IStorage *pTempStorage;
6930 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6931 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6933 *pData = NULL;
6935 /* Create temp Storage */
6936 GetTempPathW(MAX_PATH, wstrTempDir);
6937 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6938 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6940 if(hRes == S_OK)
6942 /* Copy Src Storage to the Temp Storage */
6943 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6944 StorageBaseImpl_Release(pTempStorage);
6946 /* Open Temp Storage as a file and copy to memory */
6947 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6948 if(hFile != INVALID_HANDLE_VALUE)
6950 nDataLength = GetFileSize(hFile, NULL);
6951 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6952 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6953 CloseHandle(hFile);
6955 DeleteFileW(wstrTempFile);
6957 return nDataLength;
6960 /*************************************************************************
6961 * OLECONVERT_CreateOleStream [Internal]
6963 * Creates the "\001OLE" stream in the IStorage if necessary.
6965 * PARAMS
6966 * pStorage [I] Dest storage to create the stream in
6968 * RETURNS
6969 * Nothing
6971 * NOTES
6972 * This function is used by OleConvertOLESTREAMToIStorage only.
6974 * This stream is still unknown, MS Word seems to have extra data
6975 * but since the data is stored in the OLESTREAM there should be
6976 * no need to recreate the stream. If the stream is manually
6977 * deleted it will create it with this default data.
6980 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6982 HRESULT hRes;
6983 IStream *pStream;
6984 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6985 BYTE pOleStreamHeader [] =
6987 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6988 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6989 0x00, 0x00, 0x00, 0x00
6992 /* Create stream if not present */
6993 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6994 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6996 if(hRes == S_OK)
6998 /* Write default Data */
6999 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7000 IStream_Release(pStream);
7004 /* write a string to a stream, preceded by its length */
7005 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7007 HRESULT r;
7008 LPSTR str;
7009 DWORD len = 0;
7011 if( string )
7012 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7013 r = IStream_Write( stm, &len, sizeof(len), NULL);
7014 if( FAILED( r ) )
7015 return r;
7016 if(len == 0)
7017 return r;
7018 str = CoTaskMemAlloc( len );
7019 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7020 r = IStream_Write( stm, str, len, NULL);
7021 CoTaskMemFree( str );
7022 return r;
7025 /* read a string preceded by its length from a stream */
7026 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7028 HRESULT r;
7029 DWORD len, count = 0;
7030 LPSTR str;
7031 LPWSTR wstr;
7033 r = IStream_Read( stm, &len, sizeof(len), &count );
7034 if( FAILED( r ) )
7035 return r;
7036 if( count != sizeof(len) )
7037 return E_OUTOFMEMORY;
7039 TRACE("%ld bytes\n",len);
7041 str = CoTaskMemAlloc( len );
7042 if( !str )
7043 return E_OUTOFMEMORY;
7044 count = 0;
7045 r = IStream_Read( stm, str, len, &count );
7046 if( FAILED( r ) )
7047 return r;
7048 if( count != len )
7050 CoTaskMemFree( str );
7051 return E_OUTOFMEMORY;
7054 TRACE("Read string %s\n",debugstr_an(str,len));
7056 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7057 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7058 if( wstr )
7059 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7060 CoTaskMemFree( str );
7062 *string = wstr;
7064 return r;
7068 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7069 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7071 IStream *pstm;
7072 HRESULT r = S_OK;
7073 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7075 static const BYTE unknown1[12] =
7076 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7077 0xFF, 0xFF, 0xFF, 0xFF};
7078 static const BYTE unknown2[16] =
7079 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7080 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7082 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7083 debugstr_w(lpszUserType), debugstr_w(szClipName),
7084 debugstr_w(szProgIDName));
7086 /* Create a CompObj stream if it doesn't exist */
7087 r = IStorage_CreateStream(pstg, szwStreamName,
7088 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7089 if( FAILED (r) )
7090 return r;
7092 /* Write CompObj Structure to stream */
7093 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7095 if( SUCCEEDED( r ) )
7096 r = WriteClassStm( pstm, clsid );
7098 if( SUCCEEDED( r ) )
7099 r = STREAM_WriteString( pstm, lpszUserType );
7100 if( SUCCEEDED( r ) )
7101 r = STREAM_WriteString( pstm, szClipName );
7102 if( SUCCEEDED( r ) )
7103 r = STREAM_WriteString( pstm, szProgIDName );
7104 if( SUCCEEDED( r ) )
7105 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7107 IStream_Release( pstm );
7109 return r;
7112 /***********************************************************************
7113 * WriteFmtUserTypeStg (OLE32.@)
7115 HRESULT WINAPI WriteFmtUserTypeStg(
7116 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7118 HRESULT r;
7119 WCHAR szwClipName[0x40];
7120 CLSID clsid = CLSID_NULL;
7121 LPWSTR wstrProgID = NULL;
7122 DWORD n;
7124 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7126 /* get the clipboard format name */
7127 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7128 szwClipName[n]=0;
7130 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7132 /* FIXME: There's room to save a CLSID and its ProgID, but
7133 the CLSID is not looked up in the registry and in all the
7134 tests I wrote it was CLSID_NULL. Where does it come from?
7137 /* get the real program ID. This may fail, but that's fine */
7138 ProgIDFromCLSID(&clsid, &wstrProgID);
7140 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7142 r = STORAGE_WriteCompObj( pstg, &clsid,
7143 lpszUserType, szwClipName, wstrProgID );
7145 CoTaskMemFree(wstrProgID);
7147 return r;
7151 /******************************************************************************
7152 * ReadFmtUserTypeStg [OLE32.@]
7154 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7156 HRESULT r;
7157 IStream *stm = 0;
7158 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7159 unsigned char unknown1[12];
7160 unsigned char unknown2[16];
7161 DWORD count;
7162 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7163 CLSID clsid;
7165 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7167 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7168 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7169 if( FAILED ( r ) )
7171 WARN("Failed to open stream r = %08lx\n", r);
7172 return r;
7175 /* read the various parts of the structure */
7176 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7177 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7178 goto end;
7179 r = ReadClassStm( stm, &clsid );
7180 if( FAILED( r ) )
7181 goto end;
7183 r = STREAM_ReadString( stm, &szCLSIDName );
7184 if( FAILED( r ) )
7185 goto end;
7187 r = STREAM_ReadString( stm, &szOleTypeName );
7188 if( FAILED( r ) )
7189 goto end;
7191 r = STREAM_ReadString( stm, &szProgIDName );
7192 if( FAILED( r ) )
7193 goto end;
7195 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7196 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7197 goto end;
7199 /* ok, success... now we just need to store what we found */
7200 if( pcf )
7201 *pcf = RegisterClipboardFormatW( szOleTypeName );
7202 CoTaskMemFree( szOleTypeName );
7204 if( lplpszUserType )
7205 *lplpszUserType = szCLSIDName;
7206 CoTaskMemFree( szProgIDName );
7208 end:
7209 IStream_Release( stm );
7211 return r;
7215 /*************************************************************************
7216 * OLECONVERT_CreateCompObjStream [Internal]
7218 * Creates a "\001CompObj" is the destination IStorage if necessary.
7220 * PARAMS
7221 * pStorage [I] The dest IStorage to create the CompObj Stream
7222 * if necessary.
7223 * strOleTypeName [I] The ProgID
7225 * RETURNS
7226 * Success: S_OK
7227 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7229 * NOTES
7230 * This function is used by OleConvertOLESTREAMToIStorage only.
7232 * The stream data is stored in the OLESTREAM and there should be
7233 * no need to recreate the stream. If the stream is manually
7234 * deleted it will attempt to create it by querying the registry.
7238 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7240 IStream *pStream;
7241 HRESULT hStorageRes, hRes = S_OK;
7242 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7243 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7244 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7246 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7247 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7249 /* Initialize the CompObj structure */
7250 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7251 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7252 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7255 /* Create a CompObj stream if it doesn't exist */
7256 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7257 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7258 if(hStorageRes == S_OK)
7260 /* copy the OleTypeName to the compobj struct */
7261 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7262 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7264 /* copy the OleTypeName to the compobj struct */
7265 /* Note: in the test made, these were Identical */
7266 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7267 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7269 /* Get the CLSID */
7270 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7271 bufferW, OLESTREAM_MAX_STR_LEN );
7272 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7274 if(hRes == S_OK)
7276 HKEY hKey;
7277 LONG hErr;
7278 /* Get the CLSID Default Name from the Registry */
7279 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7280 if(hErr == ERROR_SUCCESS)
7282 char strTemp[OLESTREAM_MAX_STR_LEN];
7283 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7284 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7285 if(hErr == ERROR_SUCCESS)
7287 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7289 RegCloseKey(hKey);
7293 /* Write CompObj Structure to stream */
7294 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7296 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7298 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7299 if(IStorageCompObj.dwCLSIDNameLength > 0)
7301 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7303 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7304 if(IStorageCompObj.dwOleTypeNameLength > 0)
7306 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7308 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7309 if(IStorageCompObj.dwProgIDNameLength > 0)
7311 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7313 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7314 IStream_Release(pStream);
7316 return hRes;
7320 /*************************************************************************
7321 * OLECONVERT_CreateOlePresStream[Internal]
7323 * Creates the "\002OlePres000" Stream with the Metafile data
7325 * PARAMS
7326 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7327 * dwExtentX [I] Width of the Metafile
7328 * dwExtentY [I] Height of the Metafile
7329 * pData [I] Metafile data
7330 * dwDataLength [I] Size of the Metafile data
7332 * RETURNS
7333 * Success: S_OK
7334 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7336 * NOTES
7337 * This function is used by OleConvertOLESTREAMToIStorage only.
7340 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7342 HRESULT hRes;
7343 IStream *pStream;
7344 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7345 BYTE pOlePresStreamHeader [] =
7347 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7348 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7349 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7350 0x00, 0x00, 0x00, 0x00
7353 BYTE pOlePresStreamHeaderEmpty [] =
7355 0x00, 0x00, 0x00, 0x00,
7356 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7357 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7358 0x00, 0x00, 0x00, 0x00
7361 /* Create the OlePres000 Stream */
7362 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7363 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7365 if(hRes == S_OK)
7367 DWORD nHeaderSize;
7368 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7370 memset(&OlePres, 0, sizeof(OlePres));
7371 /* Do we have any metafile data to save */
7372 if(dwDataLength > 0)
7374 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7375 nHeaderSize = sizeof(pOlePresStreamHeader);
7377 else
7379 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7380 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7382 /* Set width and height of the metafile */
7383 OlePres.dwExtentX = dwExtentX;
7384 OlePres.dwExtentY = -dwExtentY;
7386 /* Set Data and Length */
7387 if(dwDataLength > sizeof(METAFILEPICT16))
7389 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7390 OlePres.pData = &(pData[8]);
7392 /* Save OlePres000 Data to Stream */
7393 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7394 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7395 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7396 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7397 if(OlePres.dwSize > 0)
7399 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7401 IStream_Release(pStream);
7405 /*************************************************************************
7406 * OLECONVERT_CreateOle10NativeStream [Internal]
7408 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7410 * PARAMS
7411 * pStorage [I] Dest storage to create the stream in
7412 * pData [I] Ole10 Native Data (ex. bmp)
7413 * dwDataLength [I] Size of the Ole10 Native Data
7415 * RETURNS
7416 * Nothing
7418 * NOTES
7419 * This function is used by OleConvertOLESTREAMToIStorage only.
7421 * Might need to verify the data and return appropriate error message
7424 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7426 HRESULT hRes;
7427 IStream *pStream;
7428 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7430 /* Create the Ole10Native Stream */
7431 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7432 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7434 if(hRes == S_OK)
7436 /* Write info to stream */
7437 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7438 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7439 IStream_Release(pStream);
7444 /*************************************************************************
7445 * OLECONVERT_GetOLE10ProgID [Internal]
7447 * Finds the ProgID (or OleTypeID) from the IStorage
7449 * PARAMS
7450 * pStorage [I] The Src IStorage to get the ProgID
7451 * strProgID [I] the ProgID string to get
7452 * dwSize [I] the size of the string
7454 * RETURNS
7455 * Success: S_OK
7456 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7458 * NOTES
7459 * This function is used by OleConvertIStorageToOLESTREAM only.
7463 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7465 HRESULT hRes;
7466 IStream *pStream;
7467 LARGE_INTEGER iSeekPos;
7468 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7469 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7471 /* Open the CompObj Stream */
7472 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7473 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7474 if(hRes == S_OK)
7477 /*Get the OleType from the CompObj Stream */
7478 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7479 iSeekPos.u.HighPart = 0;
7481 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7482 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7483 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7484 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7485 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7486 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7487 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7489 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7490 if(*dwSize > 0)
7492 IStream_Read(pStream, strProgID, *dwSize, NULL);
7494 IStream_Release(pStream);
7496 else
7498 STATSTG stat;
7499 LPOLESTR wstrProgID;
7501 /* Get the OleType from the registry */
7502 REFCLSID clsid = &(stat.clsid);
7503 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7504 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7505 if(hRes == S_OK)
7507 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7511 return hRes;
7514 /*************************************************************************
7515 * OLECONVERT_GetOle10PresData [Internal]
7517 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7519 * PARAMS
7520 * pStorage [I] Src IStroage
7521 * pOleStream [I] Dest OleStream Mem Struct
7523 * RETURNS
7524 * Nothing
7526 * NOTES
7527 * This function is used by OleConvertIStorageToOLESTREAM only.
7529 * Memory allocated for pData must be freed by the caller
7533 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7536 HRESULT hRes;
7537 IStream *pStream;
7538 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7540 /* Initialize Default data for OLESTREAM */
7541 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7542 pOleStreamData[0].dwTypeID = 2;
7543 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7544 pOleStreamData[1].dwTypeID = 0;
7545 pOleStreamData[0].dwMetaFileWidth = 0;
7546 pOleStreamData[0].dwMetaFileHeight = 0;
7547 pOleStreamData[0].pData = NULL;
7548 pOleStreamData[1].pData = NULL;
7550 /* Open Ole10Native Stream */
7551 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7552 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7553 if(hRes == S_OK)
7556 /* Read Size and Data */
7557 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7558 if(pOleStreamData->dwDataLength > 0)
7560 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7561 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7563 IStream_Release(pStream);
7569 /*************************************************************************
7570 * OLECONVERT_GetOle20PresData[Internal]
7572 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7574 * PARAMS
7575 * pStorage [I] Src IStroage
7576 * pOleStreamData [I] Dest OleStream Mem Struct
7578 * RETURNS
7579 * Nothing
7581 * NOTES
7582 * This function is used by OleConvertIStorageToOLESTREAM only.
7584 * Memory allocated for pData must be freed by the caller
7586 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7588 HRESULT hRes;
7589 IStream *pStream;
7590 OLECONVERT_ISTORAGE_OLEPRES olePress;
7591 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7593 /* Initialize Default data for OLESTREAM */
7594 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7595 pOleStreamData[0].dwTypeID = 2;
7596 pOleStreamData[0].dwMetaFileWidth = 0;
7597 pOleStreamData[0].dwMetaFileHeight = 0;
7598 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7599 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7600 pOleStreamData[1].dwTypeID = 0;
7601 pOleStreamData[1].dwOleTypeNameLength = 0;
7602 pOleStreamData[1].strOleTypeName[0] = 0;
7603 pOleStreamData[1].dwMetaFileWidth = 0;
7604 pOleStreamData[1].dwMetaFileHeight = 0;
7605 pOleStreamData[1].pData = NULL;
7606 pOleStreamData[1].dwDataLength = 0;
7609 /* Open OlePress000 stream */
7610 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7611 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7612 if(hRes == S_OK)
7614 LARGE_INTEGER iSeekPos;
7615 METAFILEPICT16 MetaFilePict;
7616 static const char strMetafilePictName[] = "METAFILEPICT";
7618 /* Set the TypeID for a Metafile */
7619 pOleStreamData[1].dwTypeID = 5;
7621 /* Set the OleTypeName to Metafile */
7622 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7623 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7625 iSeekPos.u.HighPart = 0;
7626 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7628 /* Get Presentation Data */
7629 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7630 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7631 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7632 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7634 /*Set width and Height */
7635 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7636 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7637 if(olePress.dwSize > 0)
7639 /* Set Length */
7640 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7642 /* Set MetaFilePict struct */
7643 MetaFilePict.mm = 8;
7644 MetaFilePict.xExt = olePress.dwExtentX;
7645 MetaFilePict.yExt = olePress.dwExtentY;
7646 MetaFilePict.hMF = 0;
7648 /* Get Metafile Data */
7649 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7650 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7651 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7653 IStream_Release(pStream);
7657 /*************************************************************************
7658 * OleConvertOLESTREAMToIStorage [OLE32.@]
7660 * Read info on MSDN
7662 * TODO
7663 * DVTARGETDEVICE paramenter is not handled
7664 * Still unsure of some mem fields for OLE 10 Stream
7665 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7666 * and "\001OLE" streams
7669 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7670 LPOLESTREAM pOleStream,
7671 LPSTORAGE pstg,
7672 const DVTARGETDEVICE* ptd)
7674 int i;
7675 HRESULT hRes=S_OK;
7676 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7678 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7680 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7682 if(ptd != NULL)
7684 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7687 if(pstg == NULL || pOleStream == NULL)
7689 hRes = E_INVALIDARG;
7692 if(hRes == S_OK)
7694 /* Load the OLESTREAM to Memory */
7695 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7698 if(hRes == S_OK)
7700 /* Load the OLESTREAM to Memory (part 2)*/
7701 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7704 if(hRes == S_OK)
7707 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7709 /* Do we have the IStorage Data in the OLESTREAM */
7710 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7712 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7713 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7715 else
7717 /* It must be an original OLE 1.0 source */
7718 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7721 else
7723 /* It must be an original OLE 1.0 source */
7724 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7727 /* Create CompObj Stream if necessary */
7728 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7729 if(hRes == S_OK)
7731 /*Create the Ole Stream if necessary */
7732 OLECONVERT_CreateOleStream(pstg);
7737 /* Free allocated memory */
7738 for(i=0; i < 2; i++)
7740 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7741 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7742 pOleStreamData[i].pstrOleObjFileName = NULL;
7744 return hRes;
7747 /*************************************************************************
7748 * OleConvertIStorageToOLESTREAM [OLE32.@]
7750 * Read info on MSDN
7752 * Read info on MSDN
7754 * TODO
7755 * Still unsure of some mem fields for OLE 10 Stream
7756 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7757 * and "\001OLE" streams.
7760 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7761 LPSTORAGE pstg,
7762 LPOLESTREAM pOleStream)
7764 int i;
7765 HRESULT hRes = S_OK;
7766 IStream *pStream;
7767 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7768 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7770 TRACE("%p %p\n", pstg, pOleStream);
7772 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7774 if(pstg == NULL || pOleStream == NULL)
7776 hRes = E_INVALIDARG;
7778 if(hRes == S_OK)
7780 /* Get the ProgID */
7781 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7782 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7784 if(hRes == S_OK)
7786 /* Was it originally Ole10 */
7787 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7788 if(hRes == S_OK)
7790 IStream_Release(pStream);
7791 /* Get Presentation Data for Ole10Native */
7792 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7794 else
7796 /* Get Presentation Data (OLE20) */
7797 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7800 /* Save OLESTREAM */
7801 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7802 if(hRes == S_OK)
7804 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7809 /* Free allocated memory */
7810 for(i=0; i < 2; i++)
7812 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7815 return hRes;
7818 /***********************************************************************
7819 * GetConvertStg (OLE32.@)
7821 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7822 FIXME("unimplemented stub!\n");
7823 return E_FAIL;
7826 /******************************************************************************
7827 * StgIsStorageFile [OLE32.@]
7828 * Verify if the file contains a storage object
7830 * PARAMS
7831 * fn [ I] Filename
7833 * RETURNS
7834 * S_OK if file has magic bytes as a storage object
7835 * S_FALSE if file is not storage
7837 HRESULT WINAPI
7838 StgIsStorageFile(LPCOLESTR fn)
7840 HANDLE hf;
7841 BYTE magic[8];
7842 DWORD bytes_read;
7844 TRACE("%s\n", debugstr_w(fn));
7845 hf = CreateFileW(fn, GENERIC_READ,
7846 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7847 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7849 if (hf == INVALID_HANDLE_VALUE)
7850 return STG_E_FILENOTFOUND;
7852 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7854 WARN(" unable to read file\n");
7855 CloseHandle(hf);
7856 return S_FALSE;
7859 CloseHandle(hf);
7861 if (bytes_read != 8) {
7862 WARN(" too short\n");
7863 return S_FALSE;
7866 if (!memcmp(magic,STORAGE_magic,8)) {
7867 WARN(" -> YES\n");
7868 return S_OK;
7871 WARN(" -> Invalid header.\n");
7872 return S_FALSE;