push decde5eed3d79f9d889b4d757f73e86ce6ff9241
[wine/hacks.git] / dlls / ole32 / storage32.c
blob7d79a39cf6d4bdae6cdf87de645785047d6aec88
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.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "winreg.h"
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootPropertyName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
81 * There is no specific data for this class.
84 typedef struct StorageInternalImpl StorageInternalImpl;
86 /* Method definitions for the Storage32InternalImpl class. */
87 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
88 DWORD openFlags, ULONG rootTropertyIndex);
89 static void StorageImpl_Destroy(StorageBaseImpl* iface);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const 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 Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
97 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
98 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
100 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
102 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
103 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
104 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
106 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
107 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
108 ULONG blockIndex, ULONG offset, DWORD value);
109 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
110 ULONG blockIndex, ULONG offset, DWORD* value);
112 /* OLESTREAM memory structure to use for Get and Put Routines */
113 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
114 typedef struct
116 DWORD dwOleID;
117 DWORD dwTypeID;
118 DWORD dwOleTypeNameLength;
119 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
120 CHAR *pstrOleObjFileName;
121 DWORD dwOleObjFileNameLength;
122 DWORD dwMetaFileWidth;
123 DWORD dwMetaFileHeight;
124 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
125 DWORD dwDataLength;
126 BYTE *pData;
127 }OLECONVERT_OLESTREAM_DATA;
129 /* CompObj Stream structure */
130 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
131 typedef struct
133 BYTE byUnknown1[12];
134 CLSID clsid;
135 DWORD dwCLSIDNameLength;
136 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
137 DWORD dwOleTypeNameLength;
138 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
139 DWORD dwProgIDNameLength;
140 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
141 BYTE byUnknown2[16];
142 }OLECONVERT_ISTORAGE_COMPOBJ;
145 /* Ole Presentation Stream structure */
146 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
147 typedef struct
149 BYTE byUnknown1[28];
150 DWORD dwExtentX;
151 DWORD dwExtentY;
152 DWORD dwSize;
153 BYTE *pData;
154 }OLECONVERT_ISTORAGE_OLEPRES;
158 /***********************************************************************
159 * Forward declaration of internal functions used by the method DestroyElement
161 static HRESULT deleteStorageProperty(
162 StorageImpl *parentStorage,
163 ULONG foundPropertyIndexToDelete,
164 StgProperty propertyToDelete);
166 static HRESULT deleteStreamProperty(
167 StorageImpl *parentStorage,
168 ULONG foundPropertyIndexToDelete,
169 StgProperty propertyToDelete);
171 static HRESULT findPlaceholder(
172 StorageImpl *storage,
173 ULONG propertyIndexToStore,
174 ULONG storagePropertyIndex,
175 INT typeOfRelation);
177 static HRESULT adjustPropertyChain(
178 StorageImpl *This,
179 StgProperty propertyToDelete,
180 StgProperty parentProperty,
181 ULONG parentPropertyId,
182 INT typeOfRelation);
184 /***********************************************************************
185 * Declaration of the functions used to manipulate StgProperty
188 static ULONG getFreeProperty(
189 StorageImpl *storage);
191 static void updatePropertyChain(
192 StorageImpl *storage,
193 ULONG newPropertyIndex,
194 StgProperty newProperty);
196 static LONG propertyNameCmp(
197 const OLECHAR *newProperty,
198 const OLECHAR *currentProperty);
201 /***********************************************************************
202 * Declaration of miscellaneous functions...
204 static HRESULT validateSTGM(DWORD stgmValue);
206 static DWORD GetShareModeFromSTGM(DWORD stgm);
207 static DWORD GetAccessModeFromSTGM(DWORD stgm);
208 static DWORD GetCreationModeFromSTGM(DWORD stgm);
210 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
213 /****************************************************************************
214 * IEnumSTATSTGImpl definitions.
216 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
217 * This class allows iterating through the content of a storage and to find
218 * specific items inside it.
220 struct IEnumSTATSTGImpl
222 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
223 * since we want to cast this in an IEnumSTATSTG pointer */
225 LONG ref; /* Reference count */
226 StorageImpl* parentStorage; /* Reference to the parent storage */
227 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
230 * The current implementation of the IEnumSTATSTGImpl class uses a stack
231 * to walk the property sets to get the content of a storage. This stack
232 * is implemented by the following 3 data members
234 ULONG stackSize;
235 ULONG stackMaxSize;
236 ULONG* stackToVisit;
238 #define ENUMSTATSGT_SIZE_INCREMENT 10
242 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
243 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
244 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
245 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
246 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
247 StgProperty* buffer);
248 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
249 StgProperty *currentProperty, ULONG *propertyId);
251 /************************************************************************
252 ** Block Functions
255 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
257 if (index == 0xffffffff)
258 index = 0;
259 else
260 index ++;
262 return index * BIG_BLOCK_SIZE;
265 /************************************************************************
266 ** Storage32BaseImpl implementation
268 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
269 ULARGE_INTEGER offset,
270 void* buffer,
271 ULONG size,
272 ULONG* bytesRead)
274 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
277 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
278 ULARGE_INTEGER offset,
279 const void* buffer,
280 const ULONG size,
281 ULONG* bytesWritten)
283 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
286 /************************************************************************
287 * Storage32BaseImpl_QueryInterface (IUnknown)
289 * This method implements the common QueryInterface for all IStorage32
290 * implementations contained in this file.
292 * See Windows documentation for more details on IUnknown methods.
294 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
295 IStorage* iface,
296 REFIID riid,
297 void** ppvObject)
299 StorageBaseImpl *This = (StorageBaseImpl *)iface;
301 * Perform a sanity check on the parameters.
303 if ( (This==0) || (ppvObject==0) )
304 return E_INVALIDARG;
307 * Initialize the return parameter.
309 *ppvObject = 0;
312 * Compare the riid with the interface IDs implemented by this object.
314 if (IsEqualGUID(&IID_IUnknown, riid) ||
315 IsEqualGUID(&IID_IStorage, riid))
317 *ppvObject = (IStorage*)This;
319 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
321 *ppvObject = (IStorage*)&This->pssVtbl;
325 * Check that we obtained an interface.
327 if ((*ppvObject)==0)
328 return E_NOINTERFACE;
331 * Query Interface always increases the reference count by one when it is
332 * successful
334 IStorage_AddRef(iface);
336 return S_OK;
339 /************************************************************************
340 * Storage32BaseImpl_AddRef (IUnknown)
342 * This method implements the common AddRef for all IStorage32
343 * implementations contained in this file.
345 * See Windows documentation for more details on IUnknown methods.
347 static ULONG WINAPI StorageBaseImpl_AddRef(
348 IStorage* iface)
350 StorageBaseImpl *This = (StorageBaseImpl *)iface;
351 ULONG ref = InterlockedIncrement(&This->ref);
353 TRACE("(%p) AddRef to %d\n", This, ref);
355 return ref;
358 /************************************************************************
359 * Storage32BaseImpl_Release (IUnknown)
361 * This method implements the common Release for all IStorage32
362 * implementations contained in this file.
364 * See Windows documentation for more details on IUnknown methods.
366 static ULONG WINAPI StorageBaseImpl_Release(
367 IStorage* iface)
369 StorageBaseImpl *This = (StorageBaseImpl *)iface;
371 * Decrease the reference count on this object.
373 ULONG ref = InterlockedDecrement(&This->ref);
375 TRACE("(%p) ReleaseRef to %d\n", This, ref);
378 * If the reference count goes down to 0, perform suicide.
380 if (ref == 0)
383 * Since we are using a system of base-classes, we want to call the
384 * destructor of the appropriate derived class. To do this, we are
385 * using virtual functions to implement the destructor.
387 This->v_destructor(This);
390 return ref;
393 /************************************************************************
394 * Storage32BaseImpl_OpenStream (IStorage)
396 * This method will open the specified stream object from the current storage.
398 * See Windows documentation for more details on IStorage methods.
400 static HRESULT WINAPI StorageBaseImpl_OpenStream(
401 IStorage* iface,
402 const OLECHAR* pwcsName, /* [string][in] */
403 void* reserved1, /* [unique][in] */
404 DWORD grfMode, /* [in] */
405 DWORD reserved2, /* [in] */
406 IStream** ppstm) /* [out] */
408 StorageBaseImpl *This = (StorageBaseImpl *)iface;
409 IEnumSTATSTGImpl* propertyEnumeration;
410 StgStreamImpl* newStream;
411 StgProperty currentProperty;
412 ULONG foundPropertyIndex;
413 HRESULT res = STG_E_UNKNOWN;
415 TRACE("(%p, %s, %p, %x, %d, %p)\n",
416 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
419 * Perform a sanity check on the parameters.
421 if ( (pwcsName==NULL) || (ppstm==0) )
423 res = E_INVALIDARG;
424 goto end;
428 * Initialize the out parameter
430 *ppstm = NULL;
433 * Validate the STGM flags
435 if ( FAILED( validateSTGM(grfMode) ) ||
436 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
438 res = STG_E_INVALIDFLAG;
439 goto end;
443 * As documented.
445 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
447 res = STG_E_INVALIDFUNCTION;
448 goto end;
452 * Check that we're compatible with the parent's storage mode, but
453 * only if we are not in transacted mode
455 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
456 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
458 res = STG_E_ACCESSDENIED;
459 goto end;
464 * Create a property enumeration to search the properties
466 propertyEnumeration = IEnumSTATSTGImpl_Construct(
467 This->ancestorStorage,
468 This->rootPropertySetIndex);
471 * Search the enumeration for the property with the given name
473 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
474 propertyEnumeration,
475 pwcsName,
476 &currentProperty);
479 * Delete the property enumeration since we don't need it anymore
481 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
484 * If it was found, construct the stream object and return a pointer to it.
486 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
487 (currentProperty.propertyType==PROPTYPE_STREAM) )
489 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
491 if (newStream!=0)
493 newStream->grfMode = grfMode;
494 *ppstm = (IStream*)newStream;
497 * Since we are returning a pointer to the interface, we have to
498 * nail down the reference.
500 IStream_AddRef(*ppstm);
502 res = S_OK;
503 goto end;
506 res = E_OUTOFMEMORY;
507 goto end;
510 res = STG_E_FILENOTFOUND;
512 end:
513 if (res == S_OK)
514 TRACE("<-- IStream %p\n", *ppstm);
515 TRACE("<-- %08x\n", res);
516 return res;
519 /************************************************************************
520 * Storage32BaseImpl_OpenStorage (IStorage)
522 * This method will open a new storage object from the current storage.
524 * See Windows documentation for more details on IStorage methods.
526 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
527 IStorage* iface,
528 const OLECHAR* pwcsName, /* [string][unique][in] */
529 IStorage* pstgPriority, /* [unique][in] */
530 DWORD grfMode, /* [in] */
531 SNB snbExclude, /* [unique][in] */
532 DWORD reserved, /* [in] */
533 IStorage** ppstg) /* [out] */
535 StorageBaseImpl *This = (StorageBaseImpl *)iface;
536 StorageInternalImpl* newStorage;
537 IEnumSTATSTGImpl* propertyEnumeration;
538 StgProperty currentProperty;
539 ULONG foundPropertyIndex;
540 HRESULT res = STG_E_UNKNOWN;
542 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
543 iface, debugstr_w(pwcsName), pstgPriority,
544 grfMode, snbExclude, reserved, ppstg);
547 * Perform a sanity check on the parameters.
549 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
551 res = E_INVALIDARG;
552 goto end;
555 /* as documented */
556 if (snbExclude != NULL)
558 res = STG_E_INVALIDPARAMETER;
559 goto end;
563 * Validate the STGM flags
565 if ( FAILED( validateSTGM(grfMode) ))
567 res = STG_E_INVALIDFLAG;
568 goto end;
572 * As documented.
574 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
575 (grfMode & STGM_DELETEONRELEASE) ||
576 (grfMode & STGM_PRIORITY) )
578 res = STG_E_INVALIDFUNCTION;
579 goto end;
583 * Check that we're compatible with the parent's storage mode,
584 * but only if we are not transacted
586 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
587 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
589 res = STG_E_ACCESSDENIED;
590 goto end;
595 * Initialize the out parameter
597 *ppstg = NULL;
600 * Create a property enumeration to search the properties
602 propertyEnumeration = IEnumSTATSTGImpl_Construct(
603 This->ancestorStorage,
604 This->rootPropertySetIndex);
607 * Search the enumeration for the property with the given name
609 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
610 propertyEnumeration,
611 pwcsName,
612 &currentProperty);
615 * Delete the property enumeration since we don't need it anymore
617 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
620 * If it was found, construct the stream object and return a pointer to it.
622 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
623 (currentProperty.propertyType==PROPTYPE_STORAGE) )
626 * Construct a new Storage object
628 newStorage = StorageInternalImpl_Construct(
629 This->ancestorStorage,
630 grfMode,
631 foundPropertyIndex);
633 if (newStorage != 0)
635 *ppstg = (IStorage*)newStorage;
638 * Since we are returning a pointer to the interface,
639 * we have to nail down the reference.
641 StorageBaseImpl_AddRef(*ppstg);
643 res = S_OK;
644 goto end;
647 res = STG_E_INSUFFICIENTMEMORY;
648 goto end;
651 res = STG_E_FILENOTFOUND;
653 end:
654 TRACE("<-- %08x\n", res);
655 return res;
658 /************************************************************************
659 * Storage32BaseImpl_EnumElements (IStorage)
661 * This method will create an enumerator object that can be used to
662 * retrieve information about all the properties in the storage object.
664 * See Windows documentation for more details on IStorage methods.
666 static HRESULT WINAPI StorageBaseImpl_EnumElements(
667 IStorage* iface,
668 DWORD reserved1, /* [in] */
669 void* reserved2, /* [size_is][unique][in] */
670 DWORD reserved3, /* [in] */
671 IEnumSTATSTG** ppenum) /* [out] */
673 StorageBaseImpl *This = (StorageBaseImpl *)iface;
674 IEnumSTATSTGImpl* newEnum;
676 TRACE("(%p, %d, %p, %d, %p)\n",
677 iface, reserved1, reserved2, reserved3, ppenum);
680 * Perform a sanity check on the parameters.
682 if ( (This==0) || (ppenum==0))
683 return E_INVALIDARG;
686 * Construct the enumerator.
688 newEnum = IEnumSTATSTGImpl_Construct(
689 This->ancestorStorage,
690 This->rootPropertySetIndex);
692 if (newEnum!=0)
694 *ppenum = (IEnumSTATSTG*)newEnum;
697 * Don't forget to nail down a reference to the new object before
698 * returning it.
700 IEnumSTATSTG_AddRef(*ppenum);
702 return S_OK;
705 return E_OUTOFMEMORY;
708 /************************************************************************
709 * Storage32BaseImpl_Stat (IStorage)
711 * This method will retrieve information about this storage object.
713 * See Windows documentation for more details on IStorage methods.
715 static HRESULT WINAPI StorageBaseImpl_Stat(
716 IStorage* iface,
717 STATSTG* pstatstg, /* [out] */
718 DWORD grfStatFlag) /* [in] */
720 StorageBaseImpl *This = (StorageBaseImpl *)iface;
721 StgProperty curProperty;
722 BOOL readSuccessful;
723 HRESULT res = STG_E_UNKNOWN;
725 TRACE("(%p, %p, %x)\n",
726 iface, pstatstg, grfStatFlag);
729 * Perform a sanity check on the parameters.
731 if ( (This==0) || (pstatstg==0))
733 res = E_INVALIDARG;
734 goto end;
738 * Read the information from the property.
740 readSuccessful = StorageImpl_ReadProperty(
741 This->ancestorStorage,
742 This->rootPropertySetIndex,
743 &curProperty);
745 if (readSuccessful)
747 StorageUtl_CopyPropertyToSTATSTG(
748 pstatstg,
749 &curProperty,
750 grfStatFlag);
752 pstatstg->grfMode = This->openFlags;
753 pstatstg->grfStateBits = This->stateBits;
755 res = S_OK;
756 goto end;
759 res = E_FAIL;
761 end:
762 if (res == S_OK)
764 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
766 TRACE("<-- %08x\n", res);
767 return res;
770 /************************************************************************
771 * Storage32BaseImpl_RenameElement (IStorage)
773 * This method will rename the specified element.
775 * See Windows documentation for more details on IStorage methods.
777 * Implementation notes: The method used to rename consists of creating a clone
778 * of the deleted StgProperty object setting it with the new name and to
779 * perform a DestroyElement of the old StgProperty.
781 static HRESULT WINAPI StorageBaseImpl_RenameElement(
782 IStorage* iface,
783 const OLECHAR* pwcsOldName, /* [in] */
784 const OLECHAR* pwcsNewName) /* [in] */
786 StorageBaseImpl *This = (StorageBaseImpl *)iface;
787 IEnumSTATSTGImpl* propertyEnumeration;
788 StgProperty currentProperty;
789 ULONG foundPropertyIndex;
791 TRACE("(%p, %s, %s)\n",
792 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
795 * Create a property enumeration to search the properties
797 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
798 This->rootPropertySetIndex);
801 * Search the enumeration for the new property name
803 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
804 pwcsNewName,
805 &currentProperty);
807 if (foundPropertyIndex != PROPERTY_NULL)
810 * There is already a property with the new name
812 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
813 return STG_E_FILEALREADYEXISTS;
816 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
819 * Search the enumeration for the old property name
821 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
822 pwcsOldName,
823 &currentProperty);
826 * Delete the property enumeration since we don't need it anymore
828 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
830 if (foundPropertyIndex != PROPERTY_NULL)
832 StgProperty renamedProperty;
833 ULONG renamedPropertyIndex;
836 * Setup a new property for the renamed property
838 renamedProperty.sizeOfNameString =
839 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
841 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
842 return STG_E_INVALIDNAME;
844 strcpyW(renamedProperty.name, pwcsNewName);
846 renamedProperty.propertyType = currentProperty.propertyType;
847 renamedProperty.startingBlock = currentProperty.startingBlock;
848 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
849 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
851 renamedProperty.previousProperty = PROPERTY_NULL;
852 renamedProperty.nextProperty = PROPERTY_NULL;
855 * Bring the dirProperty link in case it is a storage and in which
856 * case the renamed storage elements don't require to be reorganized.
858 renamedProperty.dirProperty = currentProperty.dirProperty;
860 /* call CoFileTime to get the current time
861 renamedProperty.timeStampS1
862 renamedProperty.timeStampD1
863 renamedProperty.timeStampS2
864 renamedProperty.timeStampD2
865 renamedProperty.propertyUniqueID
869 * Obtain a free property in the property chain
871 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
874 * Save the new property into the new property spot
876 StorageImpl_WriteProperty(
877 This->ancestorStorage,
878 renamedPropertyIndex,
879 &renamedProperty);
882 * Find a spot in the property chain for our newly created property.
884 updatePropertyChain(
885 (StorageImpl*)This,
886 renamedPropertyIndex,
887 renamedProperty);
890 * At this point the renamed property has been inserted in the tree,
891 * now, before Destroying the old property we must zero its dirProperty
892 * otherwise the DestroyProperty below will zap it all and we do not want
893 * this to happen.
894 * Also, we fake that the old property is a storage so the DestroyProperty
895 * will not do a SetSize(0) on the stream data.
897 * This means that we need to tweak the StgProperty if it is a stream or a
898 * non empty storage.
900 StorageImpl_ReadProperty(This->ancestorStorage,
901 foundPropertyIndex,
902 &currentProperty);
904 currentProperty.dirProperty = PROPERTY_NULL;
905 currentProperty.propertyType = PROPTYPE_STORAGE;
906 StorageImpl_WriteProperty(
907 This->ancestorStorage,
908 foundPropertyIndex,
909 &currentProperty);
912 * Invoke Destroy to get rid of the ole property and automatically redo
913 * the linking of its previous and next members...
915 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
918 else
921 * There is no property with the old name
923 return STG_E_FILENOTFOUND;
926 return S_OK;
929 /************************************************************************
930 * Storage32BaseImpl_CreateStream (IStorage)
932 * This method will create a stream object within this storage
934 * See Windows documentation for more details on IStorage methods.
936 static HRESULT WINAPI StorageBaseImpl_CreateStream(
937 IStorage* iface,
938 const OLECHAR* pwcsName, /* [string][in] */
939 DWORD grfMode, /* [in] */
940 DWORD reserved1, /* [in] */
941 DWORD reserved2, /* [in] */
942 IStream** ppstm) /* [out] */
944 StorageBaseImpl *This = (StorageBaseImpl *)iface;
945 IEnumSTATSTGImpl* propertyEnumeration;
946 StgStreamImpl* newStream;
947 StgProperty currentProperty, newStreamProperty;
948 ULONG foundPropertyIndex, newPropertyIndex;
950 TRACE("(%p, %s, %x, %d, %d, %p)\n",
951 iface, debugstr_w(pwcsName), grfMode,
952 reserved1, reserved2, ppstm);
955 * Validate parameters
957 if (ppstm == 0)
958 return STG_E_INVALIDPOINTER;
960 if (pwcsName == 0)
961 return STG_E_INVALIDNAME;
963 if (reserved1 || reserved2)
964 return STG_E_INVALIDPARAMETER;
967 * Validate the STGM flags
969 if ( FAILED( validateSTGM(grfMode) ))
970 return STG_E_INVALIDFLAG;
972 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
973 return STG_E_INVALIDFLAG;
976 * As documented.
978 if ((grfMode & STGM_DELETEONRELEASE) ||
979 (grfMode & STGM_TRANSACTED))
980 return STG_E_INVALIDFUNCTION;
983 * Check that we're compatible with the parent's storage mode
984 * if not in transacted mode
986 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
987 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
988 return STG_E_ACCESSDENIED;
992 * Initialize the out parameter
994 *ppstm = 0;
997 * Create a property enumeration to search the properties
999 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1000 This->rootPropertySetIndex);
1002 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1003 pwcsName,
1004 &currentProperty);
1006 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1008 if (foundPropertyIndex != PROPERTY_NULL)
1011 * An element with this name already exists
1013 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1015 StgStreamImpl *strm;
1017 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
1019 if (strm->ownerProperty == foundPropertyIndex)
1021 TRACE("Stream deleted %p\n", strm);
1022 strm->parentStorage = NULL;
1023 list_remove(&strm->StrmListEntry);
1026 IStorage_DestroyElement(iface, pwcsName);
1028 else
1029 return STG_E_FILEALREADYEXISTS;
1031 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1033 WARN("read-only storage\n");
1034 return STG_E_ACCESSDENIED;
1038 * memset the empty property
1040 memset(&newStreamProperty, 0, sizeof(StgProperty));
1042 newStreamProperty.sizeOfNameString =
1043 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1045 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1046 return STG_E_INVALIDNAME;
1048 strcpyW(newStreamProperty.name, pwcsName);
1050 newStreamProperty.propertyType = PROPTYPE_STREAM;
1051 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1052 newStreamProperty.size.u.LowPart = 0;
1053 newStreamProperty.size.u.HighPart = 0;
1055 newStreamProperty.previousProperty = PROPERTY_NULL;
1056 newStreamProperty.nextProperty = PROPERTY_NULL;
1057 newStreamProperty.dirProperty = PROPERTY_NULL;
1059 /* call CoFileTime to get the current time
1060 newStreamProperty.timeStampS1
1061 newStreamProperty.timeStampD1
1062 newStreamProperty.timeStampS2
1063 newStreamProperty.timeStampD2
1066 /* newStreamProperty.propertyUniqueID */
1069 * Get a free property or create a new one
1071 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1074 * Save the new property into the new property spot
1076 StorageImpl_WriteProperty(
1077 This->ancestorStorage,
1078 newPropertyIndex,
1079 &newStreamProperty);
1082 * Find a spot in the property chain for our newly created property.
1084 updatePropertyChain(
1085 (StorageImpl*)This,
1086 newPropertyIndex,
1087 newStreamProperty);
1090 * Open the stream to return it.
1092 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1094 if (newStream != 0)
1096 *ppstm = (IStream*)newStream;
1099 * Since we are returning a pointer to the interface, we have to nail down
1100 * the reference.
1102 IStream_AddRef(*ppstm);
1104 else
1106 return STG_E_INSUFFICIENTMEMORY;
1109 return S_OK;
1112 /************************************************************************
1113 * Storage32BaseImpl_SetClass (IStorage)
1115 * This method will write the specified CLSID in the property of this
1116 * storage.
1118 * See Windows documentation for more details on IStorage methods.
1120 static HRESULT WINAPI StorageBaseImpl_SetClass(
1121 IStorage* iface,
1122 REFCLSID clsid) /* [in] */
1124 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1125 HRESULT hRes = E_FAIL;
1126 StgProperty curProperty;
1127 BOOL success;
1129 TRACE("(%p, %p)\n", iface, clsid);
1131 success = StorageImpl_ReadProperty(This->ancestorStorage,
1132 This->rootPropertySetIndex,
1133 &curProperty);
1134 if (success)
1136 curProperty.propertyUniqueID = *clsid;
1138 success = StorageImpl_WriteProperty(This->ancestorStorage,
1139 This->rootPropertySetIndex,
1140 &curProperty);
1141 if (success)
1142 hRes = S_OK;
1145 return hRes;
1148 /************************************************************************
1149 ** Storage32Impl implementation
1152 /************************************************************************
1153 * Storage32Impl_CreateStorage (IStorage)
1155 * This method will create the storage object within the provided storage.
1157 * See Windows documentation for more details on IStorage methods.
1159 static HRESULT WINAPI StorageImpl_CreateStorage(
1160 IStorage* iface,
1161 const OLECHAR *pwcsName, /* [string][in] */
1162 DWORD grfMode, /* [in] */
1163 DWORD reserved1, /* [in] */
1164 DWORD reserved2, /* [in] */
1165 IStorage **ppstg) /* [out] */
1167 StorageImpl* const This=(StorageImpl*)iface;
1169 IEnumSTATSTGImpl *propertyEnumeration;
1170 StgProperty currentProperty;
1171 StgProperty newProperty;
1172 ULONG foundPropertyIndex;
1173 ULONG newPropertyIndex;
1174 HRESULT hr;
1176 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1177 iface, debugstr_w(pwcsName), grfMode,
1178 reserved1, reserved2, ppstg);
1181 * Validate parameters
1183 if (ppstg == 0)
1184 return STG_E_INVALIDPOINTER;
1186 if (pwcsName == 0)
1187 return STG_E_INVALIDNAME;
1190 * Initialize the out parameter
1192 *ppstg = NULL;
1195 * Validate the STGM flags
1197 if ( FAILED( validateSTGM(grfMode) ) ||
1198 (grfMode & STGM_DELETEONRELEASE) )
1200 WARN("bad grfMode: 0x%x\n", grfMode);
1201 return STG_E_INVALIDFLAG;
1205 * Check that we're compatible with the parent's storage mode
1207 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1209 WARN("access denied\n");
1210 return STG_E_ACCESSDENIED;
1214 * Create a property enumeration and search the properties
1216 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1217 This->base.rootPropertySetIndex);
1219 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1220 pwcsName,
1221 &currentProperty);
1222 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1224 if (foundPropertyIndex != PROPERTY_NULL)
1227 * An element with this name already exists
1229 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1230 IStorage_DestroyElement(iface, pwcsName);
1231 else
1233 WARN("file already exists\n");
1234 return STG_E_FILEALREADYEXISTS;
1237 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1239 WARN("read-only storage\n");
1240 return STG_E_ACCESSDENIED;
1244 * memset the empty property
1246 memset(&newProperty, 0, sizeof(StgProperty));
1248 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1250 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1252 FIXME("name too long\n");
1253 return STG_E_INVALIDNAME;
1256 strcpyW(newProperty.name, pwcsName);
1258 newProperty.propertyType = PROPTYPE_STORAGE;
1259 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1260 newProperty.size.u.LowPart = 0;
1261 newProperty.size.u.HighPart = 0;
1263 newProperty.previousProperty = PROPERTY_NULL;
1264 newProperty.nextProperty = PROPERTY_NULL;
1265 newProperty.dirProperty = PROPERTY_NULL;
1267 /* call CoFileTime to get the current time
1268 newProperty.timeStampS1
1269 newProperty.timeStampD1
1270 newProperty.timeStampS2
1271 newProperty.timeStampD2
1274 /* newStorageProperty.propertyUniqueID */
1277 * Obtain a free property in the property chain
1279 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1282 * Save the new property into the new property spot
1284 StorageImpl_WriteProperty(
1285 This->base.ancestorStorage,
1286 newPropertyIndex,
1287 &newProperty);
1290 * Find a spot in the property chain for our newly created property.
1292 updatePropertyChain(
1293 This,
1294 newPropertyIndex,
1295 newProperty);
1298 * Open it to get a pointer to return.
1300 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1302 if( (hr != S_OK) || (*ppstg == NULL))
1304 return hr;
1308 return S_OK;
1312 /***************************************************************************
1314 * Internal Method
1316 * Get a free property or create a new one.
1318 static ULONG getFreeProperty(
1319 StorageImpl *storage)
1321 ULONG currentPropertyIndex = 0;
1322 ULONG newPropertyIndex = PROPERTY_NULL;
1323 BOOL readSuccessful = TRUE;
1324 StgProperty currentProperty;
1329 * Start by reading the root property
1331 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1332 currentPropertyIndex,
1333 &currentProperty);
1334 if (readSuccessful)
1336 if (currentProperty.sizeOfNameString == 0)
1339 * The property existis and is available, we found it.
1341 newPropertyIndex = currentPropertyIndex;
1344 else
1347 * We exhausted the property list, we will create more space below
1349 newPropertyIndex = currentPropertyIndex;
1351 currentPropertyIndex++;
1353 } while (newPropertyIndex == PROPERTY_NULL);
1356 * grow the property chain
1358 if (! readSuccessful)
1360 StgProperty emptyProperty;
1361 ULARGE_INTEGER newSize;
1362 ULONG propertyIndex;
1363 ULONG lastProperty = 0;
1364 ULONG blockCount = 0;
1367 * obtain the new count of property blocks
1369 blockCount = BlockChainStream_GetCount(
1370 storage->base.ancestorStorage->rootBlockChain)+1;
1373 * initialize the size used by the property stream
1375 newSize.u.HighPart = 0;
1376 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1379 * add a property block to the property chain
1381 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1384 * memset the empty property in order to initialize the unused newly
1385 * created property
1387 memset(&emptyProperty, 0, sizeof(StgProperty));
1390 * initialize them
1392 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1394 for(
1395 propertyIndex = newPropertyIndex;
1396 propertyIndex < lastProperty;
1397 propertyIndex++)
1399 StorageImpl_WriteProperty(
1400 storage->base.ancestorStorage,
1401 propertyIndex,
1402 &emptyProperty);
1406 return newPropertyIndex;
1409 /****************************************************************************
1411 * Internal Method
1413 * Case insensitive comparison of StgProperty.name by first considering
1414 * their size.
1416 * Returns <0 when newProperty < currentProperty
1417 * >0 when newProperty > currentProperty
1418 * 0 when newProperty == currentProperty
1420 static LONG propertyNameCmp(
1421 const OLECHAR *newProperty,
1422 const OLECHAR *currentProperty)
1424 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1426 if (diff == 0)
1429 * We compare the string themselves only when they are of the same length
1431 diff = lstrcmpiW( newProperty, currentProperty);
1434 return diff;
1437 /****************************************************************************
1439 * Internal Method
1441 * Properly link this new element in the property chain.
1443 static void updatePropertyChain(
1444 StorageImpl *storage,
1445 ULONG newPropertyIndex,
1446 StgProperty newProperty)
1448 StgProperty currentProperty;
1451 * Read the root property
1453 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1454 storage->base.rootPropertySetIndex,
1455 &currentProperty);
1457 if (currentProperty.dirProperty != PROPERTY_NULL)
1460 * The root storage contains some element, therefore, start the research
1461 * for the appropriate location.
1463 BOOL found = 0;
1464 ULONG current, next, previous, currentPropertyId;
1467 * Keep the StgProperty sequence number of the storage first property
1469 currentPropertyId = currentProperty.dirProperty;
1472 * Read
1474 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1475 currentProperty.dirProperty,
1476 &currentProperty);
1478 previous = currentProperty.previousProperty;
1479 next = currentProperty.nextProperty;
1480 current = currentPropertyId;
1482 while (found == 0)
1484 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1486 if (diff < 0)
1488 if (previous != PROPERTY_NULL)
1490 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1491 previous,
1492 &currentProperty);
1493 current = previous;
1495 else
1497 currentProperty.previousProperty = newPropertyIndex;
1498 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1499 current,
1500 &currentProperty);
1501 found = 1;
1504 else if (diff > 0)
1506 if (next != PROPERTY_NULL)
1508 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1509 next,
1510 &currentProperty);
1511 current = next;
1513 else
1515 currentProperty.nextProperty = newPropertyIndex;
1516 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1517 current,
1518 &currentProperty);
1519 found = 1;
1522 else
1525 * Trying to insert an item with the same name in the
1526 * subtree structure.
1528 assert(FALSE);
1531 previous = currentProperty.previousProperty;
1532 next = currentProperty.nextProperty;
1535 else
1538 * The root storage is empty, link the new property to its dir property
1540 currentProperty.dirProperty = newPropertyIndex;
1541 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1542 storage->base.rootPropertySetIndex,
1543 &currentProperty);
1548 /*************************************************************************
1549 * CopyTo (IStorage)
1551 static HRESULT WINAPI StorageImpl_CopyTo(
1552 IStorage* iface,
1553 DWORD ciidExclude, /* [in] */
1554 const IID* rgiidExclude, /* [size_is][unique][in] */
1555 SNB snbExclude, /* [unique][in] */
1556 IStorage* pstgDest) /* [unique][in] */
1558 IEnumSTATSTG *elements = 0;
1559 STATSTG curElement, strStat;
1560 HRESULT hr;
1561 IStorage *pstgTmp, *pstgChild;
1562 IStream *pstrTmp, *pstrChild;
1564 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1565 FIXME("Exclude option not implemented\n");
1567 TRACE("(%p, %d, %p, %p, %p)\n",
1568 iface, ciidExclude, rgiidExclude,
1569 snbExclude, pstgDest);
1572 * Perform a sanity check
1574 if ( pstgDest == 0 )
1575 return STG_E_INVALIDPOINTER;
1578 * Enumerate the elements
1580 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1582 if ( hr != S_OK )
1583 return hr;
1586 * set the class ID
1588 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1589 IStorage_SetClass( pstgDest, &curElement.clsid );
1594 * Obtain the next element
1596 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1598 if ( hr == S_FALSE )
1600 hr = S_OK; /* done, every element has been copied */
1601 break;
1604 if (curElement.type == STGTY_STORAGE)
1607 * open child source storage
1609 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1610 STGM_READ|STGM_SHARE_EXCLUSIVE,
1611 NULL, 0, &pstgChild );
1613 if (hr != S_OK)
1614 break;
1617 * Check if destination storage is not a child of the source
1618 * storage, which will cause an infinite loop
1620 if (pstgChild == pstgDest)
1622 IEnumSTATSTG_Release(elements);
1624 return STG_E_ACCESSDENIED;
1628 * create a new storage in destination storage
1630 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1631 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1632 0, 0,
1633 &pstgTmp );
1635 * if it already exist, don't create a new one use this one
1637 if (hr == STG_E_FILEALREADYEXISTS)
1639 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1640 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1641 NULL, 0, &pstgTmp );
1644 if (hr != S_OK)
1645 break;
1649 * do the copy recursively
1651 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1652 snbExclude, pstgTmp );
1654 IStorage_Release( pstgTmp );
1655 IStorage_Release( pstgChild );
1657 else if (curElement.type == STGTY_STREAM)
1660 * create a new stream in destination storage. If the stream already
1661 * exist, it will be deleted and a new one will be created.
1663 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1664 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1665 0, 0, &pstrTmp );
1667 if (hr != S_OK)
1668 break;
1671 * open child stream storage
1673 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1674 STGM_READ|STGM_SHARE_EXCLUSIVE,
1675 0, &pstrChild );
1677 if (hr != S_OK)
1678 break;
1681 * Get the size of the source stream
1683 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1686 * Set the size of the destination stream.
1688 IStream_SetSize(pstrTmp, strStat.cbSize);
1691 * do the copy
1693 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1694 NULL, NULL );
1696 IStream_Release( pstrTmp );
1697 IStream_Release( pstrChild );
1699 else
1701 WARN("unknown element type: %d\n", curElement.type);
1704 } while (hr == S_OK);
1707 * Clean-up
1709 IEnumSTATSTG_Release(elements);
1711 return hr;
1714 /*************************************************************************
1715 * MoveElementTo (IStorage)
1717 static HRESULT WINAPI StorageImpl_MoveElementTo(
1718 IStorage* iface,
1719 const OLECHAR *pwcsName, /* [string][in] */
1720 IStorage *pstgDest, /* [unique][in] */
1721 const OLECHAR *pwcsNewName,/* [string][in] */
1722 DWORD grfFlags) /* [in] */
1724 FIXME("(%p %s %p %s %u): stub\n", iface,
1725 debugstr_w(pwcsName), pstgDest,
1726 debugstr_w(pwcsNewName), grfFlags);
1727 return E_NOTIMPL;
1730 /*************************************************************************
1731 * Commit (IStorage)
1733 * Ensures that any changes made to a storage object open in transacted mode
1734 * are reflected in the parent storage
1736 * NOTES
1737 * Wine doesn't implement transacted mode, which seems to be a basic
1738 * optimization, so we can ignore this stub for now.
1740 static HRESULT WINAPI StorageImpl_Commit(
1741 IStorage* iface,
1742 DWORD grfCommitFlags)/* [in] */
1744 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1745 return S_OK;
1748 /*************************************************************************
1749 * Revert (IStorage)
1751 * Discard all changes that have been made since the last commit operation
1753 static HRESULT WINAPI StorageImpl_Revert(
1754 IStorage* iface)
1756 FIXME("(%p): stub\n", iface);
1757 return E_NOTIMPL;
1760 /*************************************************************************
1761 * DestroyElement (IStorage)
1763 * Strategy: This implementation is built this way for simplicity not for speed.
1764 * I always delete the topmost element of the enumeration and adjust
1765 * the deleted element pointer all the time. This takes longer to
1766 * do but allow to reinvoke DestroyElement whenever we encounter a
1767 * storage object. The optimisation resides in the usage of another
1768 * enumeration strategy that would give all the leaves of a storage
1769 * first. (postfix order)
1771 static HRESULT WINAPI StorageImpl_DestroyElement(
1772 IStorage* iface,
1773 const OLECHAR *pwcsName)/* [string][in] */
1775 StorageImpl* const This=(StorageImpl*)iface;
1777 IEnumSTATSTGImpl* propertyEnumeration;
1778 HRESULT hr = S_OK;
1779 BOOL res;
1780 StgProperty propertyToDelete;
1781 StgProperty parentProperty;
1782 ULONG foundPropertyIndexToDelete;
1783 ULONG typeOfRelation;
1784 ULONG parentPropertyId = 0;
1786 TRACE("(%p, %s)\n",
1787 iface, debugstr_w(pwcsName));
1790 * Perform a sanity check on the parameters.
1792 if (pwcsName==NULL)
1793 return STG_E_INVALIDPOINTER;
1796 * Create a property enumeration to search the property with the given name
1798 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1799 This->base.ancestorStorage,
1800 This->base.rootPropertySetIndex);
1802 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1803 propertyEnumeration,
1804 pwcsName,
1805 &propertyToDelete);
1807 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1809 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1811 return STG_E_FILENOTFOUND;
1815 * Find the parent property of the property to delete (the one that
1816 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1817 * the parent is This. Otherwise, the parent is one of its sibling...
1821 * First, read This's StgProperty..
1823 res = StorageImpl_ReadProperty(
1824 This->base.ancestorStorage,
1825 This->base.rootPropertySetIndex,
1826 &parentProperty);
1828 assert(res);
1831 * Second, check to see if by any chance the actual storage (This) is not
1832 * the parent of the property to delete... We never know...
1834 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1837 * Set data as it would have been done in the else part...
1839 typeOfRelation = PROPERTY_RELATION_DIR;
1840 parentPropertyId = This->base.rootPropertySetIndex;
1842 else
1845 * Create a property enumeration to search the parent properties, and
1846 * delete it once done.
1848 IEnumSTATSTGImpl* propertyEnumeration2;
1850 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1851 This->base.ancestorStorage,
1852 This->base.rootPropertySetIndex);
1854 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1855 propertyEnumeration2,
1856 foundPropertyIndexToDelete,
1857 &parentProperty,
1858 &parentPropertyId);
1860 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1863 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1865 hr = deleteStorageProperty(
1866 This,
1867 foundPropertyIndexToDelete,
1868 propertyToDelete);
1870 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1872 hr = deleteStreamProperty(
1873 This,
1874 foundPropertyIndexToDelete,
1875 propertyToDelete);
1878 if (hr!=S_OK)
1879 return hr;
1882 * Adjust the property chain
1884 hr = adjustPropertyChain(
1885 This,
1886 propertyToDelete,
1887 parentProperty,
1888 parentPropertyId,
1889 typeOfRelation);
1891 return hr;
1895 /************************************************************************
1896 * StorageImpl_Stat (IStorage)
1898 * This method will retrieve information about this storage object.
1900 * See Windows documentation for more details on IStorage methods.
1902 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1903 STATSTG* pstatstg, /* [out] */
1904 DWORD grfStatFlag) /* [in] */
1906 StorageImpl* const This = (StorageImpl*)iface;
1907 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1909 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1911 CoTaskMemFree(pstatstg->pwcsName);
1912 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1913 strcpyW(pstatstg->pwcsName, This->pwcsName);
1916 return result;
1919 /******************************************************************************
1920 * Internal stream list handlers
1923 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1925 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1926 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1929 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1931 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1932 list_remove(&(strm->StrmListEntry));
1935 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1937 struct list *cur, *cur2;
1938 StgStreamImpl *strm=NULL;
1940 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1941 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1942 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1943 strm->parentStorage = NULL;
1944 list_remove(cur);
1949 /*********************************************************************
1951 * Internal Method
1953 * Perform the deletion of a complete storage node
1956 static HRESULT deleteStorageProperty(
1957 StorageImpl *parentStorage,
1958 ULONG indexOfPropertyToDelete,
1959 StgProperty propertyToDelete)
1961 IEnumSTATSTG *elements = 0;
1962 IStorage *childStorage = 0;
1963 STATSTG currentElement;
1964 HRESULT hr;
1965 HRESULT destroyHr = S_OK;
1968 * Open the storage and enumerate it
1970 hr = StorageBaseImpl_OpenStorage(
1971 (IStorage*)parentStorage,
1972 propertyToDelete.name,
1974 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1977 &childStorage);
1979 if (hr != S_OK)
1981 return hr;
1985 * Enumerate the elements
1987 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1992 * Obtain the next element
1994 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1995 if (hr==S_OK)
1997 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
1999 CoTaskMemFree(currentElement.pwcsName);
2003 * We need to Reset the enumeration every time because we delete elements
2004 * and the enumeration could be invalid
2006 IEnumSTATSTG_Reset(elements);
2008 } while ((hr == S_OK) && (destroyHr == S_OK));
2011 * Invalidate the property by zeroing its name member.
2013 propertyToDelete.sizeOfNameString = 0;
2015 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2016 indexOfPropertyToDelete,
2017 &propertyToDelete);
2019 IStorage_Release(childStorage);
2020 IEnumSTATSTG_Release(elements);
2022 return destroyHr;
2025 /*********************************************************************
2027 * Internal Method
2029 * Perform the deletion of a stream node
2032 static HRESULT deleteStreamProperty(
2033 StorageImpl *parentStorage,
2034 ULONG indexOfPropertyToDelete,
2035 StgProperty propertyToDelete)
2037 IStream *pis;
2038 HRESULT hr;
2039 ULARGE_INTEGER size;
2041 size.u.HighPart = 0;
2042 size.u.LowPart = 0;
2044 hr = StorageBaseImpl_OpenStream(
2045 (IStorage*)parentStorage,
2046 (OLECHAR*)propertyToDelete.name,
2047 NULL,
2048 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2050 &pis);
2052 if (hr!=S_OK)
2054 return(hr);
2058 * Zap the stream
2060 hr = IStream_SetSize(pis, size);
2062 if(hr != S_OK)
2064 return hr;
2068 * Release the stream object.
2070 IStream_Release(pis);
2073 * Invalidate the property by zeroing its name member.
2075 propertyToDelete.sizeOfNameString = 0;
2078 * Here we should re-read the property so we get the updated pointer
2079 * but since we are here to zap it, I don't do it...
2081 StorageImpl_WriteProperty(
2082 parentStorage->base.ancestorStorage,
2083 indexOfPropertyToDelete,
2084 &propertyToDelete);
2086 return S_OK;
2089 /*********************************************************************
2091 * Internal Method
2093 * Finds a placeholder for the StgProperty within the Storage
2096 static HRESULT findPlaceholder(
2097 StorageImpl *storage,
2098 ULONG propertyIndexToStore,
2099 ULONG storePropertyIndex,
2100 INT typeOfRelation)
2102 StgProperty storeProperty;
2103 HRESULT hr = S_OK;
2104 BOOL res = TRUE;
2107 * Read the storage property
2109 res = StorageImpl_ReadProperty(
2110 storage->base.ancestorStorage,
2111 storePropertyIndex,
2112 &storeProperty);
2114 if(! res)
2116 return E_FAIL;
2119 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2121 if (storeProperty.previousProperty != PROPERTY_NULL)
2123 return findPlaceholder(
2124 storage,
2125 propertyIndexToStore,
2126 storeProperty.previousProperty,
2127 typeOfRelation);
2129 else
2131 storeProperty.previousProperty = propertyIndexToStore;
2134 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2136 if (storeProperty.nextProperty != PROPERTY_NULL)
2138 return findPlaceholder(
2139 storage,
2140 propertyIndexToStore,
2141 storeProperty.nextProperty,
2142 typeOfRelation);
2144 else
2146 storeProperty.nextProperty = propertyIndexToStore;
2149 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2151 if (storeProperty.dirProperty != PROPERTY_NULL)
2153 return findPlaceholder(
2154 storage,
2155 propertyIndexToStore,
2156 storeProperty.dirProperty,
2157 typeOfRelation);
2159 else
2161 storeProperty.dirProperty = propertyIndexToStore;
2165 hr = StorageImpl_WriteProperty(
2166 storage->base.ancestorStorage,
2167 storePropertyIndex,
2168 &storeProperty);
2170 if(! hr)
2172 return E_FAIL;
2175 return S_OK;
2178 /*************************************************************************
2180 * Internal Method
2182 * This method takes the previous and the next property link of a property
2183 * to be deleted and find them a place in the Storage.
2185 static HRESULT adjustPropertyChain(
2186 StorageImpl *This,
2187 StgProperty propertyToDelete,
2188 StgProperty parentProperty,
2189 ULONG parentPropertyId,
2190 INT typeOfRelation)
2192 ULONG newLinkProperty = PROPERTY_NULL;
2193 BOOL needToFindAPlaceholder = FALSE;
2194 ULONG storeNode = PROPERTY_NULL;
2195 ULONG toStoreNode = PROPERTY_NULL;
2196 INT relationType = 0;
2197 HRESULT hr = S_OK;
2198 BOOL res = TRUE;
2200 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2202 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2205 * Set the parent previous to the property to delete previous
2207 newLinkProperty = propertyToDelete.previousProperty;
2209 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2212 * We also need to find a storage for the other link, setup variables
2213 * to do this at the end...
2215 needToFindAPlaceholder = TRUE;
2216 storeNode = propertyToDelete.previousProperty;
2217 toStoreNode = propertyToDelete.nextProperty;
2218 relationType = PROPERTY_RELATION_NEXT;
2221 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2224 * Set the parent previous to the property to delete next
2226 newLinkProperty = propertyToDelete.nextProperty;
2230 * Link it for real...
2232 parentProperty.previousProperty = newLinkProperty;
2235 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2237 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2240 * Set the parent next to the property to delete next previous
2242 newLinkProperty = propertyToDelete.previousProperty;
2244 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2247 * We also need to find a storage for the other link, setup variables
2248 * to do this at the end...
2250 needToFindAPlaceholder = TRUE;
2251 storeNode = propertyToDelete.previousProperty;
2252 toStoreNode = propertyToDelete.nextProperty;
2253 relationType = PROPERTY_RELATION_NEXT;
2256 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2259 * Set the parent next to the property to delete next
2261 newLinkProperty = propertyToDelete.nextProperty;
2265 * Link it for real...
2267 parentProperty.nextProperty = newLinkProperty;
2269 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2271 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2274 * Set the parent dir to the property to delete previous
2276 newLinkProperty = propertyToDelete.previousProperty;
2278 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2281 * We also need to find a storage for the other link, setup variables
2282 * to do this at the end...
2284 needToFindAPlaceholder = TRUE;
2285 storeNode = propertyToDelete.previousProperty;
2286 toStoreNode = propertyToDelete.nextProperty;
2287 relationType = PROPERTY_RELATION_NEXT;
2290 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2293 * Set the parent dir to the property to delete next
2295 newLinkProperty = propertyToDelete.nextProperty;
2299 * Link it for real...
2301 parentProperty.dirProperty = newLinkProperty;
2305 * Write back the parent property
2307 res = StorageImpl_WriteProperty(
2308 This->base.ancestorStorage,
2309 parentPropertyId,
2310 &parentProperty);
2311 if(! res)
2313 return E_FAIL;
2317 * If a placeholder is required for the other link, then, find one and
2318 * get out of here...
2320 if (needToFindAPlaceholder)
2322 hr = findPlaceholder(
2323 This,
2324 toStoreNode,
2325 storeNode,
2326 relationType);
2329 return hr;
2333 /******************************************************************************
2334 * SetElementTimes (IStorage)
2336 static HRESULT WINAPI StorageImpl_SetElementTimes(
2337 IStorage* iface,
2338 const OLECHAR *pwcsName,/* [string][in] */
2339 const FILETIME *pctime, /* [in] */
2340 const FILETIME *patime, /* [in] */
2341 const FILETIME *pmtime) /* [in] */
2343 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2344 return S_OK;
2347 /******************************************************************************
2348 * SetStateBits (IStorage)
2350 static HRESULT WINAPI StorageImpl_SetStateBits(
2351 IStorage* iface,
2352 DWORD grfStateBits,/* [in] */
2353 DWORD grfMask) /* [in] */
2355 StorageImpl* const This = (StorageImpl*)iface;
2356 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2357 return S_OK;
2361 * Virtual function table for the IStorage32Impl class.
2363 static const IStorageVtbl Storage32Impl_Vtbl =
2365 StorageBaseImpl_QueryInterface,
2366 StorageBaseImpl_AddRef,
2367 StorageBaseImpl_Release,
2368 StorageBaseImpl_CreateStream,
2369 StorageBaseImpl_OpenStream,
2370 StorageImpl_CreateStorage,
2371 StorageBaseImpl_OpenStorage,
2372 StorageImpl_CopyTo,
2373 StorageImpl_MoveElementTo,
2374 StorageImpl_Commit,
2375 StorageImpl_Revert,
2376 StorageBaseImpl_EnumElements,
2377 StorageImpl_DestroyElement,
2378 StorageBaseImpl_RenameElement,
2379 StorageImpl_SetElementTimes,
2380 StorageBaseImpl_SetClass,
2381 StorageImpl_SetStateBits,
2382 StorageImpl_Stat
2385 static HRESULT StorageImpl_Construct(
2386 StorageImpl* This,
2387 HANDLE hFile,
2388 LPCOLESTR pwcsName,
2389 ILockBytes* pLkbyt,
2390 DWORD openFlags,
2391 BOOL fileBased,
2392 BOOL fileCreate)
2394 HRESULT hr = S_OK;
2395 StgProperty currentProperty;
2396 BOOL readSuccessful;
2397 ULONG currentPropertyIndex;
2399 if ( FAILED( validateSTGM(openFlags) ))
2400 return STG_E_INVALIDFLAG;
2402 memset(This, 0, sizeof(StorageImpl));
2405 * Initialize stream list
2408 list_init(&This->base.strmHead);
2411 * Initialize the virtual function table.
2413 This->base.lpVtbl = &Storage32Impl_Vtbl;
2414 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2415 This->base.v_destructor = StorageImpl_Destroy;
2416 This->base.openFlags = (openFlags & ~STGM_CREATE);
2419 * This is the top-level storage so initialize the ancestor pointer
2420 * to this.
2422 This->base.ancestorStorage = This;
2425 * Initialize the physical support of the storage.
2427 This->hFile = hFile;
2430 * Store copy of file path.
2432 if(pwcsName) {
2433 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2434 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2435 if (!This->pwcsName)
2436 return STG_E_INSUFFICIENTMEMORY;
2437 strcpyW(This->pwcsName, pwcsName);
2441 * Initialize the big block cache.
2443 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2444 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2445 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2446 pLkbyt,
2447 openFlags,
2448 This->bigBlockSize,
2449 fileBased);
2451 if (This->bigBlockFile == 0)
2452 return E_FAIL;
2454 if (fileCreate)
2456 ULARGE_INTEGER size;
2457 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2460 * Initialize all header variables:
2461 * - The big block depot consists of one block and it is at block 0
2462 * - The properties start at block 1
2463 * - There is no small block depot
2465 memset( This->bigBlockDepotStart,
2466 BLOCK_UNUSED,
2467 sizeof(This->bigBlockDepotStart));
2469 This->bigBlockDepotCount = 1;
2470 This->bigBlockDepotStart[0] = 0;
2471 This->rootStartBlock = 1;
2472 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2473 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2474 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2475 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2476 This->extBigBlockDepotCount = 0;
2478 StorageImpl_SaveFileHeader(This);
2481 * Add one block for the big block depot and one block for the properties
2483 size.u.HighPart = 0;
2484 size.u.LowPart = This->bigBlockSize * 3;
2485 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2488 * Initialize the big block depot
2490 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2491 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2492 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2493 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2495 else
2498 * Load the header for the file.
2500 hr = StorageImpl_LoadFileHeader(This);
2502 if (FAILED(hr))
2504 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2506 return hr;
2511 * There is no block depot cached yet.
2513 This->indexBlockDepotCached = 0xFFFFFFFF;
2516 * Start searching for free blocks with block 0.
2518 This->prevFreeBlock = 0;
2521 * Create the block chain abstractions.
2523 if(!(This->rootBlockChain =
2524 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2525 return STG_E_READFAULT;
2527 if(!(This->smallBlockDepotChain =
2528 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2529 PROPERTY_NULL)))
2530 return STG_E_READFAULT;
2533 * Write the root property (memory only)
2535 if (fileCreate)
2537 StgProperty rootProp;
2539 * Initialize the property chain
2541 memset(&rootProp, 0, sizeof(rootProp));
2542 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2543 sizeof(rootProp.name)/sizeof(WCHAR) );
2544 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2545 rootProp.propertyType = PROPTYPE_ROOT;
2546 rootProp.previousProperty = PROPERTY_NULL;
2547 rootProp.nextProperty = PROPERTY_NULL;
2548 rootProp.dirProperty = PROPERTY_NULL;
2549 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2550 rootProp.size.u.HighPart = 0;
2551 rootProp.size.u.LowPart = 0;
2553 StorageImpl_WriteProperty(This, 0, &rootProp);
2557 * Find the ID of the root in the property sets.
2559 currentPropertyIndex = 0;
2563 readSuccessful = StorageImpl_ReadProperty(
2564 This,
2565 currentPropertyIndex,
2566 &currentProperty);
2568 if (readSuccessful)
2570 if ( (currentProperty.sizeOfNameString != 0 ) &&
2571 (currentProperty.propertyType == PROPTYPE_ROOT) )
2573 This->base.rootPropertySetIndex = currentPropertyIndex;
2577 currentPropertyIndex++;
2579 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2581 if (!readSuccessful)
2583 /* TODO CLEANUP */
2584 return STG_E_READFAULT;
2588 * Create the block chain abstraction for the small block root chain.
2590 if(!(This->smallBlockRootChain =
2591 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2592 return STG_E_READFAULT;
2594 return hr;
2597 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2599 StorageImpl *This = (StorageImpl*) iface;
2600 TRACE("(%p)\n", This);
2602 StorageBaseImpl_DeleteAll(&This->base);
2604 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2606 BlockChainStream_Destroy(This->smallBlockRootChain);
2607 BlockChainStream_Destroy(This->rootBlockChain);
2608 BlockChainStream_Destroy(This->smallBlockDepotChain);
2610 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2611 HeapFree(GetProcessHeap(), 0, This);
2614 /******************************************************************************
2615 * Storage32Impl_GetNextFreeBigBlock
2617 * Returns the index of the next free big block.
2618 * If the big block depot is filled, this method will enlarge it.
2621 static ULONG StorageImpl_GetNextFreeBigBlock(
2622 StorageImpl* This)
2624 ULONG depotBlockIndexPos;
2625 BYTE depotBuffer[BIG_BLOCK_SIZE];
2626 BOOL success;
2627 ULONG depotBlockOffset;
2628 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2629 ULONG nextBlockIndex = BLOCK_SPECIAL;
2630 int depotIndex = 0;
2631 ULONG freeBlock = BLOCK_UNUSED;
2633 depotIndex = This->prevFreeBlock / blocksPerDepot;
2634 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2637 * Scan the entire big block depot until we find a block marked free
2639 while (nextBlockIndex != BLOCK_UNUSED)
2641 if (depotIndex < COUNT_BBDEPOTINHEADER)
2643 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2646 * Grow the primary depot.
2648 if (depotBlockIndexPos == BLOCK_UNUSED)
2650 depotBlockIndexPos = depotIndex*blocksPerDepot;
2653 * Add a block depot.
2655 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2656 This->bigBlockDepotCount++;
2657 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2660 * Flag it as a block depot.
2662 StorageImpl_SetNextBlockInChain(This,
2663 depotBlockIndexPos,
2664 BLOCK_SPECIAL);
2666 /* Save new header information.
2668 StorageImpl_SaveFileHeader(This);
2671 else
2673 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2675 if (depotBlockIndexPos == BLOCK_UNUSED)
2678 * Grow the extended depot.
2680 ULONG extIndex = BLOCK_UNUSED;
2681 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2682 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2684 if (extBlockOffset == 0)
2686 /* We need an extended block.
2688 extIndex = Storage32Impl_AddExtBlockDepot(This);
2689 This->extBigBlockDepotCount++;
2690 depotBlockIndexPos = extIndex + 1;
2692 else
2693 depotBlockIndexPos = depotIndex * blocksPerDepot;
2696 * Add a block depot and mark it in the extended block.
2698 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2699 This->bigBlockDepotCount++;
2700 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2702 /* Flag the block depot.
2704 StorageImpl_SetNextBlockInChain(This,
2705 depotBlockIndexPos,
2706 BLOCK_SPECIAL);
2708 /* If necessary, flag the extended depot block.
2710 if (extIndex != BLOCK_UNUSED)
2711 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2713 /* Save header information.
2715 StorageImpl_SaveFileHeader(This);
2719 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2721 if (success)
2723 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2724 ( nextBlockIndex != BLOCK_UNUSED))
2726 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2728 if (nextBlockIndex == BLOCK_UNUSED)
2730 freeBlock = (depotIndex * blocksPerDepot) +
2731 (depotBlockOffset/sizeof(ULONG));
2734 depotBlockOffset += sizeof(ULONG);
2738 depotIndex++;
2739 depotBlockOffset = 0;
2743 * make sure that the block physically exists before using it
2745 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2747 This->prevFreeBlock = freeBlock;
2749 return freeBlock;
2752 /******************************************************************************
2753 * Storage32Impl_AddBlockDepot
2755 * This will create a depot block, essentially it is a block initialized
2756 * to BLOCK_UNUSEDs.
2758 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2760 BYTE blockBuffer[BIG_BLOCK_SIZE];
2763 * Initialize blocks as free
2765 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2766 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2769 /******************************************************************************
2770 * Storage32Impl_GetExtDepotBlock
2772 * Returns the index of the block that corresponds to the specified depot
2773 * index. This method is only for depot indexes equal or greater than
2774 * COUNT_BBDEPOTINHEADER.
2776 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2778 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2779 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2780 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2781 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2782 ULONG blockIndex = BLOCK_UNUSED;
2783 ULONG extBlockIndex = This->extBigBlockDepotStart;
2785 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2787 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2788 return BLOCK_UNUSED;
2790 while (extBlockCount > 0)
2792 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2793 extBlockCount--;
2796 if (extBlockIndex != BLOCK_UNUSED)
2797 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2798 extBlockOffset * sizeof(ULONG), &blockIndex);
2800 return blockIndex;
2803 /******************************************************************************
2804 * Storage32Impl_SetExtDepotBlock
2806 * Associates the specified block index to the specified depot index.
2807 * This method is only for depot indexes equal or greater than
2808 * COUNT_BBDEPOTINHEADER.
2810 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2812 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2813 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2814 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2815 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2816 ULONG extBlockIndex = This->extBigBlockDepotStart;
2818 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2820 while (extBlockCount > 0)
2822 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2823 extBlockCount--;
2826 if (extBlockIndex != BLOCK_UNUSED)
2828 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2829 extBlockOffset * sizeof(ULONG),
2830 blockIndex);
2834 /******************************************************************************
2835 * Storage32Impl_AddExtBlockDepot
2837 * Creates an extended depot block.
2839 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2841 ULONG numExtBlocks = This->extBigBlockDepotCount;
2842 ULONG nextExtBlock = This->extBigBlockDepotStart;
2843 BYTE depotBuffer[BIG_BLOCK_SIZE];
2844 ULONG index = BLOCK_UNUSED;
2845 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2846 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2847 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2849 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2850 blocksPerDepotBlock;
2852 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2855 * The first extended block.
2857 This->extBigBlockDepotStart = index;
2859 else
2861 unsigned int i;
2863 * Follow the chain to the last one.
2865 for (i = 0; i < (numExtBlocks - 1); i++)
2867 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2871 * Add the new extended block to the chain.
2873 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2874 index);
2878 * Initialize this block.
2880 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2881 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2883 return index;
2886 /******************************************************************************
2887 * Storage32Impl_FreeBigBlock
2889 * This method will flag the specified block as free in the big block depot.
2891 static void StorageImpl_FreeBigBlock(
2892 StorageImpl* This,
2893 ULONG blockIndex)
2895 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2897 if (blockIndex < This->prevFreeBlock)
2898 This->prevFreeBlock = blockIndex;
2901 /************************************************************************
2902 * Storage32Impl_GetNextBlockInChain
2904 * This method will retrieve the block index of the next big block in
2905 * in the chain.
2907 * Params: This - Pointer to the Storage object.
2908 * blockIndex - Index of the block to retrieve the chain
2909 * for.
2910 * nextBlockIndex - receives the return value.
2912 * Returns: This method returns the index of the next block in the chain.
2913 * It will return the constants:
2914 * BLOCK_SPECIAL - If the block given was not part of a
2915 * chain.
2916 * BLOCK_END_OF_CHAIN - If the block given was the last in
2917 * a chain.
2918 * BLOCK_UNUSED - If the block given was not past of a chain
2919 * and is available.
2920 * BLOCK_EXTBBDEPOT - This block is part of the extended
2921 * big block depot.
2923 * See Windows documentation for more details on IStorage methods.
2925 static HRESULT StorageImpl_GetNextBlockInChain(
2926 StorageImpl* This,
2927 ULONG blockIndex,
2928 ULONG* nextBlockIndex)
2930 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2931 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2932 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2933 BYTE depotBuffer[BIG_BLOCK_SIZE];
2934 BOOL success;
2935 ULONG depotBlockIndexPos;
2936 int index;
2938 *nextBlockIndex = BLOCK_SPECIAL;
2940 if(depotBlockCount >= This->bigBlockDepotCount)
2942 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2943 This->bigBlockDepotCount);
2944 return STG_E_READFAULT;
2948 * Cache the currently accessed depot block.
2950 if (depotBlockCount != This->indexBlockDepotCached)
2952 This->indexBlockDepotCached = depotBlockCount;
2954 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2956 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2958 else
2961 * We have to look in the extended depot.
2963 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2966 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2968 if (!success)
2969 return STG_E_READFAULT;
2971 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2973 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2974 This->blockDepotCached[index] = *nextBlockIndex;
2978 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2980 return S_OK;
2983 /******************************************************************************
2984 * Storage32Impl_GetNextExtendedBlock
2986 * Given an extended block this method will return the next extended block.
2988 * NOTES:
2989 * The last ULONG of an extended block is the block index of the next
2990 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2991 * depot.
2993 * Return values:
2994 * - The index of the next extended block
2995 * - BLOCK_UNUSED: there is no next extended block.
2996 * - Any other return values denotes failure.
2998 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3000 ULONG nextBlockIndex = BLOCK_SPECIAL;
3001 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3003 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3004 &nextBlockIndex);
3006 return nextBlockIndex;
3009 /******************************************************************************
3010 * Storage32Impl_SetNextBlockInChain
3012 * This method will write the index of the specified block's next block
3013 * in the big block depot.
3015 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3016 * do the following
3018 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3019 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3020 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3023 static void StorageImpl_SetNextBlockInChain(
3024 StorageImpl* This,
3025 ULONG blockIndex,
3026 ULONG nextBlock)
3028 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3029 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3030 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3031 ULONG depotBlockIndexPos;
3033 assert(depotBlockCount < This->bigBlockDepotCount);
3034 assert(blockIndex != nextBlock);
3036 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3038 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3040 else
3043 * We have to look in the extended depot.
3045 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3048 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3049 nextBlock);
3051 * Update the cached block depot, if necessary.
3053 if (depotBlockCount == This->indexBlockDepotCached)
3055 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3059 /******************************************************************************
3060 * Storage32Impl_LoadFileHeader
3062 * This method will read in the file header, i.e. big block index -1.
3064 static HRESULT StorageImpl_LoadFileHeader(
3065 StorageImpl* This)
3067 HRESULT hr = STG_E_FILENOTFOUND;
3068 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3069 BOOL success;
3070 int index;
3072 TRACE("\n");
3074 * Get a pointer to the big block of data containing the header.
3076 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3079 * Extract the information from the header.
3081 if (success)
3084 * Check for the "magic number" signature and return an error if it is not
3085 * found.
3087 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3089 return STG_E_OLDFORMAT;
3092 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3094 return STG_E_INVALIDHEADER;
3097 StorageUtl_ReadWord(
3098 headerBigBlock,
3099 OFFSET_BIGBLOCKSIZEBITS,
3100 &This->bigBlockSizeBits);
3102 StorageUtl_ReadWord(
3103 headerBigBlock,
3104 OFFSET_SMALLBLOCKSIZEBITS,
3105 &This->smallBlockSizeBits);
3107 StorageUtl_ReadDWord(
3108 headerBigBlock,
3109 OFFSET_BBDEPOTCOUNT,
3110 &This->bigBlockDepotCount);
3112 StorageUtl_ReadDWord(
3113 headerBigBlock,
3114 OFFSET_ROOTSTARTBLOCK,
3115 &This->rootStartBlock);
3117 StorageUtl_ReadDWord(
3118 headerBigBlock,
3119 OFFSET_SBDEPOTSTART,
3120 &This->smallBlockDepotStart);
3122 StorageUtl_ReadDWord(
3123 headerBigBlock,
3124 OFFSET_EXTBBDEPOTSTART,
3125 &This->extBigBlockDepotStart);
3127 StorageUtl_ReadDWord(
3128 headerBigBlock,
3129 OFFSET_EXTBBDEPOTCOUNT,
3130 &This->extBigBlockDepotCount);
3132 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3134 StorageUtl_ReadDWord(
3135 headerBigBlock,
3136 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3137 &(This->bigBlockDepotStart[index]));
3141 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3143 if ((1 << 2) == 4)
3145 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3146 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3148 else
3150 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3151 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3155 * Right now, the code is making some assumptions about the size of the
3156 * blocks, just make sure they are what we're expecting.
3158 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3159 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3161 WARN("Broken OLE storage file\n");
3162 hr = STG_E_INVALIDHEADER;
3164 else
3165 hr = S_OK;
3168 return hr;
3171 /******************************************************************************
3172 * Storage32Impl_SaveFileHeader
3174 * This method will save to the file the header, i.e. big block -1.
3176 static void StorageImpl_SaveFileHeader(
3177 StorageImpl* This)
3179 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3180 int index;
3181 BOOL success;
3184 * Get a pointer to the big block of data containing the header.
3186 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3189 * If the block read failed, the file is probably new.
3191 if (!success)
3194 * Initialize for all unknown fields.
3196 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3199 * Initialize the magic number.
3201 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3204 * And a bunch of things we don't know what they mean
3206 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3207 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3208 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3209 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3213 * Write the information to the header.
3215 StorageUtl_WriteWord(
3216 headerBigBlock,
3217 OFFSET_BIGBLOCKSIZEBITS,
3218 This->bigBlockSizeBits);
3220 StorageUtl_WriteWord(
3221 headerBigBlock,
3222 OFFSET_SMALLBLOCKSIZEBITS,
3223 This->smallBlockSizeBits);
3225 StorageUtl_WriteDWord(
3226 headerBigBlock,
3227 OFFSET_BBDEPOTCOUNT,
3228 This->bigBlockDepotCount);
3230 StorageUtl_WriteDWord(
3231 headerBigBlock,
3232 OFFSET_ROOTSTARTBLOCK,
3233 This->rootStartBlock);
3235 StorageUtl_WriteDWord(
3236 headerBigBlock,
3237 OFFSET_SBDEPOTSTART,
3238 This->smallBlockDepotStart);
3240 StorageUtl_WriteDWord(
3241 headerBigBlock,
3242 OFFSET_SBDEPOTCOUNT,
3243 This->smallBlockDepotChain ?
3244 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3246 StorageUtl_WriteDWord(
3247 headerBigBlock,
3248 OFFSET_EXTBBDEPOTSTART,
3249 This->extBigBlockDepotStart);
3251 StorageUtl_WriteDWord(
3252 headerBigBlock,
3253 OFFSET_EXTBBDEPOTCOUNT,
3254 This->extBigBlockDepotCount);
3256 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3258 StorageUtl_WriteDWord(
3259 headerBigBlock,
3260 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3261 (This->bigBlockDepotStart[index]));
3265 * Write the big block back to the file.
3267 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3270 /******************************************************************************
3271 * Storage32Impl_ReadProperty
3273 * This method will read the specified property from the property chain.
3275 BOOL StorageImpl_ReadProperty(
3276 StorageImpl* This,
3277 ULONG index,
3278 StgProperty* buffer)
3280 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3281 ULARGE_INTEGER offsetInPropSet;
3282 HRESULT readRes;
3283 ULONG bytesRead;
3285 offsetInPropSet.u.HighPart = 0;
3286 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3288 readRes = BlockChainStream_ReadAt(
3289 This->rootBlockChain,
3290 offsetInPropSet,
3291 PROPSET_BLOCK_SIZE,
3292 currentProperty,
3293 &bytesRead);
3295 if (SUCCEEDED(readRes))
3297 /* replace the name of root entry (often "Root Entry") by the file name */
3298 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3299 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3301 memset(buffer->name, 0, sizeof(buffer->name));
3302 memcpy(
3303 buffer->name,
3304 propName,
3305 PROPERTY_NAME_BUFFER_LEN );
3306 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3308 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3310 StorageUtl_ReadWord(
3311 currentProperty,
3312 OFFSET_PS_NAMELENGTH,
3313 &buffer->sizeOfNameString);
3315 StorageUtl_ReadDWord(
3316 currentProperty,
3317 OFFSET_PS_PREVIOUSPROP,
3318 &buffer->previousProperty);
3320 StorageUtl_ReadDWord(
3321 currentProperty,
3322 OFFSET_PS_NEXTPROP,
3323 &buffer->nextProperty);
3325 StorageUtl_ReadDWord(
3326 currentProperty,
3327 OFFSET_PS_DIRPROP,
3328 &buffer->dirProperty);
3330 StorageUtl_ReadGUID(
3331 currentProperty,
3332 OFFSET_PS_GUID,
3333 &buffer->propertyUniqueID);
3335 StorageUtl_ReadDWord(
3336 currentProperty,
3337 OFFSET_PS_TSS1,
3338 &buffer->timeStampS1);
3340 StorageUtl_ReadDWord(
3341 currentProperty,
3342 OFFSET_PS_TSD1,
3343 &buffer->timeStampD1);
3345 StorageUtl_ReadDWord(
3346 currentProperty,
3347 OFFSET_PS_TSS2,
3348 &buffer->timeStampS2);
3350 StorageUtl_ReadDWord(
3351 currentProperty,
3352 OFFSET_PS_TSD2,
3353 &buffer->timeStampD2);
3355 StorageUtl_ReadDWord(
3356 currentProperty,
3357 OFFSET_PS_STARTBLOCK,
3358 &buffer->startingBlock);
3360 StorageUtl_ReadDWord(
3361 currentProperty,
3362 OFFSET_PS_SIZE,
3363 &buffer->size.u.LowPart);
3365 buffer->size.u.HighPart = 0;
3368 return SUCCEEDED(readRes) ? TRUE : FALSE;
3371 /*********************************************************************
3372 * Write the specified property into the property chain
3374 BOOL StorageImpl_WriteProperty(
3375 StorageImpl* This,
3376 ULONG index,
3377 const StgProperty* buffer)
3379 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3380 ULARGE_INTEGER offsetInPropSet;
3381 HRESULT writeRes;
3382 ULONG bytesWritten;
3384 offsetInPropSet.u.HighPart = 0;
3385 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3387 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3389 memcpy(
3390 currentProperty + OFFSET_PS_NAME,
3391 buffer->name,
3392 PROPERTY_NAME_BUFFER_LEN );
3394 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3396 StorageUtl_WriteWord(
3397 currentProperty,
3398 OFFSET_PS_NAMELENGTH,
3399 buffer->sizeOfNameString);
3401 StorageUtl_WriteDWord(
3402 currentProperty,
3403 OFFSET_PS_PREVIOUSPROP,
3404 buffer->previousProperty);
3406 StorageUtl_WriteDWord(
3407 currentProperty,
3408 OFFSET_PS_NEXTPROP,
3409 buffer->nextProperty);
3411 StorageUtl_WriteDWord(
3412 currentProperty,
3413 OFFSET_PS_DIRPROP,
3414 buffer->dirProperty);
3416 StorageUtl_WriteGUID(
3417 currentProperty,
3418 OFFSET_PS_GUID,
3419 &buffer->propertyUniqueID);
3421 StorageUtl_WriteDWord(
3422 currentProperty,
3423 OFFSET_PS_TSS1,
3424 buffer->timeStampS1);
3426 StorageUtl_WriteDWord(
3427 currentProperty,
3428 OFFSET_PS_TSD1,
3429 buffer->timeStampD1);
3431 StorageUtl_WriteDWord(
3432 currentProperty,
3433 OFFSET_PS_TSS2,
3434 buffer->timeStampS2);
3436 StorageUtl_WriteDWord(
3437 currentProperty,
3438 OFFSET_PS_TSD2,
3439 buffer->timeStampD2);
3441 StorageUtl_WriteDWord(
3442 currentProperty,
3443 OFFSET_PS_STARTBLOCK,
3444 buffer->startingBlock);
3446 StorageUtl_WriteDWord(
3447 currentProperty,
3448 OFFSET_PS_SIZE,
3449 buffer->size.u.LowPart);
3451 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3452 offsetInPropSet,
3453 PROPSET_BLOCK_SIZE,
3454 currentProperty,
3455 &bytesWritten);
3456 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3459 static BOOL StorageImpl_ReadBigBlock(
3460 StorageImpl* This,
3461 ULONG blockIndex,
3462 void* buffer)
3464 ULARGE_INTEGER ulOffset;
3465 DWORD read;
3467 ulOffset.u.HighPart = 0;
3468 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3470 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3471 return (read == This->bigBlockSize);
3474 static BOOL StorageImpl_ReadDWordFromBigBlock(
3475 StorageImpl* This,
3476 ULONG blockIndex,
3477 ULONG offset,
3478 DWORD* value)
3480 ULARGE_INTEGER ulOffset;
3481 DWORD read;
3482 DWORD tmp;
3484 ulOffset.u.HighPart = 0;
3485 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3486 ulOffset.u.LowPart += offset;
3488 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3489 *value = le32toh(tmp);
3490 return (read == sizeof(DWORD));
3493 static BOOL StorageImpl_WriteBigBlock(
3494 StorageImpl* This,
3495 ULONG blockIndex,
3496 const void* buffer)
3498 ULARGE_INTEGER ulOffset;
3499 DWORD wrote;
3501 ulOffset.u.HighPart = 0;
3502 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3504 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3505 return (wrote == This->bigBlockSize);
3508 static BOOL StorageImpl_WriteDWordToBigBlock(
3509 StorageImpl* This,
3510 ULONG blockIndex,
3511 ULONG offset,
3512 DWORD value)
3514 ULARGE_INTEGER ulOffset;
3515 DWORD wrote;
3517 ulOffset.u.HighPart = 0;
3518 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3519 ulOffset.u.LowPart += offset;
3521 value = htole32(value);
3522 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3523 return (wrote == sizeof(DWORD));
3526 /******************************************************************************
3527 * Storage32Impl_SmallBlocksToBigBlocks
3529 * This method will convert a small block chain to a big block chain.
3530 * The small block chain will be destroyed.
3532 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3533 StorageImpl* This,
3534 SmallBlockChainStream** ppsbChain)
3536 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3537 ULARGE_INTEGER size, offset;
3538 ULONG cbRead, cbWritten;
3539 ULARGE_INTEGER cbTotalRead;
3540 ULONG propertyIndex;
3541 HRESULT resWrite = S_OK;
3542 HRESULT resRead;
3543 StgProperty chainProperty;
3544 BYTE *buffer;
3545 BlockChainStream *bbTempChain = NULL;
3546 BlockChainStream *bigBlockChain = NULL;
3549 * Create a temporary big block chain that doesn't have
3550 * an associated property. This temporary chain will be
3551 * used to copy data from small blocks to big blocks.
3553 bbTempChain = BlockChainStream_Construct(This,
3554 &bbHeadOfChain,
3555 PROPERTY_NULL);
3556 if(!bbTempChain) return NULL;
3558 * Grow the big block chain.
3560 size = SmallBlockChainStream_GetSize(*ppsbChain);
3561 BlockChainStream_SetSize(bbTempChain, size);
3564 * Copy the contents of the small block chain to the big block chain
3565 * by small block size increments.
3567 offset.u.LowPart = 0;
3568 offset.u.HighPart = 0;
3569 cbTotalRead.QuadPart = 0;
3571 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3574 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3575 offset,
3576 This->smallBlockSize,
3577 buffer,
3578 &cbRead);
3579 if (FAILED(resRead))
3580 break;
3582 if (cbRead > 0)
3584 cbTotalRead.QuadPart += cbRead;
3586 resWrite = BlockChainStream_WriteAt(bbTempChain,
3587 offset,
3588 cbRead,
3589 buffer,
3590 &cbWritten);
3592 if (FAILED(resWrite))
3593 break;
3595 offset.u.LowPart += This->smallBlockSize;
3597 } while (cbTotalRead.QuadPart < size.QuadPart);
3598 HeapFree(GetProcessHeap(),0,buffer);
3600 if (FAILED(resRead) || FAILED(resWrite))
3602 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3603 BlockChainStream_Destroy(bbTempChain);
3604 return NULL;
3608 * Destroy the small block chain.
3610 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3611 size.u.HighPart = 0;
3612 size.u.LowPart = 0;
3613 SmallBlockChainStream_SetSize(*ppsbChain, size);
3614 SmallBlockChainStream_Destroy(*ppsbChain);
3615 *ppsbChain = 0;
3618 * Change the property information. This chain is now a big block chain
3619 * and it doesn't reside in the small blocks chain anymore.
3621 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3623 chainProperty.startingBlock = bbHeadOfChain;
3625 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3628 * Destroy the temporary propertyless big block chain.
3629 * Create a new big block chain associated with this property.
3631 BlockChainStream_Destroy(bbTempChain);
3632 bigBlockChain = BlockChainStream_Construct(This,
3633 NULL,
3634 propertyIndex);
3636 return bigBlockChain;
3639 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3641 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3643 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3644 HeapFree(GetProcessHeap(), 0, This);
3647 /******************************************************************************
3649 ** Storage32InternalImpl_Commit
3651 ** The non-root storages cannot be opened in transacted mode thus this function
3652 ** does nothing.
3654 static HRESULT WINAPI StorageInternalImpl_Commit(
3655 IStorage* iface,
3656 DWORD grfCommitFlags) /* [in] */
3658 return S_OK;
3661 /******************************************************************************
3663 ** Storage32InternalImpl_Revert
3665 ** The non-root storages cannot be opened in transacted mode thus this function
3666 ** does nothing.
3668 static HRESULT WINAPI StorageInternalImpl_Revert(
3669 IStorage* iface)
3671 return S_OK;
3674 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3676 IStorage_Release((IStorage*)This->parentStorage);
3677 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3678 HeapFree(GetProcessHeap(), 0, This);
3681 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3682 IEnumSTATSTG* iface,
3683 REFIID riid,
3684 void** ppvObject)
3686 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3689 * Perform a sanity check on the parameters.
3691 if (ppvObject==0)
3692 return E_INVALIDARG;
3695 * Initialize the return parameter.
3697 *ppvObject = 0;
3700 * Compare the riid with the interface IDs implemented by this object.
3702 if (IsEqualGUID(&IID_IUnknown, riid) ||
3703 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3705 *ppvObject = (IEnumSTATSTG*)This;
3706 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3707 return S_OK;
3710 return E_NOINTERFACE;
3713 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3714 IEnumSTATSTG* iface)
3716 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3717 return InterlockedIncrement(&This->ref);
3720 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3721 IEnumSTATSTG* iface)
3723 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3725 ULONG newRef;
3727 newRef = InterlockedDecrement(&This->ref);
3730 * If the reference count goes down to 0, perform suicide.
3732 if (newRef==0)
3734 IEnumSTATSTGImpl_Destroy(This);
3737 return newRef;
3740 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3741 IEnumSTATSTG* iface,
3742 ULONG celt,
3743 STATSTG* rgelt,
3744 ULONG* pceltFetched)
3746 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3748 StgProperty currentProperty;
3749 STATSTG* currentReturnStruct = rgelt;
3750 ULONG objectFetched = 0;
3751 ULONG currentSearchNode;
3754 * Perform a sanity check on the parameters.
3756 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3757 return E_INVALIDARG;
3760 * To avoid the special case, get another pointer to a ULONG value if
3761 * the caller didn't supply one.
3763 if (pceltFetched==0)
3764 pceltFetched = &objectFetched;
3767 * Start the iteration, we will iterate until we hit the end of the
3768 * linked list or until we hit the number of items to iterate through
3770 *pceltFetched = 0;
3773 * Start with the node at the top of the stack.
3775 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3777 while ( ( *pceltFetched < celt) &&
3778 ( currentSearchNode!=PROPERTY_NULL) )
3781 * Remove the top node from the stack
3783 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3786 * Read the property from the storage.
3788 StorageImpl_ReadProperty(This->parentStorage,
3789 currentSearchNode,
3790 &currentProperty);
3793 * Copy the information to the return buffer.
3795 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3796 &currentProperty,
3797 STATFLAG_DEFAULT);
3800 * Step to the next item in the iteration
3802 (*pceltFetched)++;
3803 currentReturnStruct++;
3806 * Push the next search node in the search stack.
3808 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3811 * continue the iteration.
3813 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3816 if (*pceltFetched == celt)
3817 return S_OK;
3819 return S_FALSE;
3823 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3824 IEnumSTATSTG* iface,
3825 ULONG celt)
3827 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3829 StgProperty currentProperty;
3830 ULONG objectFetched = 0;
3831 ULONG currentSearchNode;
3834 * Start with the node at the top of the stack.
3836 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3838 while ( (objectFetched < celt) &&
3839 (currentSearchNode!=PROPERTY_NULL) )
3842 * Remove the top node from the stack
3844 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3847 * Read the property from the storage.
3849 StorageImpl_ReadProperty(This->parentStorage,
3850 currentSearchNode,
3851 &currentProperty);
3854 * Step to the next item in the iteration
3856 objectFetched++;
3859 * Push the next search node in the search stack.
3861 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3864 * continue the iteration.
3866 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3869 if (objectFetched == celt)
3870 return S_OK;
3872 return S_FALSE;
3875 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3876 IEnumSTATSTG* iface)
3878 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3880 StgProperty rootProperty;
3881 BOOL readSuccessful;
3884 * Re-initialize the search stack to an empty stack
3886 This->stackSize = 0;
3889 * Read the root property from the storage.
3891 readSuccessful = StorageImpl_ReadProperty(
3892 This->parentStorage,
3893 This->firstPropertyNode,
3894 &rootProperty);
3896 if (readSuccessful)
3898 assert(rootProperty.sizeOfNameString!=0);
3901 * Push the search node in the search stack.
3903 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3906 return S_OK;
3909 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3910 IEnumSTATSTG* iface,
3911 IEnumSTATSTG** ppenum)
3913 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3915 IEnumSTATSTGImpl* newClone;
3918 * Perform a sanity check on the parameters.
3920 if (ppenum==0)
3921 return E_INVALIDARG;
3923 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3924 This->firstPropertyNode);
3928 * The new clone enumeration must point to the same current node as
3929 * the ole one.
3931 newClone->stackSize = This->stackSize ;
3932 newClone->stackMaxSize = This->stackMaxSize ;
3933 newClone->stackToVisit =
3934 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3936 memcpy(
3937 newClone->stackToVisit,
3938 This->stackToVisit,
3939 sizeof(ULONG) * newClone->stackSize);
3941 *ppenum = (IEnumSTATSTG*)newClone;
3944 * Don't forget to nail down a reference to the clone before
3945 * returning it.
3947 IEnumSTATSTGImpl_AddRef(*ppenum);
3949 return S_OK;
3952 static INT IEnumSTATSTGImpl_FindParentProperty(
3953 IEnumSTATSTGImpl *This,
3954 ULONG childProperty,
3955 StgProperty *currentProperty,
3956 ULONG *thisNodeId)
3958 ULONG currentSearchNode;
3959 ULONG foundNode;
3962 * To avoid the special case, get another pointer to a ULONG value if
3963 * the caller didn't supply one.
3965 if (thisNodeId==0)
3966 thisNodeId = &foundNode;
3969 * Start with the node at the top of the stack.
3971 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3974 while (currentSearchNode!=PROPERTY_NULL)
3977 * Store the current node in the returned parameters
3979 *thisNodeId = currentSearchNode;
3982 * Remove the top node from the stack
3984 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3987 * Read the property from the storage.
3989 StorageImpl_ReadProperty(
3990 This->parentStorage,
3991 currentSearchNode,
3992 currentProperty);
3994 if (currentProperty->previousProperty == childProperty)
3995 return PROPERTY_RELATION_PREVIOUS;
3997 else if (currentProperty->nextProperty == childProperty)
3998 return PROPERTY_RELATION_NEXT;
4000 else if (currentProperty->dirProperty == childProperty)
4001 return PROPERTY_RELATION_DIR;
4004 * Push the next search node in the search stack.
4006 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4009 * continue the iteration.
4011 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4014 return PROPERTY_NULL;
4017 static ULONG IEnumSTATSTGImpl_FindProperty(
4018 IEnumSTATSTGImpl* This,
4019 const OLECHAR* lpszPropName,
4020 StgProperty* currentProperty)
4022 ULONG currentSearchNode;
4025 * Start with the node at the top of the stack.
4027 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4029 while (currentSearchNode!=PROPERTY_NULL)
4032 * Remove the top node from the stack
4034 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4037 * Read the property from the storage.
4039 StorageImpl_ReadProperty(This->parentStorage,
4040 currentSearchNode,
4041 currentProperty);
4043 if ( propertyNameCmp(
4044 (const OLECHAR*)currentProperty->name, lpszPropName) == 0)
4045 return currentSearchNode;
4048 * Push the next search node in the search stack.
4050 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4053 * continue the iteration.
4055 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4058 return PROPERTY_NULL;
4061 static void IEnumSTATSTGImpl_PushSearchNode(
4062 IEnumSTATSTGImpl* This,
4063 ULONG nodeToPush)
4065 StgProperty rootProperty;
4066 BOOL readSuccessful;
4069 * First, make sure we're not trying to push an unexisting node.
4071 if (nodeToPush==PROPERTY_NULL)
4072 return;
4075 * First push the node to the stack
4077 if (This->stackSize == This->stackMaxSize)
4079 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4081 This->stackToVisit = HeapReAlloc(
4082 GetProcessHeap(),
4084 This->stackToVisit,
4085 sizeof(ULONG) * This->stackMaxSize);
4088 This->stackToVisit[This->stackSize] = nodeToPush;
4089 This->stackSize++;
4092 * Read the root property from the storage.
4094 readSuccessful = StorageImpl_ReadProperty(
4095 This->parentStorage,
4096 nodeToPush,
4097 &rootProperty);
4099 if (readSuccessful)
4101 assert(rootProperty.sizeOfNameString!=0);
4104 * Push the previous search node in the search stack.
4106 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4110 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4111 IEnumSTATSTGImpl* This,
4112 BOOL remove)
4114 ULONG topNode;
4116 if (This->stackSize == 0)
4117 return PROPERTY_NULL;
4119 topNode = This->stackToVisit[This->stackSize-1];
4121 if (remove)
4122 This->stackSize--;
4124 return topNode;
4128 * Virtual function table for the IEnumSTATSTGImpl class.
4130 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4132 IEnumSTATSTGImpl_QueryInterface,
4133 IEnumSTATSTGImpl_AddRef,
4134 IEnumSTATSTGImpl_Release,
4135 IEnumSTATSTGImpl_Next,
4136 IEnumSTATSTGImpl_Skip,
4137 IEnumSTATSTGImpl_Reset,
4138 IEnumSTATSTGImpl_Clone
4141 /******************************************************************************
4142 ** IEnumSTATSTGImpl implementation
4145 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4146 StorageImpl* parentStorage,
4147 ULONG firstPropertyNode)
4149 IEnumSTATSTGImpl* newEnumeration;
4151 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4153 if (newEnumeration!=0)
4156 * Set-up the virtual function table and reference count.
4158 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4159 newEnumeration->ref = 0;
4162 * We want to nail-down the reference to the storage in case the
4163 * enumeration out-lives the storage in the client application.
4165 newEnumeration->parentStorage = parentStorage;
4166 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4168 newEnumeration->firstPropertyNode = firstPropertyNode;
4171 * Initialize the search stack
4173 newEnumeration->stackSize = 0;
4174 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4175 newEnumeration->stackToVisit =
4176 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4179 * Make sure the current node of the iterator is the first one.
4181 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4184 return newEnumeration;
4188 * Virtual function table for the Storage32InternalImpl class.
4190 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4192 StorageBaseImpl_QueryInterface,
4193 StorageBaseImpl_AddRef,
4194 StorageBaseImpl_Release,
4195 StorageBaseImpl_CreateStream,
4196 StorageBaseImpl_OpenStream,
4197 StorageImpl_CreateStorage,
4198 StorageBaseImpl_OpenStorage,
4199 StorageImpl_CopyTo,
4200 StorageImpl_MoveElementTo,
4201 StorageInternalImpl_Commit,
4202 StorageInternalImpl_Revert,
4203 StorageBaseImpl_EnumElements,
4204 StorageImpl_DestroyElement,
4205 StorageBaseImpl_RenameElement,
4206 StorageImpl_SetElementTimes,
4207 StorageBaseImpl_SetClass,
4208 StorageImpl_SetStateBits,
4209 StorageBaseImpl_Stat
4212 /******************************************************************************
4213 ** Storage32InternalImpl implementation
4216 static StorageInternalImpl* StorageInternalImpl_Construct(
4217 StorageImpl* ancestorStorage,
4218 DWORD openFlags,
4219 ULONG rootPropertyIndex)
4221 StorageInternalImpl* newStorage;
4224 * Allocate space for the new storage object
4226 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4228 if (newStorage!=0)
4231 * Initialize the stream list
4233 list_init(&newStorage->base.strmHead);
4236 * Initialize the virtual function table.
4238 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4239 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
4240 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4243 * Keep the ancestor storage pointer and nail a reference to it.
4245 newStorage->base.ancestorStorage = ancestorStorage;
4246 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4249 * Keep the index of the root property set for this storage,
4251 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4253 return newStorage;
4256 return 0;
4259 /******************************************************************************
4260 ** StorageUtl implementation
4263 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4265 WORD tmp;
4267 memcpy(&tmp, buffer+offset, sizeof(WORD));
4268 *value = le16toh(tmp);
4271 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4273 value = htole16(value);
4274 memcpy(buffer+offset, &value, sizeof(WORD));
4277 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4279 DWORD tmp;
4281 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4282 *value = le32toh(tmp);
4285 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4287 value = htole32(value);
4288 memcpy(buffer+offset, &value, sizeof(DWORD));
4291 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4292 ULARGE_INTEGER* value)
4294 #ifdef WORDS_BIGENDIAN
4295 ULARGE_INTEGER tmp;
4297 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4298 value->u.LowPart = htole32(tmp.u.HighPart);
4299 value->u.HighPart = htole32(tmp.u.LowPart);
4300 #else
4301 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4302 #endif
4305 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4306 const ULARGE_INTEGER *value)
4308 #ifdef WORDS_BIGENDIAN
4309 ULARGE_INTEGER tmp;
4311 tmp.u.LowPart = htole32(value->u.HighPart);
4312 tmp.u.HighPart = htole32(value->u.LowPart);
4313 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4314 #else
4315 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4316 #endif
4319 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4321 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4322 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4323 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4325 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4328 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4330 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4331 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4332 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4334 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4337 void StorageUtl_CopyPropertyToSTATSTG(
4338 STATSTG* destination,
4339 const StgProperty* source,
4340 int statFlags)
4343 * The copy of the string occurs only when the flag is not set
4345 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4346 (source->name == NULL) ||
4347 (source->name[0] == 0) )
4349 destination->pwcsName = 0;
4351 else
4353 destination->pwcsName =
4354 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4356 strcpyW((LPWSTR)destination->pwcsName, source->name);
4359 switch (source->propertyType)
4361 case PROPTYPE_STORAGE:
4362 case PROPTYPE_ROOT:
4363 destination->type = STGTY_STORAGE;
4364 break;
4365 case PROPTYPE_STREAM:
4366 destination->type = STGTY_STREAM;
4367 break;
4368 default:
4369 destination->type = STGTY_STREAM;
4370 break;
4373 destination->cbSize = source->size;
4375 currentReturnStruct->mtime = {0}; TODO
4376 currentReturnStruct->ctime = {0};
4377 currentReturnStruct->atime = {0};
4379 destination->grfMode = 0;
4380 destination->grfLocksSupported = 0;
4381 destination->clsid = source->propertyUniqueID;
4382 destination->grfStateBits = 0;
4383 destination->reserved = 0;
4386 /******************************************************************************
4387 ** BlockChainStream implementation
4390 BlockChainStream* BlockChainStream_Construct(
4391 StorageImpl* parentStorage,
4392 ULONG* headOfStreamPlaceHolder,
4393 ULONG propertyIndex)
4395 BlockChainStream* newStream;
4396 ULONG blockIndex;
4398 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4400 newStream->parentStorage = parentStorage;
4401 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4402 newStream->ownerPropertyIndex = propertyIndex;
4403 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4404 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4405 newStream->numBlocks = 0;
4407 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4409 while (blockIndex != BLOCK_END_OF_CHAIN)
4411 newStream->numBlocks++;
4412 newStream->tailIndex = blockIndex;
4414 if(FAILED(StorageImpl_GetNextBlockInChain(
4415 parentStorage,
4416 blockIndex,
4417 &blockIndex)))
4419 HeapFree(GetProcessHeap(), 0, newStream);
4420 return NULL;
4424 return newStream;
4427 void BlockChainStream_Destroy(BlockChainStream* This)
4429 HeapFree(GetProcessHeap(), 0, This);
4432 /******************************************************************************
4433 * BlockChainStream_GetHeadOfChain
4435 * Returns the head of this stream chain.
4436 * Some special chains don't have properties, their heads are kept in
4437 * This->headOfStreamPlaceHolder.
4440 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4442 StgProperty chainProperty;
4443 BOOL readSuccessful;
4445 if (This->headOfStreamPlaceHolder != 0)
4446 return *(This->headOfStreamPlaceHolder);
4448 if (This->ownerPropertyIndex != PROPERTY_NULL)
4450 readSuccessful = StorageImpl_ReadProperty(
4451 This->parentStorage,
4452 This->ownerPropertyIndex,
4453 &chainProperty);
4455 if (readSuccessful)
4457 return chainProperty.startingBlock;
4461 return BLOCK_END_OF_CHAIN;
4464 /******************************************************************************
4465 * BlockChainStream_GetCount
4467 * Returns the number of blocks that comprises this chain.
4468 * This is not the size of the stream as the last block may not be full!
4471 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4473 ULONG blockIndex;
4474 ULONG count = 0;
4476 blockIndex = BlockChainStream_GetHeadOfChain(This);
4478 while (blockIndex != BLOCK_END_OF_CHAIN)
4480 count++;
4482 if(FAILED(StorageImpl_GetNextBlockInChain(
4483 This->parentStorage,
4484 blockIndex,
4485 &blockIndex)))
4486 return 0;
4489 return count;
4492 /******************************************************************************
4493 * BlockChainStream_ReadAt
4495 * Reads a specified number of bytes from this chain at the specified offset.
4496 * bytesRead may be NULL.
4497 * Failure will be returned if the specified number of bytes has not been read.
4499 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4500 ULARGE_INTEGER offset,
4501 ULONG size,
4502 void* buffer,
4503 ULONG* bytesRead)
4505 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4506 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4507 ULONG bytesToReadInBuffer;
4508 ULONG blockIndex;
4509 BYTE* bufferWalker;
4511 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4514 * Find the first block in the stream that contains part of the buffer.
4516 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4517 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4518 (blockNoInSequence < This->lastBlockNoInSequence) )
4520 blockIndex = BlockChainStream_GetHeadOfChain(This);
4521 This->lastBlockNoInSequence = blockNoInSequence;
4523 else
4525 ULONG temp = blockNoInSequence;
4527 blockIndex = This->lastBlockNoInSequenceIndex;
4528 blockNoInSequence -= This->lastBlockNoInSequence;
4529 This->lastBlockNoInSequence = temp;
4532 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4534 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4535 return STG_E_DOCFILECORRUPT;
4536 blockNoInSequence--;
4539 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4540 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4542 This->lastBlockNoInSequenceIndex = blockIndex;
4545 * Start reading the buffer.
4547 *bytesRead = 0;
4548 bufferWalker = buffer;
4550 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4552 ULARGE_INTEGER ulOffset;
4553 DWORD bytesReadAt;
4555 * Calculate how many bytes we can copy from this big block.
4557 bytesToReadInBuffer =
4558 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4560 TRACE("block %i\n",blockIndex);
4561 ulOffset.u.HighPart = 0;
4562 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4563 offsetInBlock;
4565 StorageImpl_ReadAt(This->parentStorage,
4566 ulOffset,
4567 bufferWalker,
4568 bytesToReadInBuffer,
4569 &bytesReadAt);
4571 * Step to the next big block.
4573 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4574 return STG_E_DOCFILECORRUPT;
4576 bufferWalker += bytesReadAt;
4577 size -= bytesReadAt;
4578 *bytesRead += bytesReadAt;
4579 offsetInBlock = 0; /* There is no offset on the next block */
4581 if (bytesToReadInBuffer != bytesReadAt)
4582 break;
4585 return (size == 0) ? S_OK : STG_E_READFAULT;
4588 /******************************************************************************
4589 * BlockChainStream_WriteAt
4591 * Writes the specified number of bytes to this chain at the specified offset.
4592 * bytesWritten may be NULL.
4593 * Will fail if not all specified number of bytes have been written.
4595 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4596 ULARGE_INTEGER offset,
4597 ULONG size,
4598 const void* buffer,
4599 ULONG* bytesWritten)
4601 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4602 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4603 ULONG bytesToWrite;
4604 ULONG blockIndex;
4605 const BYTE* bufferWalker;
4608 * Find the first block in the stream that contains part of the buffer.
4610 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4611 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4612 (blockNoInSequence < This->lastBlockNoInSequence) )
4614 blockIndex = BlockChainStream_GetHeadOfChain(This);
4615 This->lastBlockNoInSequence = blockNoInSequence;
4617 else
4619 ULONG temp = blockNoInSequence;
4621 blockIndex = This->lastBlockNoInSequenceIndex;
4622 blockNoInSequence -= This->lastBlockNoInSequence;
4623 This->lastBlockNoInSequence = temp;
4626 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4628 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4629 &blockIndex)))
4630 return STG_E_DOCFILECORRUPT;
4631 blockNoInSequence--;
4634 This->lastBlockNoInSequenceIndex = blockIndex;
4636 /* BlockChainStream_SetSize should have already been called to ensure we have
4637 * enough blocks in the chain to write into */
4638 if (blockIndex == BLOCK_END_OF_CHAIN)
4640 ERR("not enough blocks in chain to write data\n");
4641 return STG_E_DOCFILECORRUPT;
4644 *bytesWritten = 0;
4645 bufferWalker = (const BYTE*)buffer;
4647 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4649 ULARGE_INTEGER ulOffset;
4650 DWORD bytesWrittenAt;
4652 * Calculate how many bytes we can copy from this big block.
4654 bytesToWrite =
4655 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4657 TRACE("block %i\n",blockIndex);
4658 ulOffset.u.HighPart = 0;
4659 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4660 offsetInBlock;
4662 StorageImpl_WriteAt(This->parentStorage,
4663 ulOffset,
4664 bufferWalker,
4665 bytesToWrite,
4666 &bytesWrittenAt);
4669 * Step to the next big block.
4671 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4672 &blockIndex)))
4673 return STG_E_DOCFILECORRUPT;
4675 bufferWalker += bytesWrittenAt;
4676 size -= bytesWrittenAt;
4677 *bytesWritten += bytesWrittenAt;
4678 offsetInBlock = 0; /* There is no offset on the next block */
4680 if (bytesWrittenAt != bytesToWrite)
4681 break;
4684 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4687 /******************************************************************************
4688 * BlockChainStream_Shrink
4690 * Shrinks this chain in the big block depot.
4692 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4693 ULARGE_INTEGER newSize)
4695 ULONG blockIndex, extraBlock;
4696 ULONG numBlocks;
4697 ULONG count = 1;
4700 * Reset the last accessed block cache.
4702 This->lastBlockNoInSequence = 0xFFFFFFFF;
4703 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4706 * Figure out how many blocks are needed to contain the new size
4708 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4710 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4711 numBlocks++;
4713 blockIndex = BlockChainStream_GetHeadOfChain(This);
4716 * Go to the new end of chain
4718 while (count < numBlocks)
4720 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4721 &blockIndex)))
4722 return FALSE;
4723 count++;
4726 /* Get the next block before marking the new end */
4727 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4728 &extraBlock)))
4729 return FALSE;
4731 /* Mark the new end of chain */
4732 StorageImpl_SetNextBlockInChain(
4733 This->parentStorage,
4734 blockIndex,
4735 BLOCK_END_OF_CHAIN);
4737 This->tailIndex = blockIndex;
4738 This->numBlocks = numBlocks;
4741 * Mark the extra blocks as free
4743 while (extraBlock != BLOCK_END_OF_CHAIN)
4745 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4746 &blockIndex)))
4747 return FALSE;
4748 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4749 extraBlock = blockIndex;
4752 return TRUE;
4755 /******************************************************************************
4756 * BlockChainStream_Enlarge
4758 * Grows this chain in the big block depot.
4760 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4761 ULARGE_INTEGER newSize)
4763 ULONG blockIndex, currentBlock;
4764 ULONG newNumBlocks;
4765 ULONG oldNumBlocks = 0;
4767 blockIndex = BlockChainStream_GetHeadOfChain(This);
4770 * Empty chain. Create the head.
4772 if (blockIndex == BLOCK_END_OF_CHAIN)
4774 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4775 StorageImpl_SetNextBlockInChain(This->parentStorage,
4776 blockIndex,
4777 BLOCK_END_OF_CHAIN);
4779 if (This->headOfStreamPlaceHolder != 0)
4781 *(This->headOfStreamPlaceHolder) = blockIndex;
4783 else
4785 StgProperty chainProp;
4786 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4788 StorageImpl_ReadProperty(
4789 This->parentStorage,
4790 This->ownerPropertyIndex,
4791 &chainProp);
4793 chainProp.startingBlock = blockIndex;
4795 StorageImpl_WriteProperty(
4796 This->parentStorage,
4797 This->ownerPropertyIndex,
4798 &chainProp);
4801 This->tailIndex = blockIndex;
4802 This->numBlocks = 1;
4806 * Figure out how many blocks are needed to contain this stream
4808 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4810 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4811 newNumBlocks++;
4814 * Go to the current end of chain
4816 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4818 currentBlock = blockIndex;
4820 while (blockIndex != BLOCK_END_OF_CHAIN)
4822 This->numBlocks++;
4823 currentBlock = blockIndex;
4825 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4826 &blockIndex)))
4827 return FALSE;
4830 This->tailIndex = currentBlock;
4833 currentBlock = This->tailIndex;
4834 oldNumBlocks = This->numBlocks;
4837 * Add new blocks to the chain
4839 if (oldNumBlocks < newNumBlocks)
4841 while (oldNumBlocks < newNumBlocks)
4843 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4845 StorageImpl_SetNextBlockInChain(
4846 This->parentStorage,
4847 currentBlock,
4848 blockIndex);
4850 StorageImpl_SetNextBlockInChain(
4851 This->parentStorage,
4852 blockIndex,
4853 BLOCK_END_OF_CHAIN);
4855 currentBlock = blockIndex;
4856 oldNumBlocks++;
4859 This->tailIndex = blockIndex;
4860 This->numBlocks = newNumBlocks;
4863 return TRUE;
4866 /******************************************************************************
4867 * BlockChainStream_SetSize
4869 * Sets the size of this stream. The big block depot will be updated.
4870 * The file will grow if we grow the chain.
4872 * TODO: Free the actual blocks in the file when we shrink the chain.
4873 * Currently, the blocks are still in the file. So the file size
4874 * doesn't shrink even if we shrink streams.
4876 BOOL BlockChainStream_SetSize(
4877 BlockChainStream* This,
4878 ULARGE_INTEGER newSize)
4880 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4882 if (newSize.u.LowPart == size.u.LowPart)
4883 return TRUE;
4885 if (newSize.u.LowPart < size.u.LowPart)
4887 BlockChainStream_Shrink(This, newSize);
4889 else
4891 BlockChainStream_Enlarge(This, newSize);
4894 return TRUE;
4897 /******************************************************************************
4898 * BlockChainStream_GetSize
4900 * Returns the size of this chain.
4901 * Will return the block count if this chain doesn't have a property.
4903 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4905 StgProperty chainProperty;
4907 if(This->headOfStreamPlaceHolder == NULL)
4910 * This chain is a data stream read the property and return
4911 * the appropriate size
4913 StorageImpl_ReadProperty(
4914 This->parentStorage,
4915 This->ownerPropertyIndex,
4916 &chainProperty);
4918 return chainProperty.size;
4920 else
4923 * this chain is a chain that does not have a property, figure out the
4924 * size by making the product number of used blocks times the
4925 * size of them
4927 ULARGE_INTEGER result;
4928 result.u.HighPart = 0;
4930 result.u.LowPart =
4931 BlockChainStream_GetCount(This) *
4932 This->parentStorage->bigBlockSize;
4934 return result;
4938 /******************************************************************************
4939 ** SmallBlockChainStream implementation
4942 SmallBlockChainStream* SmallBlockChainStream_Construct(
4943 StorageImpl* parentStorage,
4944 ULONG propertyIndex)
4946 SmallBlockChainStream* newStream;
4948 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4950 newStream->parentStorage = parentStorage;
4951 newStream->ownerPropertyIndex = propertyIndex;
4953 return newStream;
4956 void SmallBlockChainStream_Destroy(
4957 SmallBlockChainStream* This)
4959 HeapFree(GetProcessHeap(), 0, This);
4962 /******************************************************************************
4963 * SmallBlockChainStream_GetHeadOfChain
4965 * Returns the head of this chain of small blocks.
4967 static ULONG SmallBlockChainStream_GetHeadOfChain(
4968 SmallBlockChainStream* This)
4970 StgProperty chainProperty;
4971 BOOL readSuccessful;
4973 if (This->ownerPropertyIndex)
4975 readSuccessful = StorageImpl_ReadProperty(
4976 This->parentStorage,
4977 This->ownerPropertyIndex,
4978 &chainProperty);
4980 if (readSuccessful)
4982 return chainProperty.startingBlock;
4987 return BLOCK_END_OF_CHAIN;
4990 /******************************************************************************
4991 * SmallBlockChainStream_GetNextBlockInChain
4993 * Returns the index of the next small block in this chain.
4995 * Return Values:
4996 * - BLOCK_END_OF_CHAIN: end of this chain
4997 * - BLOCK_UNUSED: small block 'blockIndex' is free
4999 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5000 SmallBlockChainStream* This,
5001 ULONG blockIndex,
5002 ULONG* nextBlockInChain)
5004 ULARGE_INTEGER offsetOfBlockInDepot;
5005 DWORD buffer;
5006 ULONG bytesRead;
5007 HRESULT res;
5009 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5011 offsetOfBlockInDepot.u.HighPart = 0;
5012 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5015 * Read those bytes in the buffer from the small block file.
5017 res = BlockChainStream_ReadAt(
5018 This->parentStorage->smallBlockDepotChain,
5019 offsetOfBlockInDepot,
5020 sizeof(DWORD),
5021 &buffer,
5022 &bytesRead);
5024 if (SUCCEEDED(res))
5026 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5027 return S_OK;
5030 return res;
5033 /******************************************************************************
5034 * SmallBlockChainStream_SetNextBlockInChain
5036 * Writes the index of the next block of the specified block in the small
5037 * block depot.
5038 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5039 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5041 static void SmallBlockChainStream_SetNextBlockInChain(
5042 SmallBlockChainStream* This,
5043 ULONG blockIndex,
5044 ULONG nextBlock)
5046 ULARGE_INTEGER offsetOfBlockInDepot;
5047 DWORD buffer;
5048 ULONG bytesWritten;
5050 offsetOfBlockInDepot.u.HighPart = 0;
5051 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5053 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5056 * Read those bytes in the buffer from the small block file.
5058 BlockChainStream_WriteAt(
5059 This->parentStorage->smallBlockDepotChain,
5060 offsetOfBlockInDepot,
5061 sizeof(DWORD),
5062 &buffer,
5063 &bytesWritten);
5066 /******************************************************************************
5067 * SmallBlockChainStream_FreeBlock
5069 * Flag small block 'blockIndex' as free in the small block depot.
5071 static void SmallBlockChainStream_FreeBlock(
5072 SmallBlockChainStream* This,
5073 ULONG blockIndex)
5075 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5078 /******************************************************************************
5079 * SmallBlockChainStream_GetNextFreeBlock
5081 * Returns the index of a free small block. The small block depot will be
5082 * enlarged if necessary. The small block chain will also be enlarged if
5083 * necessary.
5085 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5086 SmallBlockChainStream* This)
5088 ULARGE_INTEGER offsetOfBlockInDepot;
5089 DWORD buffer;
5090 ULONG bytesRead;
5091 ULONG blockIndex = 0;
5092 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5093 HRESULT res = S_OK;
5094 ULONG smallBlocksPerBigBlock;
5096 offsetOfBlockInDepot.u.HighPart = 0;
5099 * Scan the small block depot for a free block
5101 while (nextBlockIndex != BLOCK_UNUSED)
5103 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5105 res = BlockChainStream_ReadAt(
5106 This->parentStorage->smallBlockDepotChain,
5107 offsetOfBlockInDepot,
5108 sizeof(DWORD),
5109 &buffer,
5110 &bytesRead);
5113 * If we run out of space for the small block depot, enlarge it
5115 if (SUCCEEDED(res))
5117 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5119 if (nextBlockIndex != BLOCK_UNUSED)
5120 blockIndex++;
5122 else
5124 ULONG count =
5125 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5127 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5128 ULONG nextBlock, newsbdIndex;
5129 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5131 nextBlock = sbdIndex;
5132 while (nextBlock != BLOCK_END_OF_CHAIN)
5134 sbdIndex = nextBlock;
5135 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5138 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5139 if (sbdIndex != BLOCK_END_OF_CHAIN)
5140 StorageImpl_SetNextBlockInChain(
5141 This->parentStorage,
5142 sbdIndex,
5143 newsbdIndex);
5145 StorageImpl_SetNextBlockInChain(
5146 This->parentStorage,
5147 newsbdIndex,
5148 BLOCK_END_OF_CHAIN);
5151 * Initialize all the small blocks to free
5153 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5154 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5156 if (count == 0)
5159 * We have just created the small block depot.
5161 StgProperty rootProp;
5162 ULONG sbStartIndex;
5165 * Save it in the header
5167 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5168 StorageImpl_SaveFileHeader(This->parentStorage);
5171 * And allocate the first big block that will contain small blocks
5173 sbStartIndex =
5174 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5176 StorageImpl_SetNextBlockInChain(
5177 This->parentStorage,
5178 sbStartIndex,
5179 BLOCK_END_OF_CHAIN);
5181 StorageImpl_ReadProperty(
5182 This->parentStorage,
5183 This->parentStorage->base.rootPropertySetIndex,
5184 &rootProp);
5186 rootProp.startingBlock = sbStartIndex;
5187 rootProp.size.u.HighPart = 0;
5188 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5190 StorageImpl_WriteProperty(
5191 This->parentStorage,
5192 This->parentStorage->base.rootPropertySetIndex,
5193 &rootProp);
5195 else
5196 StorageImpl_SaveFileHeader(This->parentStorage);
5200 smallBlocksPerBigBlock =
5201 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5204 * Verify if we have to allocate big blocks to contain small blocks
5206 if (blockIndex % smallBlocksPerBigBlock == 0)
5208 StgProperty rootProp;
5209 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5211 StorageImpl_ReadProperty(
5212 This->parentStorage,
5213 This->parentStorage->base.rootPropertySetIndex,
5214 &rootProp);
5216 if (rootProp.size.u.LowPart <
5217 (blocksRequired * This->parentStorage->bigBlockSize))
5219 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5221 BlockChainStream_SetSize(
5222 This->parentStorage->smallBlockRootChain,
5223 rootProp.size);
5225 StorageImpl_WriteProperty(
5226 This->parentStorage,
5227 This->parentStorage->base.rootPropertySetIndex,
5228 &rootProp);
5232 return blockIndex;
5235 /******************************************************************************
5236 * SmallBlockChainStream_ReadAt
5238 * Reads a specified number of bytes from this chain at the specified offset.
5239 * bytesRead may be NULL.
5240 * Failure will be returned if the specified number of bytes has not been read.
5242 HRESULT SmallBlockChainStream_ReadAt(
5243 SmallBlockChainStream* This,
5244 ULARGE_INTEGER offset,
5245 ULONG size,
5246 void* buffer,
5247 ULONG* bytesRead)
5249 HRESULT rc = S_OK;
5250 ULARGE_INTEGER offsetInBigBlockFile;
5251 ULONG blockNoInSequence =
5252 offset.u.LowPart / This->parentStorage->smallBlockSize;
5254 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5255 ULONG bytesToReadInBuffer;
5256 ULONG blockIndex;
5257 ULONG bytesReadFromBigBlockFile;
5258 BYTE* bufferWalker;
5261 * This should never happen on a small block file.
5263 assert(offset.u.HighPart==0);
5266 * Find the first block in the stream that contains part of the buffer.
5268 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5270 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5272 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5273 if(FAILED(rc))
5274 return rc;
5275 blockNoInSequence--;
5279 * Start reading the buffer.
5281 *bytesRead = 0;
5282 bufferWalker = buffer;
5284 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5287 * Calculate how many bytes we can copy from this small block.
5289 bytesToReadInBuffer =
5290 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5293 * Calculate the offset of the small block in the small block file.
5295 offsetInBigBlockFile.u.HighPart = 0;
5296 offsetInBigBlockFile.u.LowPart =
5297 blockIndex * This->parentStorage->smallBlockSize;
5299 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5302 * Read those bytes in the buffer from the small block file.
5303 * The small block has already been identified so it shouldn't fail
5304 * unless the file is corrupt.
5306 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5307 offsetInBigBlockFile,
5308 bytesToReadInBuffer,
5309 bufferWalker,
5310 &bytesReadFromBigBlockFile);
5312 if (FAILED(rc))
5313 return rc;
5316 * Step to the next big block.
5318 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5319 if(FAILED(rc))
5320 return STG_E_DOCFILECORRUPT;
5322 bufferWalker += bytesReadFromBigBlockFile;
5323 size -= bytesReadFromBigBlockFile;
5324 *bytesRead += bytesReadFromBigBlockFile;
5325 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5328 return (size == 0) ? S_OK : STG_E_READFAULT;
5331 /******************************************************************************
5332 * SmallBlockChainStream_WriteAt
5334 * Writes the specified number of bytes to this chain at the specified offset.
5335 * bytesWritten may be NULL.
5336 * Will fail if not all specified number of bytes have been written.
5338 HRESULT SmallBlockChainStream_WriteAt(
5339 SmallBlockChainStream* This,
5340 ULARGE_INTEGER offset,
5341 ULONG size,
5342 const void* buffer,
5343 ULONG* bytesWritten)
5345 ULARGE_INTEGER offsetInBigBlockFile;
5346 ULONG blockNoInSequence =
5347 offset.u.LowPart / This->parentStorage->smallBlockSize;
5349 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5350 ULONG bytesToWriteInBuffer;
5351 ULONG blockIndex;
5352 ULONG bytesWrittenToBigBlockFile;
5353 const BYTE* bufferWalker;
5354 HRESULT res;
5357 * This should never happen on a small block file.
5359 assert(offset.u.HighPart==0);
5362 * Find the first block in the stream that contains part of the buffer.
5364 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5366 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5368 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5369 return STG_E_DOCFILECORRUPT;
5370 blockNoInSequence--;
5374 * Start writing the buffer.
5376 * Here, I'm casting away the constness on the buffer variable
5377 * This is OK since we don't intend to modify that buffer.
5379 *bytesWritten = 0;
5380 bufferWalker = (const BYTE*)buffer;
5381 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5384 * Calculate how many bytes we can copy to this small block.
5386 bytesToWriteInBuffer =
5387 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5390 * Calculate the offset of the small block in the small block file.
5392 offsetInBigBlockFile.u.HighPart = 0;
5393 offsetInBigBlockFile.u.LowPart =
5394 blockIndex * This->parentStorage->smallBlockSize;
5396 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5399 * Write those bytes in the buffer to the small block file.
5401 res = BlockChainStream_WriteAt(
5402 This->parentStorage->smallBlockRootChain,
5403 offsetInBigBlockFile,
5404 bytesToWriteInBuffer,
5405 bufferWalker,
5406 &bytesWrittenToBigBlockFile);
5407 if (FAILED(res))
5408 return res;
5411 * Step to the next big block.
5413 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5414 &blockIndex)))
5415 return FALSE;
5416 bufferWalker += bytesWrittenToBigBlockFile;
5417 size -= bytesWrittenToBigBlockFile;
5418 *bytesWritten += bytesWrittenToBigBlockFile;
5419 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5422 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5425 /******************************************************************************
5426 * SmallBlockChainStream_Shrink
5428 * Shrinks this chain in the small block depot.
5430 static BOOL SmallBlockChainStream_Shrink(
5431 SmallBlockChainStream* This,
5432 ULARGE_INTEGER newSize)
5434 ULONG blockIndex, extraBlock;
5435 ULONG numBlocks;
5436 ULONG count = 0;
5438 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5440 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5441 numBlocks++;
5443 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5446 * Go to the new end of chain
5448 while (count < numBlocks)
5450 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5451 &blockIndex)))
5452 return FALSE;
5453 count++;
5457 * If the count is 0, we have a special case, the head of the chain was
5458 * just freed.
5460 if (count == 0)
5462 StgProperty chainProp;
5464 StorageImpl_ReadProperty(This->parentStorage,
5465 This->ownerPropertyIndex,
5466 &chainProp);
5468 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5470 StorageImpl_WriteProperty(This->parentStorage,
5471 This->ownerPropertyIndex,
5472 &chainProp);
5475 * We start freeing the chain at the head block.
5477 extraBlock = blockIndex;
5479 else
5481 /* Get the next block before marking the new end */
5482 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5483 &extraBlock)))
5484 return FALSE;
5486 /* Mark the new end of chain */
5487 SmallBlockChainStream_SetNextBlockInChain(
5488 This,
5489 blockIndex,
5490 BLOCK_END_OF_CHAIN);
5494 * Mark the extra blocks as free
5496 while (extraBlock != BLOCK_END_OF_CHAIN)
5498 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5499 &blockIndex)))
5500 return FALSE;
5501 SmallBlockChainStream_FreeBlock(This, extraBlock);
5502 extraBlock = blockIndex;
5505 return TRUE;
5508 /******************************************************************************
5509 * SmallBlockChainStream_Enlarge
5511 * Grows this chain in the small block depot.
5513 static BOOL SmallBlockChainStream_Enlarge(
5514 SmallBlockChainStream* This,
5515 ULARGE_INTEGER newSize)
5517 ULONG blockIndex, currentBlock;
5518 ULONG newNumBlocks;
5519 ULONG oldNumBlocks = 0;
5521 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5524 * Empty chain
5526 if (blockIndex == BLOCK_END_OF_CHAIN)
5529 StgProperty chainProp;
5531 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5532 &chainProp);
5534 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5536 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5537 &chainProp);
5539 blockIndex = chainProp.startingBlock;
5540 SmallBlockChainStream_SetNextBlockInChain(
5541 This,
5542 blockIndex,
5543 BLOCK_END_OF_CHAIN);
5546 currentBlock = blockIndex;
5549 * Figure out how many blocks are needed to contain this stream
5551 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5553 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5554 newNumBlocks++;
5557 * Go to the current end of chain
5559 while (blockIndex != BLOCK_END_OF_CHAIN)
5561 oldNumBlocks++;
5562 currentBlock = blockIndex;
5563 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5564 return FALSE;
5568 * Add new blocks to the chain
5570 while (oldNumBlocks < newNumBlocks)
5572 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5573 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5575 SmallBlockChainStream_SetNextBlockInChain(
5576 This,
5577 blockIndex,
5578 BLOCK_END_OF_CHAIN);
5580 currentBlock = blockIndex;
5581 oldNumBlocks++;
5584 return TRUE;
5587 /******************************************************************************
5588 * SmallBlockChainStream_SetSize
5590 * Sets the size of this stream.
5591 * The file will grow if we grow the chain.
5593 * TODO: Free the actual blocks in the file when we shrink the chain.
5594 * Currently, the blocks are still in the file. So the file size
5595 * doesn't shrink even if we shrink streams.
5597 BOOL SmallBlockChainStream_SetSize(
5598 SmallBlockChainStream* This,
5599 ULARGE_INTEGER newSize)
5601 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5603 if (newSize.u.LowPart == size.u.LowPart)
5604 return TRUE;
5606 if (newSize.u.LowPart < size.u.LowPart)
5608 SmallBlockChainStream_Shrink(This, newSize);
5610 else
5612 SmallBlockChainStream_Enlarge(This, newSize);
5615 return TRUE;
5618 /******************************************************************************
5619 * SmallBlockChainStream_GetSize
5621 * Returns the size of this chain.
5623 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5625 StgProperty chainProperty;
5627 StorageImpl_ReadProperty(
5628 This->parentStorage,
5629 This->ownerPropertyIndex,
5630 &chainProperty);
5632 return chainProperty.size;
5635 /******************************************************************************
5636 * StgCreateDocfile [OLE32.@]
5637 * Creates a new compound file storage object
5639 * PARAMS
5640 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5641 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5642 * reserved [ ?] unused?, usually 0
5643 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5645 * RETURNS
5646 * S_OK if the file was successfully created
5647 * some STG_E_ value if error
5648 * NOTES
5649 * if pwcsName is NULL, create file with new unique name
5650 * the function can returns
5651 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5652 * (unrealized now)
5654 HRESULT WINAPI StgCreateDocfile(
5655 LPCOLESTR pwcsName,
5656 DWORD grfMode,
5657 DWORD reserved,
5658 IStorage **ppstgOpen)
5660 StorageImpl* newStorage = 0;
5661 HANDLE hFile = INVALID_HANDLE_VALUE;
5662 HRESULT hr = STG_E_INVALIDFLAG;
5663 DWORD shareMode;
5664 DWORD accessMode;
5665 DWORD creationMode;
5666 DWORD fileAttributes;
5667 WCHAR tempFileName[MAX_PATH];
5669 TRACE("(%s, %x, %d, %p)\n",
5670 debugstr_w(pwcsName), grfMode,
5671 reserved, ppstgOpen);
5674 * Validate the parameters
5676 if (ppstgOpen == 0)
5677 return STG_E_INVALIDPOINTER;
5678 if (reserved != 0)
5679 return STG_E_INVALIDPARAMETER;
5681 /* if no share mode given then DENY_NONE is the default */
5682 if (STGM_SHARE_MODE(grfMode) == 0)
5683 grfMode |= STGM_SHARE_DENY_NONE;
5686 * Validate the STGM flags
5688 if ( FAILED( validateSTGM(grfMode) ))
5689 goto end;
5691 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5692 switch(STGM_ACCESS_MODE(grfMode))
5694 case STGM_WRITE:
5695 case STGM_READWRITE:
5696 break;
5697 default:
5698 goto end;
5701 /* in direct mode, can only use SHARE_EXCLUSIVE */
5702 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5703 goto end;
5705 /* but in transacted mode, any share mode is valid */
5708 * Generate a unique name.
5710 if (pwcsName == 0)
5712 WCHAR tempPath[MAX_PATH];
5713 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5715 memset(tempPath, 0, sizeof(tempPath));
5716 memset(tempFileName, 0, sizeof(tempFileName));
5718 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5719 tempPath[0] = '.';
5721 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5722 pwcsName = tempFileName;
5723 else
5725 hr = STG_E_INSUFFICIENTMEMORY;
5726 goto end;
5729 creationMode = TRUNCATE_EXISTING;
5731 else
5733 creationMode = GetCreationModeFromSTGM(grfMode);
5737 * Interpret the STGM value grfMode
5739 shareMode = GetShareModeFromSTGM(grfMode);
5740 accessMode = GetAccessModeFromSTGM(grfMode);
5742 if (grfMode & STGM_DELETEONRELEASE)
5743 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5744 else
5745 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5747 if (grfMode & STGM_TRANSACTED)
5748 FIXME("Transacted mode not implemented.\n");
5751 * Initialize the "out" parameter.
5753 *ppstgOpen = 0;
5755 hFile = CreateFileW(pwcsName,
5756 accessMode,
5757 shareMode,
5758 NULL,
5759 creationMode,
5760 fileAttributes,
5763 if (hFile == INVALID_HANDLE_VALUE)
5765 if(GetLastError() == ERROR_FILE_EXISTS)
5766 hr = STG_E_FILEALREADYEXISTS;
5767 else
5768 hr = E_FAIL;
5769 goto end;
5773 * Allocate and initialize the new IStorage32object.
5775 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5777 if (newStorage == 0)
5779 hr = STG_E_INSUFFICIENTMEMORY;
5780 goto end;
5783 hr = StorageImpl_Construct(
5784 newStorage,
5785 hFile,
5786 pwcsName,
5787 NULL,
5788 grfMode,
5789 TRUE,
5790 TRUE);
5792 if (FAILED(hr))
5794 HeapFree(GetProcessHeap(), 0, newStorage);
5795 goto end;
5799 * Get an "out" pointer for the caller.
5801 hr = StorageBaseImpl_QueryInterface(
5802 (IStorage*)newStorage,
5803 (REFIID)&IID_IStorage,
5804 (void**)ppstgOpen);
5805 end:
5806 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5808 return hr;
5811 /******************************************************************************
5812 * StgCreateStorageEx [OLE32.@]
5814 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5816 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5817 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5819 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5821 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5822 return STG_E_INVALIDPARAMETER;
5825 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5827 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5828 return STG_E_INVALIDPARAMETER;
5831 if (stgfmt == STGFMT_FILE)
5833 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5834 return STG_E_INVALIDPARAMETER;
5837 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5839 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5840 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5843 ERR("Invalid stgfmt argument\n");
5844 return STG_E_INVALIDPARAMETER;
5847 /******************************************************************************
5848 * StgCreatePropSetStg [OLE32.@]
5850 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5851 IPropertySetStorage **ppPropSetStg)
5853 HRESULT hr;
5855 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5856 if (reserved)
5857 hr = STG_E_INVALIDPARAMETER;
5858 else
5859 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5860 (void**)ppPropSetStg);
5861 return hr;
5864 /******************************************************************************
5865 * StgOpenStorageEx [OLE32.@]
5867 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5869 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5870 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5872 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5874 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5875 return STG_E_INVALIDPARAMETER;
5878 switch (stgfmt)
5880 case STGFMT_FILE:
5881 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5882 return STG_E_INVALIDPARAMETER;
5884 case STGFMT_STORAGE:
5885 break;
5887 case STGFMT_DOCFILE:
5888 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5890 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5891 return STG_E_INVALIDPARAMETER;
5893 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5894 break;
5896 case STGFMT_ANY:
5897 WARN("STGFMT_ANY assuming storage\n");
5898 break;
5900 default:
5901 return STG_E_INVALIDPARAMETER;
5904 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5908 /******************************************************************************
5909 * StgOpenStorage [OLE32.@]
5911 HRESULT WINAPI StgOpenStorage(
5912 const OLECHAR *pwcsName,
5913 IStorage *pstgPriority,
5914 DWORD grfMode,
5915 SNB snbExclude,
5916 DWORD reserved,
5917 IStorage **ppstgOpen)
5919 StorageImpl* newStorage = 0;
5920 HRESULT hr = S_OK;
5921 HANDLE hFile = 0;
5922 DWORD shareMode;
5923 DWORD accessMode;
5924 WCHAR fullname[MAX_PATH];
5926 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5927 debugstr_w(pwcsName), pstgPriority, grfMode,
5928 snbExclude, reserved, ppstgOpen);
5931 * Perform sanity checks
5933 if (pwcsName == 0)
5935 hr = STG_E_INVALIDNAME;
5936 goto end;
5939 if (ppstgOpen == 0)
5941 hr = STG_E_INVALIDPOINTER;
5942 goto end;
5945 if (reserved)
5947 hr = STG_E_INVALIDPARAMETER;
5948 goto end;
5951 if (grfMode & STGM_PRIORITY)
5953 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5954 return STG_E_INVALIDFLAG;
5955 if (grfMode & STGM_DELETEONRELEASE)
5956 return STG_E_INVALIDFUNCTION;
5957 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5958 return STG_E_INVALIDFLAG;
5959 grfMode &= ~0xf0; /* remove the existing sharing mode */
5960 grfMode |= STGM_SHARE_DENY_NONE;
5962 /* STGM_PRIORITY stops other IStorage objects on the same file from
5963 * committing until the STGM_PRIORITY IStorage is closed. it also
5964 * stops non-transacted mode StgOpenStorage calls with write access from
5965 * succeeding. obviously, both of these cannot be achieved through just
5966 * file share flags */
5967 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5971 * Validate the sharing mode
5973 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5974 switch(STGM_SHARE_MODE(grfMode))
5976 case STGM_SHARE_EXCLUSIVE:
5977 case STGM_SHARE_DENY_WRITE:
5978 break;
5979 default:
5980 hr = STG_E_INVALIDFLAG;
5981 goto end;
5985 * Validate the STGM flags
5987 if ( FAILED( validateSTGM(grfMode) ) ||
5988 (grfMode&STGM_CREATE))
5990 hr = STG_E_INVALIDFLAG;
5991 goto end;
5994 /* shared reading requires transacted mode */
5995 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5996 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5997 !(grfMode&STGM_TRANSACTED) )
5999 hr = STG_E_INVALIDFLAG;
6000 goto end;
6004 * Interpret the STGM value grfMode
6006 shareMode = GetShareModeFromSTGM(grfMode);
6007 accessMode = GetAccessModeFromSTGM(grfMode);
6010 * Initialize the "out" parameter.
6012 *ppstgOpen = 0;
6014 hFile = CreateFileW( pwcsName,
6015 accessMode,
6016 shareMode,
6017 NULL,
6018 OPEN_EXISTING,
6019 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6022 if (hFile==INVALID_HANDLE_VALUE)
6024 DWORD last_error = GetLastError();
6026 hr = E_FAIL;
6028 switch (last_error)
6030 case ERROR_FILE_NOT_FOUND:
6031 hr = STG_E_FILENOTFOUND;
6032 break;
6034 case ERROR_PATH_NOT_FOUND:
6035 hr = STG_E_PATHNOTFOUND;
6036 break;
6038 case ERROR_ACCESS_DENIED:
6039 case ERROR_WRITE_PROTECT:
6040 hr = STG_E_ACCESSDENIED;
6041 break;
6043 case ERROR_SHARING_VIOLATION:
6044 hr = STG_E_SHAREVIOLATION;
6045 break;
6047 default:
6048 hr = E_FAIL;
6051 goto end;
6055 * Refuse to open the file if it's too small to be a structured storage file
6056 * FIXME: verify the file when reading instead of here
6058 if (GetFileSize(hFile, NULL) < 0x100)
6060 CloseHandle(hFile);
6061 hr = STG_E_FILEALREADYEXISTS;
6062 goto end;
6066 * Allocate and initialize the new IStorage32object.
6068 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6070 if (newStorage == 0)
6072 hr = STG_E_INSUFFICIENTMEMORY;
6073 goto end;
6076 /* Initialize the storage */
6077 hr = StorageImpl_Construct(
6078 newStorage,
6079 hFile,
6080 pwcsName,
6081 NULL,
6082 grfMode,
6083 TRUE,
6084 FALSE );
6086 if (FAILED(hr))
6088 HeapFree(GetProcessHeap(), 0, newStorage);
6090 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6092 if(hr == STG_E_INVALIDHEADER)
6093 hr = STG_E_FILEALREADYEXISTS;
6094 goto end;
6097 /* prepare the file name string given in lieu of the root property name */
6098 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6099 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6100 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6103 * Get an "out" pointer for the caller.
6105 hr = StorageBaseImpl_QueryInterface(
6106 (IStorage*)newStorage,
6107 (REFIID)&IID_IStorage,
6108 (void**)ppstgOpen);
6110 end:
6111 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6112 return hr;
6115 /******************************************************************************
6116 * StgCreateDocfileOnILockBytes [OLE32.@]
6118 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6119 ILockBytes *plkbyt,
6120 DWORD grfMode,
6121 DWORD reserved,
6122 IStorage** ppstgOpen)
6124 StorageImpl* newStorage = 0;
6125 HRESULT hr = S_OK;
6128 * Validate the parameters
6130 if ((ppstgOpen == 0) || (plkbyt == 0))
6131 return STG_E_INVALIDPOINTER;
6134 * Allocate and initialize the new IStorage object.
6136 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6138 if (newStorage == 0)
6139 return STG_E_INSUFFICIENTMEMORY;
6141 hr = StorageImpl_Construct(
6142 newStorage,
6145 plkbyt,
6146 grfMode,
6147 FALSE,
6148 TRUE);
6150 if (FAILED(hr))
6152 HeapFree(GetProcessHeap(), 0, newStorage);
6153 return hr;
6157 * Get an "out" pointer for the caller.
6159 hr = StorageBaseImpl_QueryInterface(
6160 (IStorage*)newStorage,
6161 (REFIID)&IID_IStorage,
6162 (void**)ppstgOpen);
6164 return hr;
6167 /******************************************************************************
6168 * StgOpenStorageOnILockBytes [OLE32.@]
6170 HRESULT WINAPI StgOpenStorageOnILockBytes(
6171 ILockBytes *plkbyt,
6172 IStorage *pstgPriority,
6173 DWORD grfMode,
6174 SNB snbExclude,
6175 DWORD reserved,
6176 IStorage **ppstgOpen)
6178 StorageImpl* newStorage = 0;
6179 HRESULT hr = S_OK;
6182 * Perform a sanity check
6184 if ((plkbyt == 0) || (ppstgOpen == 0))
6185 return STG_E_INVALIDPOINTER;
6188 * Validate the STGM flags
6190 if ( FAILED( validateSTGM(grfMode) ))
6191 return STG_E_INVALIDFLAG;
6194 * Initialize the "out" parameter.
6196 *ppstgOpen = 0;
6199 * Allocate and initialize the new IStorage object.
6201 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6203 if (newStorage == 0)
6204 return STG_E_INSUFFICIENTMEMORY;
6206 hr = StorageImpl_Construct(
6207 newStorage,
6210 plkbyt,
6211 grfMode,
6212 FALSE,
6213 FALSE);
6215 if (FAILED(hr))
6217 HeapFree(GetProcessHeap(), 0, newStorage);
6218 return hr;
6222 * Get an "out" pointer for the caller.
6224 hr = StorageBaseImpl_QueryInterface(
6225 (IStorage*)newStorage,
6226 (REFIID)&IID_IStorage,
6227 (void**)ppstgOpen);
6229 return hr;
6232 /******************************************************************************
6233 * StgSetTimes [ole32.@]
6234 * StgSetTimes [OLE32.@]
6238 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6239 FILETIME const *patime, FILETIME const *pmtime)
6241 IStorage *stg = NULL;
6242 HRESULT r;
6244 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6246 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6247 0, 0, &stg);
6248 if( SUCCEEDED(r) )
6250 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6251 IStorage_Release(stg);
6254 return r;
6257 /******************************************************************************
6258 * StgIsStorageILockBytes [OLE32.@]
6260 * Determines if the ILockBytes contains a storage object.
6262 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6264 BYTE sig[8];
6265 ULARGE_INTEGER offset;
6267 offset.u.HighPart = 0;
6268 offset.u.LowPart = 0;
6270 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6272 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6273 return S_OK;
6275 return S_FALSE;
6278 /******************************************************************************
6279 * WriteClassStg [OLE32.@]
6281 * This method will store the specified CLSID in the specified storage object
6283 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6285 HRESULT hRes;
6287 if(!pStg)
6288 return E_INVALIDARG;
6290 hRes = IStorage_SetClass(pStg, rclsid);
6292 return hRes;
6295 /***********************************************************************
6296 * ReadClassStg (OLE32.@)
6298 * This method reads the CLSID previously written to a storage object with
6299 * the WriteClassStg.
6301 * PARAMS
6302 * pstg [I] IStorage pointer
6303 * pclsid [O] Pointer to where the CLSID is written
6305 * RETURNS
6306 * Success: S_OK.
6307 * Failure: HRESULT code.
6309 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6311 STATSTG pstatstg;
6312 HRESULT hRes;
6314 TRACE("(%p, %p)\n", pstg, pclsid);
6316 if(!pstg || !pclsid)
6317 return E_INVALIDARG;
6320 * read a STATSTG structure (contains the clsid) from the storage
6322 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6324 if(SUCCEEDED(hRes))
6325 *pclsid=pstatstg.clsid;
6327 return hRes;
6330 /***********************************************************************
6331 * OleLoadFromStream (OLE32.@)
6333 * This function loads an object from stream
6335 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6337 CLSID clsid;
6338 HRESULT res;
6339 LPPERSISTSTREAM xstm;
6341 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6343 res=ReadClassStm(pStm,&clsid);
6344 if (!SUCCEEDED(res))
6345 return res;
6346 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6347 if (!SUCCEEDED(res))
6348 return res;
6349 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6350 if (!SUCCEEDED(res)) {
6351 IUnknown_Release((IUnknown*)*ppvObj);
6352 return res;
6354 res=IPersistStream_Load(xstm,pStm);
6355 IPersistStream_Release(xstm);
6356 /* FIXME: all refcounts ok at this point? I think they should be:
6357 * pStm : unchanged
6358 * ppvObj : 1
6359 * xstm : 0 (released)
6361 return res;
6364 /***********************************************************************
6365 * OleSaveToStream (OLE32.@)
6367 * This function saves an object with the IPersistStream interface on it
6368 * to the specified stream.
6370 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6373 CLSID clsid;
6374 HRESULT res;
6376 TRACE("(%p,%p)\n",pPStm,pStm);
6378 res=IPersistStream_GetClassID(pPStm,&clsid);
6380 if (SUCCEEDED(res)){
6382 res=WriteClassStm(pStm,&clsid);
6384 if (SUCCEEDED(res))
6386 res=IPersistStream_Save(pPStm,pStm,TRUE);
6389 TRACE("Finished Save\n");
6390 return res;
6393 /****************************************************************************
6394 * This method validate a STGM parameter that can contain the values below
6396 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6397 * The stgm values contained in 0xffff0000 are bitmasks.
6399 * STGM_DIRECT 0x00000000
6400 * STGM_TRANSACTED 0x00010000
6401 * STGM_SIMPLE 0x08000000
6403 * STGM_READ 0x00000000
6404 * STGM_WRITE 0x00000001
6405 * STGM_READWRITE 0x00000002
6407 * STGM_SHARE_DENY_NONE 0x00000040
6408 * STGM_SHARE_DENY_READ 0x00000030
6409 * STGM_SHARE_DENY_WRITE 0x00000020
6410 * STGM_SHARE_EXCLUSIVE 0x00000010
6412 * STGM_PRIORITY 0x00040000
6413 * STGM_DELETEONRELEASE 0x04000000
6415 * STGM_CREATE 0x00001000
6416 * STGM_CONVERT 0x00020000
6417 * STGM_FAILIFTHERE 0x00000000
6419 * STGM_NOSCRATCH 0x00100000
6420 * STGM_NOSNAPSHOT 0x00200000
6422 static HRESULT validateSTGM(DWORD stgm)
6424 DWORD access = STGM_ACCESS_MODE(stgm);
6425 DWORD share = STGM_SHARE_MODE(stgm);
6426 DWORD create = STGM_CREATE_MODE(stgm);
6428 if (stgm&~STGM_KNOWN_FLAGS)
6430 ERR("unknown flags %08x\n", stgm);
6431 return E_FAIL;
6434 switch (access)
6436 case STGM_READ:
6437 case STGM_WRITE:
6438 case STGM_READWRITE:
6439 break;
6440 default:
6441 return E_FAIL;
6444 switch (share)
6446 case STGM_SHARE_DENY_NONE:
6447 case STGM_SHARE_DENY_READ:
6448 case STGM_SHARE_DENY_WRITE:
6449 case STGM_SHARE_EXCLUSIVE:
6450 break;
6451 default:
6452 return E_FAIL;
6455 switch (create)
6457 case STGM_CREATE:
6458 case STGM_FAILIFTHERE:
6459 break;
6460 default:
6461 return E_FAIL;
6465 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6467 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6468 return E_FAIL;
6471 * STGM_CREATE | STGM_CONVERT
6472 * if both are false, STGM_FAILIFTHERE is set to TRUE
6474 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6475 return E_FAIL;
6478 * STGM_NOSCRATCH requires STGM_TRANSACTED
6480 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6481 return E_FAIL;
6484 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6485 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6487 if ( (stgm & STGM_NOSNAPSHOT) &&
6488 (!(stgm & STGM_TRANSACTED) ||
6489 share == STGM_SHARE_EXCLUSIVE ||
6490 share == STGM_SHARE_DENY_WRITE) )
6491 return E_FAIL;
6493 return S_OK;
6496 /****************************************************************************
6497 * GetShareModeFromSTGM
6499 * This method will return a share mode flag from a STGM value.
6500 * The STGM value is assumed valid.
6502 static DWORD GetShareModeFromSTGM(DWORD stgm)
6504 switch (STGM_SHARE_MODE(stgm))
6506 case STGM_SHARE_DENY_NONE:
6507 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6508 case STGM_SHARE_DENY_READ:
6509 return FILE_SHARE_WRITE;
6510 case STGM_SHARE_DENY_WRITE:
6511 return FILE_SHARE_READ;
6512 case STGM_SHARE_EXCLUSIVE:
6513 return 0;
6515 ERR("Invalid share mode!\n");
6516 assert(0);
6517 return 0;
6520 /****************************************************************************
6521 * GetAccessModeFromSTGM
6523 * This method will return an access mode flag from a STGM value.
6524 * The STGM value is assumed valid.
6526 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6528 switch (STGM_ACCESS_MODE(stgm))
6530 case STGM_READ:
6531 return GENERIC_READ;
6532 case STGM_WRITE:
6533 case STGM_READWRITE:
6534 return GENERIC_READ | GENERIC_WRITE;
6536 ERR("Invalid access mode!\n");
6537 assert(0);
6538 return 0;
6541 /****************************************************************************
6542 * GetCreationModeFromSTGM
6544 * This method will return a creation mode flag from a STGM value.
6545 * The STGM value is assumed valid.
6547 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6549 switch(STGM_CREATE_MODE(stgm))
6551 case STGM_CREATE:
6552 return CREATE_ALWAYS;
6553 case STGM_CONVERT:
6554 FIXME("STGM_CONVERT not implemented!\n");
6555 return CREATE_NEW;
6556 case STGM_FAILIFTHERE:
6557 return CREATE_NEW;
6559 ERR("Invalid create mode!\n");
6560 assert(0);
6561 return 0;
6565 /*************************************************************************
6566 * OLECONVERT_LoadOLE10 [Internal]
6568 * Loads the OLE10 STREAM to memory
6570 * PARAMS
6571 * pOleStream [I] The OLESTREAM
6572 * pData [I] Data Structure for the OLESTREAM Data
6574 * RETURNS
6575 * Success: S_OK
6576 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6577 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6579 * NOTES
6580 * This function is used by OleConvertOLESTREAMToIStorage only.
6582 * Memory allocated for pData must be freed by the caller
6584 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6586 DWORD dwSize;
6587 HRESULT hRes = S_OK;
6588 int nTryCnt=0;
6589 int max_try = 6;
6591 pData->pData = NULL;
6592 pData->pstrOleObjFileName = (CHAR *) NULL;
6594 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6596 /* Get the OleID */
6597 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6598 if(dwSize != sizeof(pData->dwOleID))
6600 hRes = CONVERT10_E_OLESTREAM_GET;
6602 else if(pData->dwOleID != OLESTREAM_ID)
6604 hRes = CONVERT10_E_OLESTREAM_FMT;
6606 else
6608 hRes = S_OK;
6609 break;
6613 if(hRes == S_OK)
6615 /* Get the TypeID...more info needed for this field */
6616 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6617 if(dwSize != sizeof(pData->dwTypeID))
6619 hRes = CONVERT10_E_OLESTREAM_GET;
6622 if(hRes == S_OK)
6624 if(pData->dwTypeID != 0)
6626 /* Get the length of the OleTypeName */
6627 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6628 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6630 hRes = CONVERT10_E_OLESTREAM_GET;
6633 if(hRes == S_OK)
6635 if(pData->dwOleTypeNameLength > 0)
6637 /* Get the OleTypeName */
6638 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6639 if(dwSize != pData->dwOleTypeNameLength)
6641 hRes = CONVERT10_E_OLESTREAM_GET;
6645 if(bStrem1)
6647 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6648 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6650 hRes = CONVERT10_E_OLESTREAM_GET;
6652 if(hRes == S_OK)
6654 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6655 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6656 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6657 if(pData->pstrOleObjFileName)
6659 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6660 if(dwSize != pData->dwOleObjFileNameLength)
6662 hRes = CONVERT10_E_OLESTREAM_GET;
6665 else
6666 hRes = CONVERT10_E_OLESTREAM_GET;
6669 else
6671 /* Get the Width of the Metafile */
6672 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6673 if(dwSize != sizeof(pData->dwMetaFileWidth))
6675 hRes = CONVERT10_E_OLESTREAM_GET;
6677 if(hRes == S_OK)
6679 /* Get the Height of the Metafile */
6680 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6681 if(dwSize != sizeof(pData->dwMetaFileHeight))
6683 hRes = CONVERT10_E_OLESTREAM_GET;
6687 if(hRes == S_OK)
6689 /* Get the Length of the Data */
6690 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6691 if(dwSize != sizeof(pData->dwDataLength))
6693 hRes = CONVERT10_E_OLESTREAM_GET;
6697 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6699 if(!bStrem1) /* if it is a second OLE stream data */
6701 pData->dwDataLength -= 8;
6702 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6703 if(dwSize != sizeof(pData->strUnknown))
6705 hRes = CONVERT10_E_OLESTREAM_GET;
6709 if(hRes == S_OK)
6711 if(pData->dwDataLength > 0)
6713 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6715 /* Get Data (ex. IStorage, Metafile, or BMP) */
6716 if(pData->pData)
6718 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6719 if(dwSize != pData->dwDataLength)
6721 hRes = CONVERT10_E_OLESTREAM_GET;
6724 else
6726 hRes = CONVERT10_E_OLESTREAM_GET;
6732 return hRes;
6735 /*************************************************************************
6736 * OLECONVERT_SaveOLE10 [Internal]
6738 * Saves the OLE10 STREAM From memory
6740 * PARAMS
6741 * pData [I] Data Structure for the OLESTREAM Data
6742 * pOleStream [I] The OLESTREAM to save
6744 * RETURNS
6745 * Success: S_OK
6746 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6748 * NOTES
6749 * This function is used by OleConvertIStorageToOLESTREAM only.
6752 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6754 DWORD dwSize;
6755 HRESULT hRes = S_OK;
6758 /* Set the OleID */
6759 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6760 if(dwSize != sizeof(pData->dwOleID))
6762 hRes = CONVERT10_E_OLESTREAM_PUT;
6765 if(hRes == S_OK)
6767 /* Set the TypeID */
6768 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6769 if(dwSize != sizeof(pData->dwTypeID))
6771 hRes = CONVERT10_E_OLESTREAM_PUT;
6775 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6777 /* Set the Length of the OleTypeName */
6778 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6779 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6781 hRes = CONVERT10_E_OLESTREAM_PUT;
6784 if(hRes == S_OK)
6786 if(pData->dwOleTypeNameLength > 0)
6788 /* Set the OleTypeName */
6789 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6790 if(dwSize != pData->dwOleTypeNameLength)
6792 hRes = CONVERT10_E_OLESTREAM_PUT;
6797 if(hRes == S_OK)
6799 /* Set the width of the Metafile */
6800 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6801 if(dwSize != sizeof(pData->dwMetaFileWidth))
6803 hRes = CONVERT10_E_OLESTREAM_PUT;
6807 if(hRes == S_OK)
6809 /* Set the height of the Metafile */
6810 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6811 if(dwSize != sizeof(pData->dwMetaFileHeight))
6813 hRes = CONVERT10_E_OLESTREAM_PUT;
6817 if(hRes == S_OK)
6819 /* Set the length of the Data */
6820 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6821 if(dwSize != sizeof(pData->dwDataLength))
6823 hRes = CONVERT10_E_OLESTREAM_PUT;
6827 if(hRes == S_OK)
6829 if(pData->dwDataLength > 0)
6831 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6832 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6833 if(dwSize != pData->dwDataLength)
6835 hRes = CONVERT10_E_OLESTREAM_PUT;
6840 return hRes;
6843 /*************************************************************************
6844 * OLECONVERT_GetOLE20FromOLE10[Internal]
6846 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6847 * opens it, and copies the content to the dest IStorage for
6848 * OleConvertOLESTREAMToIStorage
6851 * PARAMS
6852 * pDestStorage [I] The IStorage to copy the data to
6853 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6854 * nBufferLength [I] The size of the buffer
6856 * RETURNS
6857 * Nothing
6859 * NOTES
6863 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6865 HRESULT hRes;
6866 HANDLE hFile;
6867 IStorage *pTempStorage;
6868 DWORD dwNumOfBytesWritten;
6869 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6870 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6872 /* Create a temp File */
6873 GetTempPathW(MAX_PATH, wstrTempDir);
6874 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6875 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6877 if(hFile != INVALID_HANDLE_VALUE)
6879 /* Write IStorage Data to File */
6880 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6881 CloseHandle(hFile);
6883 /* Open and copy temp storage to the Dest Storage */
6884 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6885 if(hRes == S_OK)
6887 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6888 StorageBaseImpl_Release(pTempStorage);
6890 DeleteFileW(wstrTempFile);
6895 /*************************************************************************
6896 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6898 * Saves the OLE10 STREAM From memory
6900 * PARAMS
6901 * pStorage [I] The Src IStorage to copy
6902 * pData [I] The Dest Memory to write to.
6904 * RETURNS
6905 * The size in bytes allocated for pData
6907 * NOTES
6908 * Memory allocated for pData must be freed by the caller
6910 * Used by OleConvertIStorageToOLESTREAM only.
6913 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6915 HANDLE hFile;
6916 HRESULT hRes;
6917 DWORD nDataLength = 0;
6918 IStorage *pTempStorage;
6919 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6920 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6922 *pData = NULL;
6924 /* Create temp Storage */
6925 GetTempPathW(MAX_PATH, wstrTempDir);
6926 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6927 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6929 if(hRes == S_OK)
6931 /* Copy Src Storage to the Temp Storage */
6932 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6933 StorageBaseImpl_Release(pTempStorage);
6935 /* Open Temp Storage as a file and copy to memory */
6936 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6937 if(hFile != INVALID_HANDLE_VALUE)
6939 nDataLength = GetFileSize(hFile, NULL);
6940 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6941 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6942 CloseHandle(hFile);
6944 DeleteFileW(wstrTempFile);
6946 return nDataLength;
6949 /*************************************************************************
6950 * OLECONVERT_CreateOleStream [Internal]
6952 * Creates the "\001OLE" stream in the IStorage if necessary.
6954 * PARAMS
6955 * pStorage [I] Dest storage to create the stream in
6957 * RETURNS
6958 * Nothing
6960 * NOTES
6961 * This function is used by OleConvertOLESTREAMToIStorage only.
6963 * This stream is still unknown, MS Word seems to have extra data
6964 * but since the data is stored in the OLESTREAM there should be
6965 * no need to recreate the stream. If the stream is manually
6966 * deleted it will create it with this default data.
6969 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6971 HRESULT hRes;
6972 IStream *pStream;
6973 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6974 BYTE pOleStreamHeader [] =
6976 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6977 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6978 0x00, 0x00, 0x00, 0x00
6981 /* Create stream if not present */
6982 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6983 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6985 if(hRes == S_OK)
6987 /* Write default Data */
6988 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6989 IStream_Release(pStream);
6993 /* write a string to a stream, preceded by its length */
6994 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6996 HRESULT r;
6997 LPSTR str;
6998 DWORD len = 0;
7000 if( string )
7001 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7002 r = IStream_Write( stm, &len, sizeof(len), NULL);
7003 if( FAILED( r ) )
7004 return r;
7005 if(len == 0)
7006 return r;
7007 str = CoTaskMemAlloc( len );
7008 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7009 r = IStream_Write( stm, str, len, NULL);
7010 CoTaskMemFree( str );
7011 return r;
7014 /* read a string preceded by its length from a stream */
7015 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7017 HRESULT r;
7018 DWORD len, count = 0;
7019 LPSTR str;
7020 LPWSTR wstr;
7022 r = IStream_Read( stm, &len, sizeof(len), &count );
7023 if( FAILED( r ) )
7024 return r;
7025 if( count != sizeof(len) )
7026 return E_OUTOFMEMORY;
7028 TRACE("%d bytes\n",len);
7030 str = CoTaskMemAlloc( len );
7031 if( !str )
7032 return E_OUTOFMEMORY;
7033 count = 0;
7034 r = IStream_Read( stm, str, len, &count );
7035 if( FAILED( r ) )
7036 return r;
7037 if( count != len )
7039 CoTaskMemFree( str );
7040 return E_OUTOFMEMORY;
7043 TRACE("Read string %s\n",debugstr_an(str,len));
7045 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7046 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7047 if( wstr )
7048 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7049 CoTaskMemFree( str );
7051 *string = wstr;
7053 return r;
7057 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7058 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7060 IStream *pstm;
7061 HRESULT r = S_OK;
7062 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7064 static const BYTE unknown1[12] =
7065 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7066 0xFF, 0xFF, 0xFF, 0xFF};
7067 static const BYTE unknown2[16] =
7068 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7069 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7071 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7072 debugstr_w(lpszUserType), debugstr_w(szClipName),
7073 debugstr_w(szProgIDName));
7075 /* Create a CompObj stream if it doesn't exist */
7076 r = IStorage_CreateStream(pstg, szwStreamName,
7077 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7078 if( FAILED (r) )
7079 return r;
7081 /* Write CompObj Structure to stream */
7082 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7084 if( SUCCEEDED( r ) )
7085 r = WriteClassStm( pstm, clsid );
7087 if( SUCCEEDED( r ) )
7088 r = STREAM_WriteString( pstm, lpszUserType );
7089 if( SUCCEEDED( r ) )
7090 r = STREAM_WriteString( pstm, szClipName );
7091 if( SUCCEEDED( r ) )
7092 r = STREAM_WriteString( pstm, szProgIDName );
7093 if( SUCCEEDED( r ) )
7094 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7096 IStream_Release( pstm );
7098 return r;
7101 /***********************************************************************
7102 * WriteFmtUserTypeStg (OLE32.@)
7104 HRESULT WINAPI WriteFmtUserTypeStg(
7105 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7107 HRESULT r;
7108 WCHAR szwClipName[0x40];
7109 CLSID clsid = CLSID_NULL;
7110 LPWSTR wstrProgID = NULL;
7111 DWORD n;
7113 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7115 /* get the clipboard format name */
7116 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7117 szwClipName[n]=0;
7119 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7121 /* FIXME: There's room to save a CLSID and its ProgID, but
7122 the CLSID is not looked up in the registry and in all the
7123 tests I wrote it was CLSID_NULL. Where does it come from?
7126 /* get the real program ID. This may fail, but that's fine */
7127 ProgIDFromCLSID(&clsid, &wstrProgID);
7129 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7131 r = STORAGE_WriteCompObj( pstg, &clsid,
7132 lpszUserType, szwClipName, wstrProgID );
7134 CoTaskMemFree(wstrProgID);
7136 return r;
7140 /******************************************************************************
7141 * ReadFmtUserTypeStg [OLE32.@]
7143 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7145 HRESULT r;
7146 IStream *stm = 0;
7147 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7148 unsigned char unknown1[12];
7149 unsigned char unknown2[16];
7150 DWORD count;
7151 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7152 CLSID clsid;
7154 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7156 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7157 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7158 if( FAILED ( r ) )
7160 WARN("Failed to open stream r = %08x\n", r);
7161 return r;
7164 /* read the various parts of the structure */
7165 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7166 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7167 goto end;
7168 r = ReadClassStm( stm, &clsid );
7169 if( FAILED( r ) )
7170 goto end;
7172 r = STREAM_ReadString( stm, &szCLSIDName );
7173 if( FAILED( r ) )
7174 goto end;
7176 r = STREAM_ReadString( stm, &szOleTypeName );
7177 if( FAILED( r ) )
7178 goto end;
7180 r = STREAM_ReadString( stm, &szProgIDName );
7181 if( FAILED( r ) )
7182 goto end;
7184 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7185 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7186 goto end;
7188 /* ok, success... now we just need to store what we found */
7189 if( pcf )
7190 *pcf = RegisterClipboardFormatW( szOleTypeName );
7191 CoTaskMemFree( szOleTypeName );
7193 if( lplpszUserType )
7194 *lplpszUserType = szCLSIDName;
7195 CoTaskMemFree( szProgIDName );
7197 end:
7198 IStream_Release( stm );
7200 return r;
7204 /*************************************************************************
7205 * OLECONVERT_CreateCompObjStream [Internal]
7207 * Creates a "\001CompObj" is the destination IStorage if necessary.
7209 * PARAMS
7210 * pStorage [I] The dest IStorage to create the CompObj Stream
7211 * if necessary.
7212 * strOleTypeName [I] The ProgID
7214 * RETURNS
7215 * Success: S_OK
7216 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7218 * NOTES
7219 * This function is used by OleConvertOLESTREAMToIStorage only.
7221 * The stream data is stored in the OLESTREAM and there should be
7222 * no need to recreate the stream. If the stream is manually
7223 * deleted it will attempt to create it by querying the registry.
7227 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7229 IStream *pStream;
7230 HRESULT hStorageRes, hRes = S_OK;
7231 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7232 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7233 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7235 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7236 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7238 /* Initialize the CompObj structure */
7239 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7240 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7241 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7244 /* Create a CompObj stream if it doesn't exist */
7245 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7246 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7247 if(hStorageRes == S_OK)
7249 /* copy the OleTypeName to the compobj struct */
7250 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7251 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7253 /* copy the OleTypeName to the compobj struct */
7254 /* Note: in the test made, these were Identical */
7255 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7256 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7258 /* Get the CLSID */
7259 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7260 bufferW, OLESTREAM_MAX_STR_LEN );
7261 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7263 if(hRes == S_OK)
7265 HKEY hKey;
7266 LONG hErr;
7267 /* Get the CLSID Default Name from the Registry */
7268 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7269 if(hErr == ERROR_SUCCESS)
7271 char strTemp[OLESTREAM_MAX_STR_LEN];
7272 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7273 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7274 if(hErr == ERROR_SUCCESS)
7276 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7278 RegCloseKey(hKey);
7282 /* Write CompObj Structure to stream */
7283 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7285 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7287 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7288 if(IStorageCompObj.dwCLSIDNameLength > 0)
7290 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7292 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7293 if(IStorageCompObj.dwOleTypeNameLength > 0)
7295 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7297 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7298 if(IStorageCompObj.dwProgIDNameLength > 0)
7300 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7302 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7303 IStream_Release(pStream);
7305 return hRes;
7309 /*************************************************************************
7310 * OLECONVERT_CreateOlePresStream[Internal]
7312 * Creates the "\002OlePres000" Stream with the Metafile data
7314 * PARAMS
7315 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7316 * dwExtentX [I] Width of the Metafile
7317 * dwExtentY [I] Height of the Metafile
7318 * pData [I] Metafile data
7319 * dwDataLength [I] Size of the Metafile data
7321 * RETURNS
7322 * Success: S_OK
7323 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7325 * NOTES
7326 * This function is used by OleConvertOLESTREAMToIStorage only.
7329 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7331 HRESULT hRes;
7332 IStream *pStream;
7333 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7334 BYTE pOlePresStreamHeader [] =
7336 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7337 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7338 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7339 0x00, 0x00, 0x00, 0x00
7342 BYTE pOlePresStreamHeaderEmpty [] =
7344 0x00, 0x00, 0x00, 0x00,
7345 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7346 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7347 0x00, 0x00, 0x00, 0x00
7350 /* Create the OlePres000 Stream */
7351 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7352 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7354 if(hRes == S_OK)
7356 DWORD nHeaderSize;
7357 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7359 memset(&OlePres, 0, sizeof(OlePres));
7360 /* Do we have any metafile data to save */
7361 if(dwDataLength > 0)
7363 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7364 nHeaderSize = sizeof(pOlePresStreamHeader);
7366 else
7368 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7369 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7371 /* Set width and height of the metafile */
7372 OlePres.dwExtentX = dwExtentX;
7373 OlePres.dwExtentY = -dwExtentY;
7375 /* Set Data and Length */
7376 if(dwDataLength > sizeof(METAFILEPICT16))
7378 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7379 OlePres.pData = &(pData[8]);
7381 /* Save OlePres000 Data to Stream */
7382 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7383 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7384 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7385 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7386 if(OlePres.dwSize > 0)
7388 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7390 IStream_Release(pStream);
7394 /*************************************************************************
7395 * OLECONVERT_CreateOle10NativeStream [Internal]
7397 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7399 * PARAMS
7400 * pStorage [I] Dest storage to create the stream in
7401 * pData [I] Ole10 Native Data (ex. bmp)
7402 * dwDataLength [I] Size of the Ole10 Native Data
7404 * RETURNS
7405 * Nothing
7407 * NOTES
7408 * This function is used by OleConvertOLESTREAMToIStorage only.
7410 * Might need to verify the data and return appropriate error message
7413 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7415 HRESULT hRes;
7416 IStream *pStream;
7417 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7419 /* Create the Ole10Native Stream */
7420 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7421 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7423 if(hRes == S_OK)
7425 /* Write info to stream */
7426 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7427 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7428 IStream_Release(pStream);
7433 /*************************************************************************
7434 * OLECONVERT_GetOLE10ProgID [Internal]
7436 * Finds the ProgID (or OleTypeID) from the IStorage
7438 * PARAMS
7439 * pStorage [I] The Src IStorage to get the ProgID
7440 * strProgID [I] the ProgID string to get
7441 * dwSize [I] the size of the string
7443 * RETURNS
7444 * Success: S_OK
7445 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7447 * NOTES
7448 * This function is used by OleConvertIStorageToOLESTREAM only.
7452 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7454 HRESULT hRes;
7455 IStream *pStream;
7456 LARGE_INTEGER iSeekPos;
7457 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7458 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7460 /* Open the CompObj Stream */
7461 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7462 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7463 if(hRes == S_OK)
7466 /*Get the OleType from the CompObj Stream */
7467 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7468 iSeekPos.u.HighPart = 0;
7470 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7471 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7472 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7473 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7474 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7475 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7476 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7478 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7479 if(*dwSize > 0)
7481 IStream_Read(pStream, strProgID, *dwSize, NULL);
7483 IStream_Release(pStream);
7485 else
7487 STATSTG stat;
7488 LPOLESTR wstrProgID;
7490 /* Get the OleType from the registry */
7491 REFCLSID clsid = &(stat.clsid);
7492 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7493 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7494 if(hRes == S_OK)
7496 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7500 return hRes;
7503 /*************************************************************************
7504 * OLECONVERT_GetOle10PresData [Internal]
7506 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7508 * PARAMS
7509 * pStorage [I] Src IStroage
7510 * pOleStream [I] Dest OleStream Mem Struct
7512 * RETURNS
7513 * Nothing
7515 * NOTES
7516 * This function is used by OleConvertIStorageToOLESTREAM only.
7518 * Memory allocated for pData must be freed by the caller
7522 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7525 HRESULT hRes;
7526 IStream *pStream;
7527 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7529 /* Initialize Default data for OLESTREAM */
7530 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7531 pOleStreamData[0].dwTypeID = 2;
7532 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7533 pOleStreamData[1].dwTypeID = 0;
7534 pOleStreamData[0].dwMetaFileWidth = 0;
7535 pOleStreamData[0].dwMetaFileHeight = 0;
7536 pOleStreamData[0].pData = NULL;
7537 pOleStreamData[1].pData = NULL;
7539 /* Open Ole10Native Stream */
7540 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7541 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7542 if(hRes == S_OK)
7545 /* Read Size and Data */
7546 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7547 if(pOleStreamData->dwDataLength > 0)
7549 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7550 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7552 IStream_Release(pStream);
7558 /*************************************************************************
7559 * OLECONVERT_GetOle20PresData[Internal]
7561 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7563 * PARAMS
7564 * pStorage [I] Src IStroage
7565 * pOleStreamData [I] Dest OleStream Mem Struct
7567 * RETURNS
7568 * Nothing
7570 * NOTES
7571 * This function is used by OleConvertIStorageToOLESTREAM only.
7573 * Memory allocated for pData must be freed by the caller
7575 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7577 HRESULT hRes;
7578 IStream *pStream;
7579 OLECONVERT_ISTORAGE_OLEPRES olePress;
7580 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7582 /* Initialize Default data for OLESTREAM */
7583 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7584 pOleStreamData[0].dwTypeID = 2;
7585 pOleStreamData[0].dwMetaFileWidth = 0;
7586 pOleStreamData[0].dwMetaFileHeight = 0;
7587 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7588 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7589 pOleStreamData[1].dwTypeID = 0;
7590 pOleStreamData[1].dwOleTypeNameLength = 0;
7591 pOleStreamData[1].strOleTypeName[0] = 0;
7592 pOleStreamData[1].dwMetaFileWidth = 0;
7593 pOleStreamData[1].dwMetaFileHeight = 0;
7594 pOleStreamData[1].pData = NULL;
7595 pOleStreamData[1].dwDataLength = 0;
7598 /* Open OlePress000 stream */
7599 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7600 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7601 if(hRes == S_OK)
7603 LARGE_INTEGER iSeekPos;
7604 METAFILEPICT16 MetaFilePict;
7605 static const char strMetafilePictName[] = "METAFILEPICT";
7607 /* Set the TypeID for a Metafile */
7608 pOleStreamData[1].dwTypeID = 5;
7610 /* Set the OleTypeName to Metafile */
7611 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7612 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7614 iSeekPos.u.HighPart = 0;
7615 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7617 /* Get Presentation Data */
7618 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7619 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7620 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7621 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7623 /*Set width and Height */
7624 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7625 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7626 if(olePress.dwSize > 0)
7628 /* Set Length */
7629 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7631 /* Set MetaFilePict struct */
7632 MetaFilePict.mm = 8;
7633 MetaFilePict.xExt = olePress.dwExtentX;
7634 MetaFilePict.yExt = olePress.dwExtentY;
7635 MetaFilePict.hMF = 0;
7637 /* Get Metafile Data */
7638 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7639 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7640 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7642 IStream_Release(pStream);
7646 /*************************************************************************
7647 * OleConvertOLESTREAMToIStorage [OLE32.@]
7649 * Read info on MSDN
7651 * TODO
7652 * DVTARGETDEVICE parameter is not handled
7653 * Still unsure of some mem fields for OLE 10 Stream
7654 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7655 * and "\001OLE" streams
7658 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7659 LPOLESTREAM pOleStream,
7660 LPSTORAGE pstg,
7661 const DVTARGETDEVICE* ptd)
7663 int i;
7664 HRESULT hRes=S_OK;
7665 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7667 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7669 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7671 if(ptd != NULL)
7673 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7676 if(pstg == NULL || pOleStream == NULL)
7678 hRes = E_INVALIDARG;
7681 if(hRes == S_OK)
7683 /* Load the OLESTREAM to Memory */
7684 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7687 if(hRes == S_OK)
7689 /* Load the OLESTREAM to Memory (part 2)*/
7690 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7693 if(hRes == S_OK)
7696 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7698 /* Do we have the IStorage Data in the OLESTREAM */
7699 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7701 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7702 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7704 else
7706 /* It must be an original OLE 1.0 source */
7707 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7710 else
7712 /* It must be an original OLE 1.0 source */
7713 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7716 /* Create CompObj Stream if necessary */
7717 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7718 if(hRes == S_OK)
7720 /*Create the Ole Stream if necessary */
7721 OLECONVERT_CreateOleStream(pstg);
7726 /* Free allocated memory */
7727 for(i=0; i < 2; i++)
7729 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7730 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7731 pOleStreamData[i].pstrOleObjFileName = NULL;
7733 return hRes;
7736 /*************************************************************************
7737 * OleConvertIStorageToOLESTREAM [OLE32.@]
7739 * Read info on MSDN
7741 * Read info on MSDN
7743 * TODO
7744 * Still unsure of some mem fields for OLE 10 Stream
7745 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7746 * and "\001OLE" streams.
7749 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7750 LPSTORAGE pstg,
7751 LPOLESTREAM pOleStream)
7753 int i;
7754 HRESULT hRes = S_OK;
7755 IStream *pStream;
7756 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7757 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7759 TRACE("%p %p\n", pstg, pOleStream);
7761 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7763 if(pstg == NULL || pOleStream == NULL)
7765 hRes = E_INVALIDARG;
7767 if(hRes == S_OK)
7769 /* Get the ProgID */
7770 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7771 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7773 if(hRes == S_OK)
7775 /* Was it originally Ole10 */
7776 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7777 if(hRes == S_OK)
7779 IStream_Release(pStream);
7780 /* Get Presentation Data for Ole10Native */
7781 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7783 else
7785 /* Get Presentation Data (OLE20) */
7786 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7789 /* Save OLESTREAM */
7790 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7791 if(hRes == S_OK)
7793 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7798 /* Free allocated memory */
7799 for(i=0; i < 2; i++)
7801 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7804 return hRes;
7807 /***********************************************************************
7808 * GetConvertStg (OLE32.@)
7810 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7811 FIXME("unimplemented stub!\n");
7812 return E_FAIL;
7815 /******************************************************************************
7816 * StgIsStorageFile [OLE32.@]
7817 * Verify if the file contains a storage object
7819 * PARAMS
7820 * fn [ I] Filename
7822 * RETURNS
7823 * S_OK if file has magic bytes as a storage object
7824 * S_FALSE if file is not storage
7826 HRESULT WINAPI
7827 StgIsStorageFile(LPCOLESTR fn)
7829 HANDLE hf;
7830 BYTE magic[8];
7831 DWORD bytes_read;
7833 TRACE("%s\n", debugstr_w(fn));
7834 hf = CreateFileW(fn, GENERIC_READ,
7835 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7836 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7838 if (hf == INVALID_HANDLE_VALUE)
7839 return STG_E_FILENOTFOUND;
7841 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7843 WARN(" unable to read file\n");
7844 CloseHandle(hf);
7845 return S_FALSE;
7848 CloseHandle(hf);
7850 if (bytes_read != 8) {
7851 WARN(" too short\n");
7852 return S_FALSE;
7855 if (!memcmp(magic,STORAGE_magic,8)) {
7856 WARN(" -> YES\n");
7857 return S_OK;
7860 WARN(" -> Invalid header.\n");
7861 return S_FALSE;
7864 /***********************************************************************
7865 * WriteClassStm (OLE32.@)
7867 * Writes a CLSID to a stream.
7869 * PARAMS
7870 * pStm [I] Stream to write to.
7871 * rclsid [I] CLSID to write.
7873 * RETURNS
7874 * Success: S_OK.
7875 * Failure: HRESULT code.
7877 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7879 TRACE("(%p,%p)\n",pStm,rclsid);
7881 if (!pStm || !rclsid)
7882 return E_INVALIDARG;
7884 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7887 /***********************************************************************
7888 * ReadClassStm (OLE32.@)
7890 * Reads a CLSID from a stream.
7892 * PARAMS
7893 * pStm [I] Stream to read from.
7894 * rclsid [O] CLSID to read.
7896 * RETURNS
7897 * Success: S_OK.
7898 * Failure: HRESULT code.
7900 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7902 ULONG nbByte;
7903 HRESULT res;
7905 TRACE("(%p,%p)\n",pStm,pclsid);
7907 if (!pStm || !pclsid)
7908 return E_INVALIDARG;
7910 /* clear the output args */
7911 *pclsid = CLSID_NULL;
7913 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7915 if (FAILED(res))
7916 return res;
7918 if (nbByte != sizeof(CLSID))
7919 return STG_E_READFAULT;
7920 else
7921 return S_OK;