wined3d: Add modifier support to tex and friends.
[wine/wine-gecko.git] / dlls / ole32 / storage32.c
blob212c1ff965f3196f53ba4c0e360bbc75e751e1af
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 = This;
319 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
321 *ppvObject = &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(iface, 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;
982 /* Can't create a stream on read-only storage */
983 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
984 return STG_E_ACCESSDENIED;
987 * Check that we're compatible with the parent's storage mode
988 * if not in transacted mode
990 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
991 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
992 return STG_E_ACCESSDENIED;
995 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
996 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
999 * Initialize the out parameter
1001 *ppstm = 0;
1004 * Create a property enumeration to search the properties
1006 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1007 This->rootPropertySetIndex);
1009 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1010 pwcsName,
1011 &currentProperty);
1013 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1015 if (foundPropertyIndex != PROPERTY_NULL)
1018 * An element with this name already exists
1020 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1022 StgStreamImpl *strm;
1024 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
1026 if (strm->ownerProperty == foundPropertyIndex)
1028 TRACE("Stream deleted %p\n", strm);
1029 strm->parentStorage = NULL;
1030 list_remove(&strm->StrmListEntry);
1033 IStorage_DestroyElement(iface, pwcsName);
1035 else
1036 return STG_E_FILEALREADYEXISTS;
1038 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1040 WARN("read-only storage\n");
1041 return STG_E_ACCESSDENIED;
1045 * memset the empty property
1047 memset(&newStreamProperty, 0, sizeof(StgProperty));
1049 newStreamProperty.sizeOfNameString =
1050 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1052 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1053 return STG_E_INVALIDNAME;
1055 strcpyW(newStreamProperty.name, pwcsName);
1057 newStreamProperty.propertyType = PROPTYPE_STREAM;
1058 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1059 newStreamProperty.size.u.LowPart = 0;
1060 newStreamProperty.size.u.HighPart = 0;
1062 newStreamProperty.previousProperty = PROPERTY_NULL;
1063 newStreamProperty.nextProperty = PROPERTY_NULL;
1064 newStreamProperty.dirProperty = PROPERTY_NULL;
1066 /* call CoFileTime to get the current time
1067 newStreamProperty.timeStampS1
1068 newStreamProperty.timeStampD1
1069 newStreamProperty.timeStampS2
1070 newStreamProperty.timeStampD2
1073 /* newStreamProperty.propertyUniqueID */
1076 * Get a free property or create a new one
1078 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1081 * Save the new property into the new property spot
1083 StorageImpl_WriteProperty(
1084 This->ancestorStorage,
1085 newPropertyIndex,
1086 &newStreamProperty);
1089 * Find a spot in the property chain for our newly created property.
1091 updatePropertyChain(
1092 (StorageImpl*)This,
1093 newPropertyIndex,
1094 newStreamProperty);
1097 * Open the stream to return it.
1099 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1101 if (newStream != 0)
1103 *ppstm = (IStream*)newStream;
1106 * Since we are returning a pointer to the interface, we have to nail down
1107 * the reference.
1109 IStream_AddRef(*ppstm);
1111 else
1113 return STG_E_INSUFFICIENTMEMORY;
1116 return S_OK;
1119 /************************************************************************
1120 * Storage32BaseImpl_SetClass (IStorage)
1122 * This method will write the specified CLSID in the property of this
1123 * storage.
1125 * See Windows documentation for more details on IStorage methods.
1127 static HRESULT WINAPI StorageBaseImpl_SetClass(
1128 IStorage* iface,
1129 REFCLSID clsid) /* [in] */
1131 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1132 HRESULT hRes = E_FAIL;
1133 StgProperty curProperty;
1134 BOOL success;
1136 TRACE("(%p, %p)\n", iface, clsid);
1138 success = StorageImpl_ReadProperty(This->ancestorStorage,
1139 This->rootPropertySetIndex,
1140 &curProperty);
1141 if (success)
1143 curProperty.propertyUniqueID = *clsid;
1145 success = StorageImpl_WriteProperty(This->ancestorStorage,
1146 This->rootPropertySetIndex,
1147 &curProperty);
1148 if (success)
1149 hRes = S_OK;
1152 return hRes;
1155 /************************************************************************
1156 ** Storage32Impl implementation
1159 /************************************************************************
1160 * Storage32Impl_CreateStorage (IStorage)
1162 * This method will create the storage object within the provided storage.
1164 * See Windows documentation for more details on IStorage methods.
1166 static HRESULT WINAPI StorageImpl_CreateStorage(
1167 IStorage* iface,
1168 const OLECHAR *pwcsName, /* [string][in] */
1169 DWORD grfMode, /* [in] */
1170 DWORD reserved1, /* [in] */
1171 DWORD reserved2, /* [in] */
1172 IStorage **ppstg) /* [out] */
1174 StorageImpl* const This=(StorageImpl*)iface;
1176 IEnumSTATSTGImpl *propertyEnumeration;
1177 StgProperty currentProperty;
1178 StgProperty newProperty;
1179 ULONG foundPropertyIndex;
1180 ULONG newPropertyIndex;
1181 HRESULT hr;
1183 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1184 iface, debugstr_w(pwcsName), grfMode,
1185 reserved1, reserved2, ppstg);
1188 * Validate parameters
1190 if (ppstg == 0)
1191 return STG_E_INVALIDPOINTER;
1193 if (pwcsName == 0)
1194 return STG_E_INVALIDNAME;
1197 * Initialize the out parameter
1199 *ppstg = NULL;
1202 * Validate the STGM flags
1204 if ( FAILED( validateSTGM(grfMode) ) ||
1205 (grfMode & STGM_DELETEONRELEASE) )
1207 WARN("bad grfMode: 0x%x\n", grfMode);
1208 return STG_E_INVALIDFLAG;
1212 * Check that we're compatible with the parent's storage mode
1214 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1216 WARN("access denied\n");
1217 return STG_E_ACCESSDENIED;
1221 * Create a property enumeration and search the properties
1223 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1224 This->base.rootPropertySetIndex);
1226 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1227 pwcsName,
1228 &currentProperty);
1229 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1231 if (foundPropertyIndex != PROPERTY_NULL)
1234 * An element with this name already exists
1236 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1237 STGM_ACCESS_MODE(This->base.openFlags) != STGM_READ)
1239 hr = IStorage_DestroyElement(iface, pwcsName);
1240 if (FAILED(hr))
1241 return hr;
1243 else
1245 WARN("file already exists\n");
1246 return STG_E_FILEALREADYEXISTS;
1249 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1251 WARN("read-only storage\n");
1252 return STG_E_ACCESSDENIED;
1256 * memset the empty property
1258 memset(&newProperty, 0, sizeof(StgProperty));
1260 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1262 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1264 FIXME("name too long\n");
1265 return STG_E_INVALIDNAME;
1268 strcpyW(newProperty.name, pwcsName);
1270 newProperty.propertyType = PROPTYPE_STORAGE;
1271 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1272 newProperty.size.u.LowPart = 0;
1273 newProperty.size.u.HighPart = 0;
1275 newProperty.previousProperty = PROPERTY_NULL;
1276 newProperty.nextProperty = PROPERTY_NULL;
1277 newProperty.dirProperty = PROPERTY_NULL;
1279 /* call CoFileTime to get the current time
1280 newProperty.timeStampS1
1281 newProperty.timeStampD1
1282 newProperty.timeStampS2
1283 newProperty.timeStampD2
1286 /* newStorageProperty.propertyUniqueID */
1289 * Obtain a free property in the property chain
1291 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1294 * Save the new property into the new property spot
1296 StorageImpl_WriteProperty(
1297 This->base.ancestorStorage,
1298 newPropertyIndex,
1299 &newProperty);
1302 * Find a spot in the property chain for our newly created property.
1304 updatePropertyChain(
1305 This,
1306 newPropertyIndex,
1307 newProperty);
1310 * Open it to get a pointer to return.
1312 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1314 if( (hr != S_OK) || (*ppstg == NULL))
1316 return hr;
1320 return S_OK;
1324 /***************************************************************************
1326 * Internal Method
1328 * Get a free property or create a new one.
1330 static ULONG getFreeProperty(
1331 StorageImpl *storage)
1333 ULONG currentPropertyIndex = 0;
1334 ULONG newPropertyIndex = PROPERTY_NULL;
1335 BOOL readSuccessful = TRUE;
1336 StgProperty currentProperty;
1341 * Start by reading the root property
1343 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1344 currentPropertyIndex,
1345 &currentProperty);
1346 if (readSuccessful)
1348 if (currentProperty.sizeOfNameString == 0)
1351 * The property existis and is available, we found it.
1353 newPropertyIndex = currentPropertyIndex;
1356 else
1359 * We exhausted the property list, we will create more space below
1361 newPropertyIndex = currentPropertyIndex;
1363 currentPropertyIndex++;
1365 } while (newPropertyIndex == PROPERTY_NULL);
1368 * grow the property chain
1370 if (! readSuccessful)
1372 StgProperty emptyProperty;
1373 ULARGE_INTEGER newSize;
1374 ULONG propertyIndex;
1375 ULONG lastProperty = 0;
1376 ULONG blockCount = 0;
1379 * obtain the new count of property blocks
1381 blockCount = BlockChainStream_GetCount(
1382 storage->base.ancestorStorage->rootBlockChain)+1;
1385 * initialize the size used by the property stream
1387 newSize.u.HighPart = 0;
1388 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1391 * add a property block to the property chain
1393 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1396 * memset the empty property in order to initialize the unused newly
1397 * created property
1399 memset(&emptyProperty, 0, sizeof(StgProperty));
1402 * initialize them
1404 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1406 for(
1407 propertyIndex = newPropertyIndex;
1408 propertyIndex < lastProperty;
1409 propertyIndex++)
1411 StorageImpl_WriteProperty(
1412 storage->base.ancestorStorage,
1413 propertyIndex,
1414 &emptyProperty);
1418 return newPropertyIndex;
1421 /****************************************************************************
1423 * Internal Method
1425 * Case insensitive comparison of StgProperty.name by first considering
1426 * their size.
1428 * Returns <0 when newProperty < currentProperty
1429 * >0 when newProperty > currentProperty
1430 * 0 when newProperty == currentProperty
1432 static LONG propertyNameCmp(
1433 const OLECHAR *newProperty,
1434 const OLECHAR *currentProperty)
1436 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1438 if (diff == 0)
1441 * We compare the string themselves only when they are of the same length
1443 diff = lstrcmpiW( newProperty, currentProperty);
1446 return diff;
1449 /****************************************************************************
1451 * Internal Method
1453 * Properly link this new element in the property chain.
1455 static void updatePropertyChain(
1456 StorageImpl *storage,
1457 ULONG newPropertyIndex,
1458 StgProperty newProperty)
1460 StgProperty currentProperty;
1463 * Read the root property
1465 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1466 storage->base.rootPropertySetIndex,
1467 &currentProperty);
1469 if (currentProperty.dirProperty != PROPERTY_NULL)
1472 * The root storage contains some element, therefore, start the research
1473 * for the appropriate location.
1475 BOOL found = 0;
1476 ULONG current, next, previous, currentPropertyId;
1479 * Keep the StgProperty sequence number of the storage first property
1481 currentPropertyId = currentProperty.dirProperty;
1484 * Read
1486 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1487 currentProperty.dirProperty,
1488 &currentProperty);
1490 previous = currentProperty.previousProperty;
1491 next = currentProperty.nextProperty;
1492 current = currentPropertyId;
1494 while (found == 0)
1496 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1498 if (diff < 0)
1500 if (previous != PROPERTY_NULL)
1502 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1503 previous,
1504 &currentProperty);
1505 current = previous;
1507 else
1509 currentProperty.previousProperty = newPropertyIndex;
1510 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1511 current,
1512 &currentProperty);
1513 found = 1;
1516 else if (diff > 0)
1518 if (next != PROPERTY_NULL)
1520 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1521 next,
1522 &currentProperty);
1523 current = next;
1525 else
1527 currentProperty.nextProperty = newPropertyIndex;
1528 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1529 current,
1530 &currentProperty);
1531 found = 1;
1534 else
1537 * Trying to insert an item with the same name in the
1538 * subtree structure.
1540 assert(FALSE);
1543 previous = currentProperty.previousProperty;
1544 next = currentProperty.nextProperty;
1547 else
1550 * The root storage is empty, link the new property to its dir property
1552 currentProperty.dirProperty = newPropertyIndex;
1553 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1554 storage->base.rootPropertySetIndex,
1555 &currentProperty);
1560 /*************************************************************************
1561 * CopyTo (IStorage)
1563 static HRESULT WINAPI StorageImpl_CopyTo(
1564 IStorage* iface,
1565 DWORD ciidExclude, /* [in] */
1566 const IID* rgiidExclude, /* [size_is][unique][in] */
1567 SNB snbExclude, /* [unique][in] */
1568 IStorage* pstgDest) /* [unique][in] */
1570 IEnumSTATSTG *elements = 0;
1571 STATSTG curElement, strStat;
1572 HRESULT hr;
1573 IStorage *pstgTmp, *pstgChild;
1574 IStream *pstrTmp, *pstrChild;
1576 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1577 FIXME("Exclude option not implemented\n");
1579 TRACE("(%p, %d, %p, %p, %p)\n",
1580 iface, ciidExclude, rgiidExclude,
1581 snbExclude, pstgDest);
1584 * Perform a sanity check
1586 if ( pstgDest == 0 )
1587 return STG_E_INVALIDPOINTER;
1590 * Enumerate the elements
1592 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1594 if ( hr != S_OK )
1595 return hr;
1598 * set the class ID
1600 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1601 IStorage_SetClass( pstgDest, &curElement.clsid );
1606 * Obtain the next element
1608 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1610 if ( hr == S_FALSE )
1612 hr = S_OK; /* done, every element has been copied */
1613 break;
1616 if (curElement.type == STGTY_STORAGE)
1619 * open child source storage
1621 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1622 STGM_READ|STGM_SHARE_EXCLUSIVE,
1623 NULL, 0, &pstgChild );
1625 if (hr != S_OK)
1626 break;
1629 * Check if destination storage is not a child of the source
1630 * storage, which will cause an infinite loop
1632 if (pstgChild == pstgDest)
1634 IEnumSTATSTG_Release(elements);
1636 return STG_E_ACCESSDENIED;
1640 * create a new storage in destination storage
1642 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1643 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1644 0, 0,
1645 &pstgTmp );
1647 * if it already exist, don't create a new one use this one
1649 if (hr == STG_E_FILEALREADYEXISTS)
1651 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1652 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1653 NULL, 0, &pstgTmp );
1656 if (hr != S_OK)
1657 break;
1661 * do the copy recursively
1663 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1664 snbExclude, pstgTmp );
1666 IStorage_Release( pstgTmp );
1667 IStorage_Release( pstgChild );
1669 else if (curElement.type == STGTY_STREAM)
1672 * create a new stream in destination storage. If the stream already
1673 * exist, it will be deleted and a new one will be created.
1675 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1676 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1677 0, 0, &pstrTmp );
1679 if (hr != S_OK)
1680 break;
1683 * open child stream storage
1685 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1686 STGM_READ|STGM_SHARE_EXCLUSIVE,
1687 0, &pstrChild );
1689 if (hr != S_OK)
1690 break;
1693 * Get the size of the source stream
1695 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1698 * Set the size of the destination stream.
1700 IStream_SetSize(pstrTmp, strStat.cbSize);
1703 * do the copy
1705 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1706 NULL, NULL );
1708 IStream_Release( pstrTmp );
1709 IStream_Release( pstrChild );
1711 else
1713 WARN("unknown element type: %d\n", curElement.type);
1716 } while (hr == S_OK);
1719 * Clean-up
1721 IEnumSTATSTG_Release(elements);
1723 return hr;
1726 /*************************************************************************
1727 * MoveElementTo (IStorage)
1729 static HRESULT WINAPI StorageImpl_MoveElementTo(
1730 IStorage* iface,
1731 const OLECHAR *pwcsName, /* [string][in] */
1732 IStorage *pstgDest, /* [unique][in] */
1733 const OLECHAR *pwcsNewName,/* [string][in] */
1734 DWORD grfFlags) /* [in] */
1736 FIXME("(%p %s %p %s %u): stub\n", iface,
1737 debugstr_w(pwcsName), pstgDest,
1738 debugstr_w(pwcsNewName), grfFlags);
1739 return E_NOTIMPL;
1742 /*************************************************************************
1743 * Commit (IStorage)
1745 * Ensures that any changes made to a storage object open in transacted mode
1746 * are reflected in the parent storage
1748 * NOTES
1749 * Wine doesn't implement transacted mode, which seems to be a basic
1750 * optimization, so we can ignore this stub for now.
1752 static HRESULT WINAPI StorageImpl_Commit(
1753 IStorage* iface,
1754 DWORD grfCommitFlags)/* [in] */
1756 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1757 return S_OK;
1760 /*************************************************************************
1761 * Revert (IStorage)
1763 * Discard all changes that have been made since the last commit operation
1765 static HRESULT WINAPI StorageImpl_Revert(
1766 IStorage* iface)
1768 FIXME("(%p): stub\n", iface);
1769 return E_NOTIMPL;
1772 /*************************************************************************
1773 * DestroyElement (IStorage)
1775 * Strategy: This implementation is built this way for simplicity not for speed.
1776 * I always delete the topmost element of the enumeration and adjust
1777 * the deleted element pointer all the time. This takes longer to
1778 * do but allow to reinvoke DestroyElement whenever we encounter a
1779 * storage object. The optimisation resides in the usage of another
1780 * enumeration strategy that would give all the leaves of a storage
1781 * first. (postfix order)
1783 static HRESULT WINAPI StorageImpl_DestroyElement(
1784 IStorage* iface,
1785 const OLECHAR *pwcsName)/* [string][in] */
1787 StorageImpl* const This=(StorageImpl*)iface;
1789 IEnumSTATSTGImpl* propertyEnumeration;
1790 HRESULT hr = S_OK;
1791 BOOL res;
1792 StgProperty propertyToDelete;
1793 StgProperty parentProperty;
1794 ULONG foundPropertyIndexToDelete;
1795 ULONG typeOfRelation;
1796 ULONG parentPropertyId = 0;
1798 TRACE("(%p, %s)\n",
1799 iface, debugstr_w(pwcsName));
1802 * Perform a sanity check on the parameters.
1804 if (pwcsName==NULL)
1805 return STG_E_INVALIDPOINTER;
1807 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
1808 return STG_E_ACCESSDENIED;
1811 * Create a property enumeration to search the property with the given name
1813 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1814 This->base.ancestorStorage,
1815 This->base.rootPropertySetIndex);
1817 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1818 propertyEnumeration,
1819 pwcsName,
1820 &propertyToDelete);
1822 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1824 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1826 return STG_E_FILENOTFOUND;
1830 * Find the parent property of the property to delete (the one that
1831 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1832 * the parent is This. Otherwise, the parent is one of its sibling...
1836 * First, read This's StgProperty..
1838 res = StorageImpl_ReadProperty(
1839 This->base.ancestorStorage,
1840 This->base.rootPropertySetIndex,
1841 &parentProperty);
1843 assert(res);
1846 * Second, check to see if by any chance the actual storage (This) is not
1847 * the parent of the property to delete... We never know...
1849 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1852 * Set data as it would have been done in the else part...
1854 typeOfRelation = PROPERTY_RELATION_DIR;
1855 parentPropertyId = This->base.rootPropertySetIndex;
1857 else
1860 * Create a property enumeration to search the parent properties, and
1861 * delete it once done.
1863 IEnumSTATSTGImpl* propertyEnumeration2;
1865 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1866 This->base.ancestorStorage,
1867 This->base.rootPropertySetIndex);
1869 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1870 propertyEnumeration2,
1871 foundPropertyIndexToDelete,
1872 &parentProperty,
1873 &parentPropertyId);
1875 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1878 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1880 hr = deleteStorageProperty(
1881 This,
1882 foundPropertyIndexToDelete,
1883 propertyToDelete);
1885 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1887 hr = deleteStreamProperty(
1888 This,
1889 foundPropertyIndexToDelete,
1890 propertyToDelete);
1893 if (hr!=S_OK)
1894 return hr;
1897 * Adjust the property chain
1899 hr = adjustPropertyChain(
1900 This,
1901 propertyToDelete,
1902 parentProperty,
1903 parentPropertyId,
1904 typeOfRelation);
1906 return hr;
1910 /************************************************************************
1911 * StorageImpl_Stat (IStorage)
1913 * This method will retrieve information about this storage object.
1915 * See Windows documentation for more details on IStorage methods.
1917 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1918 STATSTG* pstatstg, /* [out] */
1919 DWORD grfStatFlag) /* [in] */
1921 StorageImpl* const This = (StorageImpl*)iface;
1922 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1924 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1926 CoTaskMemFree(pstatstg->pwcsName);
1927 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1928 strcpyW(pstatstg->pwcsName, This->pwcsName);
1931 return result;
1934 /******************************************************************************
1935 * Internal stream list handlers
1938 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1940 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1941 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1944 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1946 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1947 list_remove(&(strm->StrmListEntry));
1950 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1952 struct list *cur, *cur2;
1953 StgStreamImpl *strm=NULL;
1955 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1956 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1957 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1958 strm->parentStorage = NULL;
1959 list_remove(cur);
1964 /*********************************************************************
1966 * Internal Method
1968 * Perform the deletion of a complete storage node
1971 static HRESULT deleteStorageProperty(
1972 StorageImpl *parentStorage,
1973 ULONG indexOfPropertyToDelete,
1974 StgProperty propertyToDelete)
1976 IEnumSTATSTG *elements = 0;
1977 IStorage *childStorage = 0;
1978 STATSTG currentElement;
1979 HRESULT hr;
1980 HRESULT destroyHr = S_OK;
1983 * Open the storage and enumerate it
1985 hr = StorageBaseImpl_OpenStorage(
1986 (IStorage*)parentStorage,
1987 propertyToDelete.name,
1989 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1992 &childStorage);
1994 if (hr != S_OK)
1996 return hr;
2000 * Enumerate the elements
2002 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2007 * Obtain the next element
2009 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2010 if (hr==S_OK)
2012 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
2014 CoTaskMemFree(currentElement.pwcsName);
2018 * We need to Reset the enumeration every time because we delete elements
2019 * and the enumeration could be invalid
2021 IEnumSTATSTG_Reset(elements);
2023 } while ((hr == S_OK) && (destroyHr == S_OK));
2026 * Invalidate the property by zeroing its name member.
2028 propertyToDelete.sizeOfNameString = 0;
2030 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2031 indexOfPropertyToDelete,
2032 &propertyToDelete);
2034 IStorage_Release(childStorage);
2035 IEnumSTATSTG_Release(elements);
2037 return destroyHr;
2040 /*********************************************************************
2042 * Internal Method
2044 * Perform the deletion of a stream node
2047 static HRESULT deleteStreamProperty(
2048 StorageImpl *parentStorage,
2049 ULONG indexOfPropertyToDelete,
2050 StgProperty propertyToDelete)
2052 IStream *pis;
2053 HRESULT hr;
2054 ULARGE_INTEGER size;
2056 size.u.HighPart = 0;
2057 size.u.LowPart = 0;
2059 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2060 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2062 if (hr!=S_OK)
2064 return(hr);
2068 * Zap the stream
2070 hr = IStream_SetSize(pis, size);
2072 if(hr != S_OK)
2074 return hr;
2078 * Release the stream object.
2080 IStream_Release(pis);
2083 * Invalidate the property by zeroing its name member.
2085 propertyToDelete.sizeOfNameString = 0;
2088 * Here we should re-read the property so we get the updated pointer
2089 * but since we are here to zap it, I don't do it...
2091 StorageImpl_WriteProperty(
2092 parentStorage->base.ancestorStorage,
2093 indexOfPropertyToDelete,
2094 &propertyToDelete);
2096 return S_OK;
2099 /*********************************************************************
2101 * Internal Method
2103 * Finds a placeholder for the StgProperty within the Storage
2106 static HRESULT findPlaceholder(
2107 StorageImpl *storage,
2108 ULONG propertyIndexToStore,
2109 ULONG storePropertyIndex,
2110 INT typeOfRelation)
2112 StgProperty storeProperty;
2113 BOOL res = TRUE;
2116 * Read the storage property
2118 res = StorageImpl_ReadProperty(
2119 storage->base.ancestorStorage,
2120 storePropertyIndex,
2121 &storeProperty);
2123 if(! res)
2125 return E_FAIL;
2128 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2130 if (storeProperty.previousProperty != PROPERTY_NULL)
2132 return findPlaceholder(
2133 storage,
2134 propertyIndexToStore,
2135 storeProperty.previousProperty,
2136 typeOfRelation);
2138 else
2140 storeProperty.previousProperty = propertyIndexToStore;
2143 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2145 if (storeProperty.nextProperty != PROPERTY_NULL)
2147 return findPlaceholder(
2148 storage,
2149 propertyIndexToStore,
2150 storeProperty.nextProperty,
2151 typeOfRelation);
2153 else
2155 storeProperty.nextProperty = propertyIndexToStore;
2158 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2160 if (storeProperty.dirProperty != PROPERTY_NULL)
2162 return findPlaceholder(
2163 storage,
2164 propertyIndexToStore,
2165 storeProperty.dirProperty,
2166 typeOfRelation);
2168 else
2170 storeProperty.dirProperty = propertyIndexToStore;
2174 res = StorageImpl_WriteProperty(
2175 storage->base.ancestorStorage,
2176 storePropertyIndex,
2177 &storeProperty);
2179 if(!res)
2181 return E_FAIL;
2184 return S_OK;
2187 /*************************************************************************
2189 * Internal Method
2191 * This method takes the previous and the next property link of a property
2192 * to be deleted and find them a place in the Storage.
2194 static HRESULT adjustPropertyChain(
2195 StorageImpl *This,
2196 StgProperty propertyToDelete,
2197 StgProperty parentProperty,
2198 ULONG parentPropertyId,
2199 INT typeOfRelation)
2201 ULONG newLinkProperty = PROPERTY_NULL;
2202 BOOL needToFindAPlaceholder = FALSE;
2203 ULONG storeNode = PROPERTY_NULL;
2204 ULONG toStoreNode = PROPERTY_NULL;
2205 INT relationType = 0;
2206 HRESULT hr = S_OK;
2207 BOOL res = TRUE;
2209 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2211 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2214 * Set the parent previous to the property to delete previous
2216 newLinkProperty = propertyToDelete.previousProperty;
2218 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2221 * We also need to find a storage for the other link, setup variables
2222 * to do this at the end...
2224 needToFindAPlaceholder = TRUE;
2225 storeNode = propertyToDelete.previousProperty;
2226 toStoreNode = propertyToDelete.nextProperty;
2227 relationType = PROPERTY_RELATION_NEXT;
2230 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2233 * Set the parent previous to the property to delete next
2235 newLinkProperty = propertyToDelete.nextProperty;
2239 * Link it for real...
2241 parentProperty.previousProperty = newLinkProperty;
2244 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2246 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2249 * Set the parent next to the property to delete next previous
2251 newLinkProperty = propertyToDelete.previousProperty;
2253 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2256 * We also need to find a storage for the other link, setup variables
2257 * to do this at the end...
2259 needToFindAPlaceholder = TRUE;
2260 storeNode = propertyToDelete.previousProperty;
2261 toStoreNode = propertyToDelete.nextProperty;
2262 relationType = PROPERTY_RELATION_NEXT;
2265 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2268 * Set the parent next to the property to delete next
2270 newLinkProperty = propertyToDelete.nextProperty;
2274 * Link it for real...
2276 parentProperty.nextProperty = newLinkProperty;
2278 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2280 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2283 * Set the parent dir to the property to delete previous
2285 newLinkProperty = propertyToDelete.previousProperty;
2287 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2290 * We also need to find a storage for the other link, setup variables
2291 * to do this at the end...
2293 needToFindAPlaceholder = TRUE;
2294 storeNode = propertyToDelete.previousProperty;
2295 toStoreNode = propertyToDelete.nextProperty;
2296 relationType = PROPERTY_RELATION_NEXT;
2299 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2302 * Set the parent dir to the property to delete next
2304 newLinkProperty = propertyToDelete.nextProperty;
2308 * Link it for real...
2310 parentProperty.dirProperty = newLinkProperty;
2314 * Write back the parent property
2316 res = StorageImpl_WriteProperty(
2317 This->base.ancestorStorage,
2318 parentPropertyId,
2319 &parentProperty);
2320 if(! res)
2322 return E_FAIL;
2326 * If a placeholder is required for the other link, then, find one and
2327 * get out of here...
2329 if (needToFindAPlaceholder)
2331 hr = findPlaceholder(
2332 This,
2333 toStoreNode,
2334 storeNode,
2335 relationType);
2338 return hr;
2342 /******************************************************************************
2343 * SetElementTimes (IStorage)
2345 static HRESULT WINAPI StorageImpl_SetElementTimes(
2346 IStorage* iface,
2347 const OLECHAR *pwcsName,/* [string][in] */
2348 const FILETIME *pctime, /* [in] */
2349 const FILETIME *patime, /* [in] */
2350 const FILETIME *pmtime) /* [in] */
2352 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2353 return S_OK;
2356 /******************************************************************************
2357 * SetStateBits (IStorage)
2359 static HRESULT WINAPI StorageImpl_SetStateBits(
2360 IStorage* iface,
2361 DWORD grfStateBits,/* [in] */
2362 DWORD grfMask) /* [in] */
2364 StorageImpl* const This = (StorageImpl*)iface;
2365 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2366 return S_OK;
2370 * Virtual function table for the IStorage32Impl class.
2372 static const IStorageVtbl Storage32Impl_Vtbl =
2374 StorageBaseImpl_QueryInterface,
2375 StorageBaseImpl_AddRef,
2376 StorageBaseImpl_Release,
2377 StorageBaseImpl_CreateStream,
2378 StorageBaseImpl_OpenStream,
2379 StorageImpl_CreateStorage,
2380 StorageBaseImpl_OpenStorage,
2381 StorageImpl_CopyTo,
2382 StorageImpl_MoveElementTo,
2383 StorageImpl_Commit,
2384 StorageImpl_Revert,
2385 StorageBaseImpl_EnumElements,
2386 StorageImpl_DestroyElement,
2387 StorageBaseImpl_RenameElement,
2388 StorageImpl_SetElementTimes,
2389 StorageBaseImpl_SetClass,
2390 StorageImpl_SetStateBits,
2391 StorageImpl_Stat
2394 static HRESULT StorageImpl_Construct(
2395 StorageImpl* This,
2396 HANDLE hFile,
2397 LPCOLESTR pwcsName,
2398 ILockBytes* pLkbyt,
2399 DWORD openFlags,
2400 BOOL fileBased,
2401 BOOL create)
2403 HRESULT hr = S_OK;
2404 StgProperty currentProperty;
2405 BOOL readSuccessful;
2406 ULONG currentPropertyIndex;
2408 if ( FAILED( validateSTGM(openFlags) ))
2409 return STG_E_INVALIDFLAG;
2411 memset(This, 0, sizeof(StorageImpl));
2413 list_init(&This->base.strmHead);
2415 This->base.lpVtbl = &Storage32Impl_Vtbl;
2416 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2417 This->base.v_destructor = StorageImpl_Destroy;
2418 This->base.openFlags = (openFlags & ~STGM_CREATE);
2419 This->create = create;
2422 * This is the top-level storage so initialize the ancestor pointer
2423 * to this.
2425 This->base.ancestorStorage = This;
2427 This->hFile = hFile;
2429 if(pwcsName) {
2430 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2431 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2432 if (!This->pwcsName)
2433 return STG_E_INSUFFICIENTMEMORY;
2434 strcpyW(This->pwcsName, pwcsName);
2438 * Initialize the big block cache.
2440 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2441 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2442 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2443 pLkbyt,
2444 openFlags,
2445 This->bigBlockSize,
2446 fileBased);
2448 if (This->bigBlockFile == 0)
2449 return E_FAIL;
2451 if (create)
2453 ULARGE_INTEGER size;
2454 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2457 * Initialize all header variables:
2458 * - The big block depot consists of one block and it is at block 0
2459 * - The properties start at block 1
2460 * - There is no small block depot
2462 memset( This->bigBlockDepotStart,
2463 BLOCK_UNUSED,
2464 sizeof(This->bigBlockDepotStart));
2466 This->bigBlockDepotCount = 1;
2467 This->bigBlockDepotStart[0] = 0;
2468 This->rootStartBlock = 1;
2469 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2470 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2471 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2472 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2473 This->extBigBlockDepotCount = 0;
2475 StorageImpl_SaveFileHeader(This);
2478 * Add one block for the big block depot and one block for the properties
2480 size.u.HighPart = 0;
2481 size.u.LowPart = This->bigBlockSize * 3;
2482 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2485 * Initialize the big block depot
2487 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2488 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2489 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2490 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2492 else
2495 * Load the header for the file.
2497 hr = StorageImpl_LoadFileHeader(This);
2499 if (FAILED(hr))
2501 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2503 return hr;
2508 * There is no block depot cached yet.
2510 This->indexBlockDepotCached = 0xFFFFFFFF;
2513 * Start searching for free blocks with block 0.
2515 This->prevFreeBlock = 0;
2518 * Create the block chain abstractions.
2520 if(!(This->rootBlockChain =
2521 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2522 return STG_E_READFAULT;
2524 if(!(This->smallBlockDepotChain =
2525 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2526 PROPERTY_NULL)))
2527 return STG_E_READFAULT;
2530 * Write the root property (memory only)
2532 if (create)
2534 StgProperty rootProp;
2536 * Initialize the property chain
2538 memset(&rootProp, 0, sizeof(rootProp));
2539 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2540 sizeof(rootProp.name)/sizeof(WCHAR) );
2541 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2542 rootProp.propertyType = PROPTYPE_ROOT;
2543 rootProp.previousProperty = PROPERTY_NULL;
2544 rootProp.nextProperty = PROPERTY_NULL;
2545 rootProp.dirProperty = PROPERTY_NULL;
2546 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2547 rootProp.size.u.HighPart = 0;
2548 rootProp.size.u.LowPart = 0;
2550 StorageImpl_WriteProperty(This, 0, &rootProp);
2554 * Find the ID of the root in the property sets.
2556 currentPropertyIndex = 0;
2560 readSuccessful = StorageImpl_ReadProperty(
2561 This,
2562 currentPropertyIndex,
2563 &currentProperty);
2565 if (readSuccessful)
2567 if ( (currentProperty.sizeOfNameString != 0 ) &&
2568 (currentProperty.propertyType == PROPTYPE_ROOT) )
2570 This->base.rootPropertySetIndex = currentPropertyIndex;
2574 currentPropertyIndex++;
2576 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2578 if (!readSuccessful)
2580 /* TODO CLEANUP */
2581 return STG_E_READFAULT;
2585 * Create the block chain abstraction for the small block root chain.
2587 if(!(This->smallBlockRootChain =
2588 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2589 return STG_E_READFAULT;
2591 return hr;
2594 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2596 StorageImpl *This = (StorageImpl*) iface;
2597 TRACE("(%p)\n", This);
2599 StorageBaseImpl_DeleteAll(&This->base);
2601 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2603 BlockChainStream_Destroy(This->smallBlockRootChain);
2604 BlockChainStream_Destroy(This->rootBlockChain);
2605 BlockChainStream_Destroy(This->smallBlockDepotChain);
2607 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2608 HeapFree(GetProcessHeap(), 0, This);
2611 /******************************************************************************
2612 * Storage32Impl_GetNextFreeBigBlock
2614 * Returns the index of the next free big block.
2615 * If the big block depot is filled, this method will enlarge it.
2618 static ULONG StorageImpl_GetNextFreeBigBlock(
2619 StorageImpl* This)
2621 ULONG depotBlockIndexPos;
2622 BYTE depotBuffer[BIG_BLOCK_SIZE];
2623 BOOL success;
2624 ULONG depotBlockOffset;
2625 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2626 ULONG nextBlockIndex = BLOCK_SPECIAL;
2627 int depotIndex = 0;
2628 ULONG freeBlock = BLOCK_UNUSED;
2630 depotIndex = This->prevFreeBlock / blocksPerDepot;
2631 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2634 * Scan the entire big block depot until we find a block marked free
2636 while (nextBlockIndex != BLOCK_UNUSED)
2638 if (depotIndex < COUNT_BBDEPOTINHEADER)
2640 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2643 * Grow the primary depot.
2645 if (depotBlockIndexPos == BLOCK_UNUSED)
2647 depotBlockIndexPos = depotIndex*blocksPerDepot;
2650 * Add a block depot.
2652 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2653 This->bigBlockDepotCount++;
2654 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2657 * Flag it as a block depot.
2659 StorageImpl_SetNextBlockInChain(This,
2660 depotBlockIndexPos,
2661 BLOCK_SPECIAL);
2663 /* Save new header information.
2665 StorageImpl_SaveFileHeader(This);
2668 else
2670 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2672 if (depotBlockIndexPos == BLOCK_UNUSED)
2675 * Grow the extended depot.
2677 ULONG extIndex = BLOCK_UNUSED;
2678 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2679 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2681 if (extBlockOffset == 0)
2683 /* We need an extended block.
2685 extIndex = Storage32Impl_AddExtBlockDepot(This);
2686 This->extBigBlockDepotCount++;
2687 depotBlockIndexPos = extIndex + 1;
2689 else
2690 depotBlockIndexPos = depotIndex * blocksPerDepot;
2693 * Add a block depot and mark it in the extended block.
2695 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2696 This->bigBlockDepotCount++;
2697 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2699 /* Flag the block depot.
2701 StorageImpl_SetNextBlockInChain(This,
2702 depotBlockIndexPos,
2703 BLOCK_SPECIAL);
2705 /* If necessary, flag the extended depot block.
2707 if (extIndex != BLOCK_UNUSED)
2708 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2710 /* Save header information.
2712 StorageImpl_SaveFileHeader(This);
2716 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2718 if (success)
2720 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2721 ( nextBlockIndex != BLOCK_UNUSED))
2723 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2725 if (nextBlockIndex == BLOCK_UNUSED)
2727 freeBlock = (depotIndex * blocksPerDepot) +
2728 (depotBlockOffset/sizeof(ULONG));
2731 depotBlockOffset += sizeof(ULONG);
2735 depotIndex++;
2736 depotBlockOffset = 0;
2740 * make sure that the block physically exists before using it
2742 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2744 This->prevFreeBlock = freeBlock;
2746 return freeBlock;
2749 /******************************************************************************
2750 * Storage32Impl_AddBlockDepot
2752 * This will create a depot block, essentially it is a block initialized
2753 * to BLOCK_UNUSEDs.
2755 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2757 BYTE blockBuffer[BIG_BLOCK_SIZE];
2760 * Initialize blocks as free
2762 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2763 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2766 /******************************************************************************
2767 * Storage32Impl_GetExtDepotBlock
2769 * Returns the index of the block that corresponds to the specified depot
2770 * index. This method is only for depot indexes equal or greater than
2771 * COUNT_BBDEPOTINHEADER.
2773 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2775 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2776 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2777 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2778 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2779 ULONG blockIndex = BLOCK_UNUSED;
2780 ULONG extBlockIndex = This->extBigBlockDepotStart;
2782 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2784 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2785 return BLOCK_UNUSED;
2787 while (extBlockCount > 0)
2789 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2790 extBlockCount--;
2793 if (extBlockIndex != BLOCK_UNUSED)
2794 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2795 extBlockOffset * sizeof(ULONG), &blockIndex);
2797 return blockIndex;
2800 /******************************************************************************
2801 * Storage32Impl_SetExtDepotBlock
2803 * Associates the specified block index to the specified depot index.
2804 * This method is only for depot indexes equal or greater than
2805 * COUNT_BBDEPOTINHEADER.
2807 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2809 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2810 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2811 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2812 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2813 ULONG extBlockIndex = This->extBigBlockDepotStart;
2815 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2817 while (extBlockCount > 0)
2819 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2820 extBlockCount--;
2823 if (extBlockIndex != BLOCK_UNUSED)
2825 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2826 extBlockOffset * sizeof(ULONG),
2827 blockIndex);
2831 /******************************************************************************
2832 * Storage32Impl_AddExtBlockDepot
2834 * Creates an extended depot block.
2836 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2838 ULONG numExtBlocks = This->extBigBlockDepotCount;
2839 ULONG nextExtBlock = This->extBigBlockDepotStart;
2840 BYTE depotBuffer[BIG_BLOCK_SIZE];
2841 ULONG index = BLOCK_UNUSED;
2842 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2843 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2844 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2846 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2847 blocksPerDepotBlock;
2849 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2852 * The first extended block.
2854 This->extBigBlockDepotStart = index;
2856 else
2858 unsigned int i;
2860 * Follow the chain to the last one.
2862 for (i = 0; i < (numExtBlocks - 1); i++)
2864 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2868 * Add the new extended block to the chain.
2870 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2871 index);
2875 * Initialize this block.
2877 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2878 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2880 return index;
2883 /******************************************************************************
2884 * Storage32Impl_FreeBigBlock
2886 * This method will flag the specified block as free in the big block depot.
2888 static void StorageImpl_FreeBigBlock(
2889 StorageImpl* This,
2890 ULONG blockIndex)
2892 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2894 if (blockIndex < This->prevFreeBlock)
2895 This->prevFreeBlock = blockIndex;
2898 /************************************************************************
2899 * Storage32Impl_GetNextBlockInChain
2901 * This method will retrieve the block index of the next big block in
2902 * in the chain.
2904 * Params: This - Pointer to the Storage object.
2905 * blockIndex - Index of the block to retrieve the chain
2906 * for.
2907 * nextBlockIndex - receives the return value.
2909 * Returns: This method returns the index of the next block in the chain.
2910 * It will return the constants:
2911 * BLOCK_SPECIAL - If the block given was not part of a
2912 * chain.
2913 * BLOCK_END_OF_CHAIN - If the block given was the last in
2914 * a chain.
2915 * BLOCK_UNUSED - If the block given was not past of a chain
2916 * and is available.
2917 * BLOCK_EXTBBDEPOT - This block is part of the extended
2918 * big block depot.
2920 * See Windows documentation for more details on IStorage methods.
2922 static HRESULT StorageImpl_GetNextBlockInChain(
2923 StorageImpl* This,
2924 ULONG blockIndex,
2925 ULONG* nextBlockIndex)
2927 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2928 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2929 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2930 BYTE depotBuffer[BIG_BLOCK_SIZE];
2931 BOOL success;
2932 ULONG depotBlockIndexPos;
2933 int index;
2935 *nextBlockIndex = BLOCK_SPECIAL;
2937 if(depotBlockCount >= This->bigBlockDepotCount)
2939 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2940 This->bigBlockDepotCount);
2941 return STG_E_READFAULT;
2945 * Cache the currently accessed depot block.
2947 if (depotBlockCount != This->indexBlockDepotCached)
2949 This->indexBlockDepotCached = depotBlockCount;
2951 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2953 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2955 else
2958 * We have to look in the extended depot.
2960 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2963 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2965 if (!success)
2966 return STG_E_READFAULT;
2968 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2970 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2971 This->blockDepotCached[index] = *nextBlockIndex;
2975 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2977 return S_OK;
2980 /******************************************************************************
2981 * Storage32Impl_GetNextExtendedBlock
2983 * Given an extended block this method will return the next extended block.
2985 * NOTES:
2986 * The last ULONG of an extended block is the block index of the next
2987 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2988 * depot.
2990 * Return values:
2991 * - The index of the next extended block
2992 * - BLOCK_UNUSED: there is no next extended block.
2993 * - Any other return values denotes failure.
2995 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2997 ULONG nextBlockIndex = BLOCK_SPECIAL;
2998 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3000 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3001 &nextBlockIndex);
3003 return nextBlockIndex;
3006 /******************************************************************************
3007 * Storage32Impl_SetNextBlockInChain
3009 * This method will write the index of the specified block's next block
3010 * in the big block depot.
3012 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3013 * do the following
3015 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3016 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3017 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3020 static void StorageImpl_SetNextBlockInChain(
3021 StorageImpl* This,
3022 ULONG blockIndex,
3023 ULONG nextBlock)
3025 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3026 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3027 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3028 ULONG depotBlockIndexPos;
3030 assert(depotBlockCount < This->bigBlockDepotCount);
3031 assert(blockIndex != nextBlock);
3033 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3035 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3037 else
3040 * We have to look in the extended depot.
3042 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3045 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3046 nextBlock);
3048 * Update the cached block depot, if necessary.
3050 if (depotBlockCount == This->indexBlockDepotCached)
3052 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3056 /******************************************************************************
3057 * Storage32Impl_LoadFileHeader
3059 * This method will read in the file header, i.e. big block index -1.
3061 static HRESULT StorageImpl_LoadFileHeader(
3062 StorageImpl* This)
3064 HRESULT hr = STG_E_FILENOTFOUND;
3065 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3066 BOOL success;
3067 int index;
3069 TRACE("\n");
3071 * Get a pointer to the big block of data containing the header.
3073 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3076 * Extract the information from the header.
3078 if (success)
3081 * Check for the "magic number" signature and return an error if it is not
3082 * found.
3084 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3086 return STG_E_OLDFORMAT;
3089 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3091 return STG_E_INVALIDHEADER;
3094 StorageUtl_ReadWord(
3095 headerBigBlock,
3096 OFFSET_BIGBLOCKSIZEBITS,
3097 &This->bigBlockSizeBits);
3099 StorageUtl_ReadWord(
3100 headerBigBlock,
3101 OFFSET_SMALLBLOCKSIZEBITS,
3102 &This->smallBlockSizeBits);
3104 StorageUtl_ReadDWord(
3105 headerBigBlock,
3106 OFFSET_BBDEPOTCOUNT,
3107 &This->bigBlockDepotCount);
3109 StorageUtl_ReadDWord(
3110 headerBigBlock,
3111 OFFSET_ROOTSTARTBLOCK,
3112 &This->rootStartBlock);
3114 StorageUtl_ReadDWord(
3115 headerBigBlock,
3116 OFFSET_SBDEPOTSTART,
3117 &This->smallBlockDepotStart);
3119 StorageUtl_ReadDWord(
3120 headerBigBlock,
3121 OFFSET_EXTBBDEPOTSTART,
3122 &This->extBigBlockDepotStart);
3124 StorageUtl_ReadDWord(
3125 headerBigBlock,
3126 OFFSET_EXTBBDEPOTCOUNT,
3127 &This->extBigBlockDepotCount);
3129 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3131 StorageUtl_ReadDWord(
3132 headerBigBlock,
3133 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3134 &(This->bigBlockDepotStart[index]));
3138 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3140 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3141 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3144 * Right now, the code is making some assumptions about the size of the
3145 * blocks, just make sure they are what we're expecting.
3147 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3148 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3150 WARN("Broken OLE storage file\n");
3151 hr = STG_E_INVALIDHEADER;
3153 else
3154 hr = S_OK;
3157 return hr;
3160 /******************************************************************************
3161 * Storage32Impl_SaveFileHeader
3163 * This method will save to the file the header, i.e. big block -1.
3165 static void StorageImpl_SaveFileHeader(
3166 StorageImpl* This)
3168 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3169 int index;
3170 BOOL success;
3173 * Get a pointer to the big block of data containing the header.
3175 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3178 * If the block read failed, the file is probably new.
3180 if (!success)
3183 * Initialize for all unknown fields.
3185 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3188 * Initialize the magic number.
3190 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3193 * And a bunch of things we don't know what they mean
3195 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3196 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3197 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3198 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3202 * Write the information to the header.
3204 StorageUtl_WriteWord(
3205 headerBigBlock,
3206 OFFSET_BIGBLOCKSIZEBITS,
3207 This->bigBlockSizeBits);
3209 StorageUtl_WriteWord(
3210 headerBigBlock,
3211 OFFSET_SMALLBLOCKSIZEBITS,
3212 This->smallBlockSizeBits);
3214 StorageUtl_WriteDWord(
3215 headerBigBlock,
3216 OFFSET_BBDEPOTCOUNT,
3217 This->bigBlockDepotCount);
3219 StorageUtl_WriteDWord(
3220 headerBigBlock,
3221 OFFSET_ROOTSTARTBLOCK,
3222 This->rootStartBlock);
3224 StorageUtl_WriteDWord(
3225 headerBigBlock,
3226 OFFSET_SBDEPOTSTART,
3227 This->smallBlockDepotStart);
3229 StorageUtl_WriteDWord(
3230 headerBigBlock,
3231 OFFSET_SBDEPOTCOUNT,
3232 This->smallBlockDepotChain ?
3233 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3235 StorageUtl_WriteDWord(
3236 headerBigBlock,
3237 OFFSET_EXTBBDEPOTSTART,
3238 This->extBigBlockDepotStart);
3240 StorageUtl_WriteDWord(
3241 headerBigBlock,
3242 OFFSET_EXTBBDEPOTCOUNT,
3243 This->extBigBlockDepotCount);
3245 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3247 StorageUtl_WriteDWord(
3248 headerBigBlock,
3249 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3250 (This->bigBlockDepotStart[index]));
3254 * Write the big block back to the file.
3256 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3259 /******************************************************************************
3260 * Storage32Impl_ReadProperty
3262 * This method will read the specified property from the property chain.
3264 BOOL StorageImpl_ReadProperty(
3265 StorageImpl* This,
3266 ULONG index,
3267 StgProperty* buffer)
3269 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3270 ULARGE_INTEGER offsetInPropSet;
3271 HRESULT readRes;
3272 ULONG bytesRead;
3274 offsetInPropSet.u.HighPart = 0;
3275 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3277 readRes = BlockChainStream_ReadAt(
3278 This->rootBlockChain,
3279 offsetInPropSet,
3280 PROPSET_BLOCK_SIZE,
3281 currentProperty,
3282 &bytesRead);
3284 if (SUCCEEDED(readRes))
3286 /* replace the name of root entry (often "Root Entry") by the file name */
3287 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3288 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3290 memset(buffer->name, 0, sizeof(buffer->name));
3291 memcpy(
3292 buffer->name,
3293 propName,
3294 PROPERTY_NAME_BUFFER_LEN );
3295 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3297 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3299 StorageUtl_ReadWord(
3300 currentProperty,
3301 OFFSET_PS_NAMELENGTH,
3302 &buffer->sizeOfNameString);
3304 StorageUtl_ReadDWord(
3305 currentProperty,
3306 OFFSET_PS_PREVIOUSPROP,
3307 &buffer->previousProperty);
3309 StorageUtl_ReadDWord(
3310 currentProperty,
3311 OFFSET_PS_NEXTPROP,
3312 &buffer->nextProperty);
3314 StorageUtl_ReadDWord(
3315 currentProperty,
3316 OFFSET_PS_DIRPROP,
3317 &buffer->dirProperty);
3319 StorageUtl_ReadGUID(
3320 currentProperty,
3321 OFFSET_PS_GUID,
3322 &buffer->propertyUniqueID);
3324 StorageUtl_ReadDWord(
3325 currentProperty,
3326 OFFSET_PS_TSS1,
3327 &buffer->timeStampS1);
3329 StorageUtl_ReadDWord(
3330 currentProperty,
3331 OFFSET_PS_TSD1,
3332 &buffer->timeStampD1);
3334 StorageUtl_ReadDWord(
3335 currentProperty,
3336 OFFSET_PS_TSS2,
3337 &buffer->timeStampS2);
3339 StorageUtl_ReadDWord(
3340 currentProperty,
3341 OFFSET_PS_TSD2,
3342 &buffer->timeStampD2);
3344 StorageUtl_ReadDWord(
3345 currentProperty,
3346 OFFSET_PS_STARTBLOCK,
3347 &buffer->startingBlock);
3349 StorageUtl_ReadDWord(
3350 currentProperty,
3351 OFFSET_PS_SIZE,
3352 &buffer->size.u.LowPart);
3354 buffer->size.u.HighPart = 0;
3357 return SUCCEEDED(readRes) ? TRUE : FALSE;
3360 /*********************************************************************
3361 * Write the specified property into the property chain
3363 BOOL StorageImpl_WriteProperty(
3364 StorageImpl* This,
3365 ULONG index,
3366 const StgProperty* buffer)
3368 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3369 ULARGE_INTEGER offsetInPropSet;
3370 HRESULT writeRes;
3371 ULONG bytesWritten;
3373 offsetInPropSet.u.HighPart = 0;
3374 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3376 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3378 memcpy(
3379 currentProperty + OFFSET_PS_NAME,
3380 buffer->name,
3381 PROPERTY_NAME_BUFFER_LEN );
3383 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3385 StorageUtl_WriteWord(
3386 currentProperty,
3387 OFFSET_PS_NAMELENGTH,
3388 buffer->sizeOfNameString);
3390 StorageUtl_WriteDWord(
3391 currentProperty,
3392 OFFSET_PS_PREVIOUSPROP,
3393 buffer->previousProperty);
3395 StorageUtl_WriteDWord(
3396 currentProperty,
3397 OFFSET_PS_NEXTPROP,
3398 buffer->nextProperty);
3400 StorageUtl_WriteDWord(
3401 currentProperty,
3402 OFFSET_PS_DIRPROP,
3403 buffer->dirProperty);
3405 StorageUtl_WriteGUID(
3406 currentProperty,
3407 OFFSET_PS_GUID,
3408 &buffer->propertyUniqueID);
3410 StorageUtl_WriteDWord(
3411 currentProperty,
3412 OFFSET_PS_TSS1,
3413 buffer->timeStampS1);
3415 StorageUtl_WriteDWord(
3416 currentProperty,
3417 OFFSET_PS_TSD1,
3418 buffer->timeStampD1);
3420 StorageUtl_WriteDWord(
3421 currentProperty,
3422 OFFSET_PS_TSS2,
3423 buffer->timeStampS2);
3425 StorageUtl_WriteDWord(
3426 currentProperty,
3427 OFFSET_PS_TSD2,
3428 buffer->timeStampD2);
3430 StorageUtl_WriteDWord(
3431 currentProperty,
3432 OFFSET_PS_STARTBLOCK,
3433 buffer->startingBlock);
3435 StorageUtl_WriteDWord(
3436 currentProperty,
3437 OFFSET_PS_SIZE,
3438 buffer->size.u.LowPart);
3440 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3441 offsetInPropSet,
3442 PROPSET_BLOCK_SIZE,
3443 currentProperty,
3444 &bytesWritten);
3445 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3448 static BOOL StorageImpl_ReadBigBlock(
3449 StorageImpl* This,
3450 ULONG blockIndex,
3451 void* buffer)
3453 ULARGE_INTEGER ulOffset;
3454 DWORD read;
3456 ulOffset.u.HighPart = 0;
3457 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3459 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3460 return (read == This->bigBlockSize);
3463 static BOOL StorageImpl_ReadDWordFromBigBlock(
3464 StorageImpl* This,
3465 ULONG blockIndex,
3466 ULONG offset,
3467 DWORD* value)
3469 ULARGE_INTEGER ulOffset;
3470 DWORD read;
3471 DWORD tmp;
3473 ulOffset.u.HighPart = 0;
3474 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3475 ulOffset.u.LowPart += offset;
3477 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3478 *value = lendian32toh(tmp);
3479 return (read == sizeof(DWORD));
3482 static BOOL StorageImpl_WriteBigBlock(
3483 StorageImpl* This,
3484 ULONG blockIndex,
3485 const void* buffer)
3487 ULARGE_INTEGER ulOffset;
3488 DWORD wrote;
3490 ulOffset.u.HighPart = 0;
3491 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3493 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3494 return (wrote == This->bigBlockSize);
3497 static BOOL StorageImpl_WriteDWordToBigBlock(
3498 StorageImpl* This,
3499 ULONG blockIndex,
3500 ULONG offset,
3501 DWORD value)
3503 ULARGE_INTEGER ulOffset;
3504 DWORD wrote;
3506 ulOffset.u.HighPart = 0;
3507 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3508 ulOffset.u.LowPart += offset;
3510 value = htole32(value);
3511 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3512 return (wrote == sizeof(DWORD));
3515 /******************************************************************************
3516 * Storage32Impl_SmallBlocksToBigBlocks
3518 * This method will convert a small block chain to a big block chain.
3519 * The small block chain will be destroyed.
3521 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3522 StorageImpl* This,
3523 SmallBlockChainStream** ppsbChain)
3525 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3526 ULARGE_INTEGER size, offset;
3527 ULONG cbRead, cbWritten;
3528 ULARGE_INTEGER cbTotalRead;
3529 ULONG propertyIndex;
3530 HRESULT resWrite = S_OK;
3531 HRESULT resRead;
3532 StgProperty chainProperty;
3533 BYTE *buffer;
3534 BlockChainStream *bbTempChain = NULL;
3535 BlockChainStream *bigBlockChain = NULL;
3538 * Create a temporary big block chain that doesn't have
3539 * an associated property. This temporary chain will be
3540 * used to copy data from small blocks to big blocks.
3542 bbTempChain = BlockChainStream_Construct(This,
3543 &bbHeadOfChain,
3544 PROPERTY_NULL);
3545 if(!bbTempChain) return NULL;
3547 * Grow the big block chain.
3549 size = SmallBlockChainStream_GetSize(*ppsbChain);
3550 BlockChainStream_SetSize(bbTempChain, size);
3553 * Copy the contents of the small block chain to the big block chain
3554 * by small block size increments.
3556 offset.u.LowPart = 0;
3557 offset.u.HighPart = 0;
3558 cbTotalRead.QuadPart = 0;
3560 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3563 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3564 offset,
3565 This->smallBlockSize,
3566 buffer,
3567 &cbRead);
3568 if (FAILED(resRead))
3569 break;
3571 if (cbRead > 0)
3573 cbTotalRead.QuadPart += cbRead;
3575 resWrite = BlockChainStream_WriteAt(bbTempChain,
3576 offset,
3577 cbRead,
3578 buffer,
3579 &cbWritten);
3581 if (FAILED(resWrite))
3582 break;
3584 offset.u.LowPart += This->smallBlockSize;
3586 } while (cbTotalRead.QuadPart < size.QuadPart);
3587 HeapFree(GetProcessHeap(),0,buffer);
3589 if (FAILED(resRead) || FAILED(resWrite))
3591 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3592 BlockChainStream_Destroy(bbTempChain);
3593 return NULL;
3597 * Destroy the small block chain.
3599 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3600 size.u.HighPart = 0;
3601 size.u.LowPart = 0;
3602 SmallBlockChainStream_SetSize(*ppsbChain, size);
3603 SmallBlockChainStream_Destroy(*ppsbChain);
3604 *ppsbChain = 0;
3607 * Change the property information. This chain is now a big block chain
3608 * and it doesn't reside in the small blocks chain anymore.
3610 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3612 chainProperty.startingBlock = bbHeadOfChain;
3614 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3617 * Destroy the temporary propertyless big block chain.
3618 * Create a new big block chain associated with this property.
3620 BlockChainStream_Destroy(bbTempChain);
3621 bigBlockChain = BlockChainStream_Construct(This,
3622 NULL,
3623 propertyIndex);
3625 return bigBlockChain;
3628 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3630 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3632 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3633 HeapFree(GetProcessHeap(), 0, This);
3636 /******************************************************************************
3638 ** Storage32InternalImpl_Commit
3640 ** The non-root storages cannot be opened in transacted mode thus this function
3641 ** does nothing.
3643 static HRESULT WINAPI StorageInternalImpl_Commit(
3644 IStorage* iface,
3645 DWORD grfCommitFlags) /* [in] */
3647 return S_OK;
3650 /******************************************************************************
3652 ** Storage32InternalImpl_Revert
3654 ** The non-root storages cannot be opened in transacted mode thus this function
3655 ** does nothing.
3657 static HRESULT WINAPI StorageInternalImpl_Revert(
3658 IStorage* iface)
3660 return S_OK;
3663 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3665 IStorage_Release((IStorage*)This->parentStorage);
3666 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3667 HeapFree(GetProcessHeap(), 0, This);
3670 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3671 IEnumSTATSTG* iface,
3672 REFIID riid,
3673 void** ppvObject)
3675 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3678 * Perform a sanity check on the parameters.
3680 if (ppvObject==0)
3681 return E_INVALIDARG;
3684 * Initialize the return parameter.
3686 *ppvObject = 0;
3689 * Compare the riid with the interface IDs implemented by this object.
3691 if (IsEqualGUID(&IID_IUnknown, riid) ||
3692 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3694 *ppvObject = This;
3695 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3696 return S_OK;
3699 return E_NOINTERFACE;
3702 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3703 IEnumSTATSTG* iface)
3705 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3706 return InterlockedIncrement(&This->ref);
3709 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3710 IEnumSTATSTG* iface)
3712 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3714 ULONG newRef;
3716 newRef = InterlockedDecrement(&This->ref);
3719 * If the reference count goes down to 0, perform suicide.
3721 if (newRef==0)
3723 IEnumSTATSTGImpl_Destroy(This);
3726 return newRef;
3729 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3730 IEnumSTATSTG* iface,
3731 ULONG celt,
3732 STATSTG* rgelt,
3733 ULONG* pceltFetched)
3735 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3737 StgProperty currentProperty;
3738 STATSTG* currentReturnStruct = rgelt;
3739 ULONG objectFetched = 0;
3740 ULONG currentSearchNode;
3743 * Perform a sanity check on the parameters.
3745 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3746 return E_INVALIDARG;
3749 * To avoid the special case, get another pointer to a ULONG value if
3750 * the caller didn't supply one.
3752 if (pceltFetched==0)
3753 pceltFetched = &objectFetched;
3756 * Start the iteration, we will iterate until we hit the end of the
3757 * linked list or until we hit the number of items to iterate through
3759 *pceltFetched = 0;
3762 * Start with the node at the top of the stack.
3764 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3766 while ( ( *pceltFetched < celt) &&
3767 ( currentSearchNode!=PROPERTY_NULL) )
3770 * Remove the top node from the stack
3772 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3775 * Read the property from the storage.
3777 StorageImpl_ReadProperty(This->parentStorage,
3778 currentSearchNode,
3779 &currentProperty);
3782 * Copy the information to the return buffer.
3784 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3785 &currentProperty,
3786 STATFLAG_DEFAULT);
3789 * Step to the next item in the iteration
3791 (*pceltFetched)++;
3792 currentReturnStruct++;
3795 * Push the next search node in the search stack.
3797 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3800 * continue the iteration.
3802 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3805 if (*pceltFetched == celt)
3806 return S_OK;
3808 return S_FALSE;
3812 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3813 IEnumSTATSTG* iface,
3814 ULONG celt)
3816 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3818 StgProperty currentProperty;
3819 ULONG objectFetched = 0;
3820 ULONG currentSearchNode;
3823 * Start with the node at the top of the stack.
3825 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3827 while ( (objectFetched < celt) &&
3828 (currentSearchNode!=PROPERTY_NULL) )
3831 * Remove the top node from the stack
3833 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3836 * Read the property from the storage.
3838 StorageImpl_ReadProperty(This->parentStorage,
3839 currentSearchNode,
3840 &currentProperty);
3843 * Step to the next item in the iteration
3845 objectFetched++;
3848 * Push the next search node in the search stack.
3850 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3853 * continue the iteration.
3855 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3858 if (objectFetched == celt)
3859 return S_OK;
3861 return S_FALSE;
3864 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3865 IEnumSTATSTG* iface)
3867 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3869 StgProperty rootProperty;
3870 BOOL readSuccessful;
3873 * Re-initialize the search stack to an empty stack
3875 This->stackSize = 0;
3878 * Read the root property from the storage.
3880 readSuccessful = StorageImpl_ReadProperty(
3881 This->parentStorage,
3882 This->firstPropertyNode,
3883 &rootProperty);
3885 if (readSuccessful)
3887 assert(rootProperty.sizeOfNameString!=0);
3890 * Push the search node in the search stack.
3892 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3895 return S_OK;
3898 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3899 IEnumSTATSTG* iface,
3900 IEnumSTATSTG** ppenum)
3902 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3904 IEnumSTATSTGImpl* newClone;
3907 * Perform a sanity check on the parameters.
3909 if (ppenum==0)
3910 return E_INVALIDARG;
3912 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3913 This->firstPropertyNode);
3917 * The new clone enumeration must point to the same current node as
3918 * the ole one.
3920 newClone->stackSize = This->stackSize ;
3921 newClone->stackMaxSize = This->stackMaxSize ;
3922 newClone->stackToVisit =
3923 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3925 memcpy(
3926 newClone->stackToVisit,
3927 This->stackToVisit,
3928 sizeof(ULONG) * newClone->stackSize);
3930 *ppenum = (IEnumSTATSTG*)newClone;
3933 * Don't forget to nail down a reference to the clone before
3934 * returning it.
3936 IEnumSTATSTGImpl_AddRef(*ppenum);
3938 return S_OK;
3941 static INT IEnumSTATSTGImpl_FindParentProperty(
3942 IEnumSTATSTGImpl *This,
3943 ULONG childProperty,
3944 StgProperty *currentProperty,
3945 ULONG *thisNodeId)
3947 ULONG currentSearchNode;
3948 ULONG foundNode;
3951 * To avoid the special case, get another pointer to a ULONG value if
3952 * the caller didn't supply one.
3954 if (thisNodeId==0)
3955 thisNodeId = &foundNode;
3958 * Start with the node at the top of the stack.
3960 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3963 while (currentSearchNode!=PROPERTY_NULL)
3966 * Store the current node in the returned parameters
3968 *thisNodeId = currentSearchNode;
3971 * Remove the top node from the stack
3973 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3976 * Read the property from the storage.
3978 StorageImpl_ReadProperty(
3979 This->parentStorage,
3980 currentSearchNode,
3981 currentProperty);
3983 if (currentProperty->previousProperty == childProperty)
3984 return PROPERTY_RELATION_PREVIOUS;
3986 else if (currentProperty->nextProperty == childProperty)
3987 return PROPERTY_RELATION_NEXT;
3989 else if (currentProperty->dirProperty == childProperty)
3990 return PROPERTY_RELATION_DIR;
3993 * Push the next search node in the search stack.
3995 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3998 * continue the iteration.
4000 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4003 return PROPERTY_NULL;
4006 static ULONG IEnumSTATSTGImpl_FindProperty(
4007 IEnumSTATSTGImpl* This,
4008 const OLECHAR* lpszPropName,
4009 StgProperty* currentProperty)
4011 ULONG currentSearchNode;
4014 * Start with the node at the top of the stack.
4016 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4018 while (currentSearchNode!=PROPERTY_NULL)
4021 * Remove the top node from the stack
4023 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4026 * Read the property from the storage.
4028 StorageImpl_ReadProperty(This->parentStorage,
4029 currentSearchNode,
4030 currentProperty);
4032 if (propertyNameCmp(currentProperty->name, lpszPropName) == 0)
4033 return currentSearchNode;
4036 * Push the next search node in the search stack.
4038 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4041 * continue the iteration.
4043 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4046 return PROPERTY_NULL;
4049 static void IEnumSTATSTGImpl_PushSearchNode(
4050 IEnumSTATSTGImpl* This,
4051 ULONG nodeToPush)
4053 StgProperty rootProperty;
4054 BOOL readSuccessful;
4057 * First, make sure we're not trying to push an unexisting node.
4059 if (nodeToPush==PROPERTY_NULL)
4060 return;
4063 * First push the node to the stack
4065 if (This->stackSize == This->stackMaxSize)
4067 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4069 This->stackToVisit = HeapReAlloc(
4070 GetProcessHeap(),
4072 This->stackToVisit,
4073 sizeof(ULONG) * This->stackMaxSize);
4076 This->stackToVisit[This->stackSize] = nodeToPush;
4077 This->stackSize++;
4080 * Read the root property from the storage.
4082 readSuccessful = StorageImpl_ReadProperty(
4083 This->parentStorage,
4084 nodeToPush,
4085 &rootProperty);
4087 if (readSuccessful)
4089 assert(rootProperty.sizeOfNameString!=0);
4092 * Push the previous search node in the search stack.
4094 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4098 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4099 IEnumSTATSTGImpl* This,
4100 BOOL remove)
4102 ULONG topNode;
4104 if (This->stackSize == 0)
4105 return PROPERTY_NULL;
4107 topNode = This->stackToVisit[This->stackSize-1];
4109 if (remove)
4110 This->stackSize--;
4112 return topNode;
4116 * Virtual function table for the IEnumSTATSTGImpl class.
4118 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4120 IEnumSTATSTGImpl_QueryInterface,
4121 IEnumSTATSTGImpl_AddRef,
4122 IEnumSTATSTGImpl_Release,
4123 IEnumSTATSTGImpl_Next,
4124 IEnumSTATSTGImpl_Skip,
4125 IEnumSTATSTGImpl_Reset,
4126 IEnumSTATSTGImpl_Clone
4129 /******************************************************************************
4130 ** IEnumSTATSTGImpl implementation
4133 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4134 StorageImpl* parentStorage,
4135 ULONG firstPropertyNode)
4137 IEnumSTATSTGImpl* newEnumeration;
4139 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4141 if (newEnumeration!=0)
4144 * Set-up the virtual function table and reference count.
4146 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4147 newEnumeration->ref = 0;
4150 * We want to nail-down the reference to the storage in case the
4151 * enumeration out-lives the storage in the client application.
4153 newEnumeration->parentStorage = parentStorage;
4154 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4156 newEnumeration->firstPropertyNode = firstPropertyNode;
4159 * Initialize the search stack
4161 newEnumeration->stackSize = 0;
4162 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4163 newEnumeration->stackToVisit =
4164 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4167 * Make sure the current node of the iterator is the first one.
4169 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4172 return newEnumeration;
4176 * Virtual function table for the Storage32InternalImpl class.
4178 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4180 StorageBaseImpl_QueryInterface,
4181 StorageBaseImpl_AddRef,
4182 StorageBaseImpl_Release,
4183 StorageBaseImpl_CreateStream,
4184 StorageBaseImpl_OpenStream,
4185 StorageImpl_CreateStorage,
4186 StorageBaseImpl_OpenStorage,
4187 StorageImpl_CopyTo,
4188 StorageImpl_MoveElementTo,
4189 StorageInternalImpl_Commit,
4190 StorageInternalImpl_Revert,
4191 StorageBaseImpl_EnumElements,
4192 StorageImpl_DestroyElement,
4193 StorageBaseImpl_RenameElement,
4194 StorageImpl_SetElementTimes,
4195 StorageBaseImpl_SetClass,
4196 StorageImpl_SetStateBits,
4197 StorageBaseImpl_Stat
4200 /******************************************************************************
4201 ** Storage32InternalImpl implementation
4204 static StorageInternalImpl* StorageInternalImpl_Construct(
4205 StorageImpl* ancestorStorage,
4206 DWORD openFlags,
4207 ULONG rootPropertyIndex)
4209 StorageInternalImpl* newStorage;
4212 * Allocate space for the new storage object
4214 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4216 if (newStorage!=0)
4219 * Initialize the stream list
4221 list_init(&newStorage->base.strmHead);
4224 * Initialize the virtual function table.
4226 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4227 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
4228 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4231 * Keep the ancestor storage pointer and nail a reference to it.
4233 newStorage->base.ancestorStorage = ancestorStorage;
4234 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4237 * Keep the index of the root property set for this storage,
4239 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4241 return newStorage;
4244 return 0;
4247 /******************************************************************************
4248 ** StorageUtl implementation
4251 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4253 WORD tmp;
4255 memcpy(&tmp, buffer+offset, sizeof(WORD));
4256 *value = lendian16toh(tmp);
4259 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4261 value = htole16(value);
4262 memcpy(buffer+offset, &value, sizeof(WORD));
4265 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4267 DWORD tmp;
4269 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4270 *value = lendian32toh(tmp);
4273 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4275 value = htole32(value);
4276 memcpy(buffer+offset, &value, sizeof(DWORD));
4279 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4280 ULARGE_INTEGER* value)
4282 #ifdef WORDS_BIGENDIAN
4283 ULARGE_INTEGER tmp;
4285 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4286 value->u.LowPart = htole32(tmp.u.HighPart);
4287 value->u.HighPart = htole32(tmp.u.LowPart);
4288 #else
4289 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4290 #endif
4293 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4294 const ULARGE_INTEGER *value)
4296 #ifdef WORDS_BIGENDIAN
4297 ULARGE_INTEGER tmp;
4299 tmp.u.LowPart = htole32(value->u.HighPart);
4300 tmp.u.HighPart = htole32(value->u.LowPart);
4301 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4302 #else
4303 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4304 #endif
4307 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4309 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4310 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4311 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4313 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4316 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4318 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4319 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4320 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4322 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4325 void StorageUtl_CopyPropertyToSTATSTG(
4326 STATSTG* destination,
4327 const StgProperty* source,
4328 int statFlags)
4331 * The copy of the string occurs only when the flag is not set
4333 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4334 (source->name == NULL) ||
4335 (source->name[0] == 0) )
4337 destination->pwcsName = 0;
4339 else
4341 destination->pwcsName =
4342 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4344 strcpyW(destination->pwcsName, source->name);
4347 switch (source->propertyType)
4349 case PROPTYPE_STORAGE:
4350 case PROPTYPE_ROOT:
4351 destination->type = STGTY_STORAGE;
4352 break;
4353 case PROPTYPE_STREAM:
4354 destination->type = STGTY_STREAM;
4355 break;
4356 default:
4357 destination->type = STGTY_STREAM;
4358 break;
4361 destination->cbSize = source->size;
4363 currentReturnStruct->mtime = {0}; TODO
4364 currentReturnStruct->ctime = {0};
4365 currentReturnStruct->atime = {0};
4367 destination->grfMode = 0;
4368 destination->grfLocksSupported = 0;
4369 destination->clsid = source->propertyUniqueID;
4370 destination->grfStateBits = 0;
4371 destination->reserved = 0;
4374 /******************************************************************************
4375 ** BlockChainStream implementation
4378 BlockChainStream* BlockChainStream_Construct(
4379 StorageImpl* parentStorage,
4380 ULONG* headOfStreamPlaceHolder,
4381 ULONG propertyIndex)
4383 BlockChainStream* newStream;
4384 ULONG blockIndex;
4386 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4388 newStream->parentStorage = parentStorage;
4389 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4390 newStream->ownerPropertyIndex = propertyIndex;
4391 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4392 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4393 newStream->numBlocks = 0;
4395 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4397 while (blockIndex != BLOCK_END_OF_CHAIN)
4399 newStream->numBlocks++;
4400 newStream->tailIndex = blockIndex;
4402 if(FAILED(StorageImpl_GetNextBlockInChain(
4403 parentStorage,
4404 blockIndex,
4405 &blockIndex)))
4407 HeapFree(GetProcessHeap(), 0, newStream);
4408 return NULL;
4412 return newStream;
4415 void BlockChainStream_Destroy(BlockChainStream* This)
4417 HeapFree(GetProcessHeap(), 0, This);
4420 /******************************************************************************
4421 * BlockChainStream_GetHeadOfChain
4423 * Returns the head of this stream chain.
4424 * Some special chains don't have properties, their heads are kept in
4425 * This->headOfStreamPlaceHolder.
4428 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4430 StgProperty chainProperty;
4431 BOOL readSuccessful;
4433 if (This->headOfStreamPlaceHolder != 0)
4434 return *(This->headOfStreamPlaceHolder);
4436 if (This->ownerPropertyIndex != PROPERTY_NULL)
4438 readSuccessful = StorageImpl_ReadProperty(
4439 This->parentStorage,
4440 This->ownerPropertyIndex,
4441 &chainProperty);
4443 if (readSuccessful)
4445 return chainProperty.startingBlock;
4449 return BLOCK_END_OF_CHAIN;
4452 /******************************************************************************
4453 * BlockChainStream_GetCount
4455 * Returns the number of blocks that comprises this chain.
4456 * This is not the size of the stream as the last block may not be full!
4459 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4461 ULONG blockIndex;
4462 ULONG count = 0;
4464 blockIndex = BlockChainStream_GetHeadOfChain(This);
4466 while (blockIndex != BLOCK_END_OF_CHAIN)
4468 count++;
4470 if(FAILED(StorageImpl_GetNextBlockInChain(
4471 This->parentStorage,
4472 blockIndex,
4473 &blockIndex)))
4474 return 0;
4477 return count;
4480 /******************************************************************************
4481 * BlockChainStream_ReadAt
4483 * Reads a specified number of bytes from this chain at the specified offset.
4484 * bytesRead may be NULL.
4485 * Failure will be returned if the specified number of bytes has not been read.
4487 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4488 ULARGE_INTEGER offset,
4489 ULONG size,
4490 void* buffer,
4491 ULONG* bytesRead)
4493 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4494 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4495 ULONG bytesToReadInBuffer;
4496 ULONG blockIndex;
4497 BYTE* bufferWalker;
4499 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4502 * Find the first block in the stream that contains part of the buffer.
4504 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4505 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4506 (blockNoInSequence < This->lastBlockNoInSequence) )
4508 blockIndex = BlockChainStream_GetHeadOfChain(This);
4509 This->lastBlockNoInSequence = blockNoInSequence;
4511 else
4513 ULONG temp = blockNoInSequence;
4515 blockIndex = This->lastBlockNoInSequenceIndex;
4516 blockNoInSequence -= This->lastBlockNoInSequence;
4517 This->lastBlockNoInSequence = temp;
4520 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4522 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4523 return STG_E_DOCFILECORRUPT;
4524 blockNoInSequence--;
4527 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4528 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4530 This->lastBlockNoInSequenceIndex = blockIndex;
4533 * Start reading the buffer.
4535 *bytesRead = 0;
4536 bufferWalker = buffer;
4538 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4540 ULARGE_INTEGER ulOffset;
4541 DWORD bytesReadAt;
4543 * Calculate how many bytes we can copy from this big block.
4545 bytesToReadInBuffer =
4546 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4548 TRACE("block %i\n",blockIndex);
4549 ulOffset.u.HighPart = 0;
4550 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4551 offsetInBlock;
4553 StorageImpl_ReadAt(This->parentStorage,
4554 ulOffset,
4555 bufferWalker,
4556 bytesToReadInBuffer,
4557 &bytesReadAt);
4559 * Step to the next big block.
4561 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4562 return STG_E_DOCFILECORRUPT;
4564 bufferWalker += bytesReadAt;
4565 size -= bytesReadAt;
4566 *bytesRead += bytesReadAt;
4567 offsetInBlock = 0; /* There is no offset on the next block */
4569 if (bytesToReadInBuffer != bytesReadAt)
4570 break;
4573 return (size == 0) ? S_OK : STG_E_READFAULT;
4576 /******************************************************************************
4577 * BlockChainStream_WriteAt
4579 * Writes the specified number of bytes to this chain at the specified offset.
4580 * bytesWritten may be NULL.
4581 * Will fail if not all specified number of bytes have been written.
4583 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4584 ULARGE_INTEGER offset,
4585 ULONG size,
4586 const void* buffer,
4587 ULONG* bytesWritten)
4589 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4590 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4591 ULONG bytesToWrite;
4592 ULONG blockIndex;
4593 const BYTE* bufferWalker;
4596 * Find the first block in the stream that contains part of the buffer.
4598 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4599 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4600 (blockNoInSequence < This->lastBlockNoInSequence) )
4602 blockIndex = BlockChainStream_GetHeadOfChain(This);
4603 This->lastBlockNoInSequence = blockNoInSequence;
4605 else
4607 ULONG temp = blockNoInSequence;
4609 blockIndex = This->lastBlockNoInSequenceIndex;
4610 blockNoInSequence -= This->lastBlockNoInSequence;
4611 This->lastBlockNoInSequence = temp;
4614 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4616 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4617 &blockIndex)))
4618 return STG_E_DOCFILECORRUPT;
4619 blockNoInSequence--;
4622 This->lastBlockNoInSequenceIndex = blockIndex;
4624 /* BlockChainStream_SetSize should have already been called to ensure we have
4625 * enough blocks in the chain to write into */
4626 if (blockIndex == BLOCK_END_OF_CHAIN)
4628 ERR("not enough blocks in chain to write data\n");
4629 return STG_E_DOCFILECORRUPT;
4632 *bytesWritten = 0;
4633 bufferWalker = buffer;
4635 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4637 ULARGE_INTEGER ulOffset;
4638 DWORD bytesWrittenAt;
4640 * Calculate how many bytes we can copy from this big block.
4642 bytesToWrite =
4643 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4645 TRACE("block %i\n",blockIndex);
4646 ulOffset.u.HighPart = 0;
4647 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4648 offsetInBlock;
4650 StorageImpl_WriteAt(This->parentStorage,
4651 ulOffset,
4652 bufferWalker,
4653 bytesToWrite,
4654 &bytesWrittenAt);
4657 * Step to the next big block.
4659 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4660 &blockIndex)))
4661 return STG_E_DOCFILECORRUPT;
4663 bufferWalker += bytesWrittenAt;
4664 size -= bytesWrittenAt;
4665 *bytesWritten += bytesWrittenAt;
4666 offsetInBlock = 0; /* There is no offset on the next block */
4668 if (bytesWrittenAt != bytesToWrite)
4669 break;
4672 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4675 /******************************************************************************
4676 * BlockChainStream_Shrink
4678 * Shrinks this chain in the big block depot.
4680 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4681 ULARGE_INTEGER newSize)
4683 ULONG blockIndex, extraBlock;
4684 ULONG numBlocks;
4685 ULONG count = 1;
4688 * Reset the last accessed block cache.
4690 This->lastBlockNoInSequence = 0xFFFFFFFF;
4691 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4694 * Figure out how many blocks are needed to contain the new size
4696 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4698 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4699 numBlocks++;
4701 blockIndex = BlockChainStream_GetHeadOfChain(This);
4704 * Go to the new end of chain
4706 while (count < numBlocks)
4708 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4709 &blockIndex)))
4710 return FALSE;
4711 count++;
4714 /* Get the next block before marking the new end */
4715 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4716 &extraBlock)))
4717 return FALSE;
4719 /* Mark the new end of chain */
4720 StorageImpl_SetNextBlockInChain(
4721 This->parentStorage,
4722 blockIndex,
4723 BLOCK_END_OF_CHAIN);
4725 This->tailIndex = blockIndex;
4726 This->numBlocks = numBlocks;
4729 * Mark the extra blocks as free
4731 while (extraBlock != BLOCK_END_OF_CHAIN)
4733 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4734 &blockIndex)))
4735 return FALSE;
4736 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4737 extraBlock = blockIndex;
4740 return TRUE;
4743 /******************************************************************************
4744 * BlockChainStream_Enlarge
4746 * Grows this chain in the big block depot.
4748 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4749 ULARGE_INTEGER newSize)
4751 ULONG blockIndex, currentBlock;
4752 ULONG newNumBlocks;
4753 ULONG oldNumBlocks = 0;
4755 blockIndex = BlockChainStream_GetHeadOfChain(This);
4758 * Empty chain. Create the head.
4760 if (blockIndex == BLOCK_END_OF_CHAIN)
4762 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4763 StorageImpl_SetNextBlockInChain(This->parentStorage,
4764 blockIndex,
4765 BLOCK_END_OF_CHAIN);
4767 if (This->headOfStreamPlaceHolder != 0)
4769 *(This->headOfStreamPlaceHolder) = blockIndex;
4771 else
4773 StgProperty chainProp;
4774 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4776 StorageImpl_ReadProperty(
4777 This->parentStorage,
4778 This->ownerPropertyIndex,
4779 &chainProp);
4781 chainProp.startingBlock = blockIndex;
4783 StorageImpl_WriteProperty(
4784 This->parentStorage,
4785 This->ownerPropertyIndex,
4786 &chainProp);
4789 This->tailIndex = blockIndex;
4790 This->numBlocks = 1;
4794 * Figure out how many blocks are needed to contain this stream
4796 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4798 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4799 newNumBlocks++;
4802 * Go to the current end of chain
4804 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4806 currentBlock = blockIndex;
4808 while (blockIndex != BLOCK_END_OF_CHAIN)
4810 This->numBlocks++;
4811 currentBlock = blockIndex;
4813 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4814 &blockIndex)))
4815 return FALSE;
4818 This->tailIndex = currentBlock;
4821 currentBlock = This->tailIndex;
4822 oldNumBlocks = This->numBlocks;
4825 * Add new blocks to the chain
4827 if (oldNumBlocks < newNumBlocks)
4829 while (oldNumBlocks < newNumBlocks)
4831 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4833 StorageImpl_SetNextBlockInChain(
4834 This->parentStorage,
4835 currentBlock,
4836 blockIndex);
4838 StorageImpl_SetNextBlockInChain(
4839 This->parentStorage,
4840 blockIndex,
4841 BLOCK_END_OF_CHAIN);
4843 currentBlock = blockIndex;
4844 oldNumBlocks++;
4847 This->tailIndex = blockIndex;
4848 This->numBlocks = newNumBlocks;
4851 return TRUE;
4854 /******************************************************************************
4855 * BlockChainStream_SetSize
4857 * Sets the size of this stream. The big block depot will be updated.
4858 * The file will grow if we grow the chain.
4860 * TODO: Free the actual blocks in the file when we shrink the chain.
4861 * Currently, the blocks are still in the file. So the file size
4862 * doesn't shrink even if we shrink streams.
4864 BOOL BlockChainStream_SetSize(
4865 BlockChainStream* This,
4866 ULARGE_INTEGER newSize)
4868 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4870 if (newSize.u.LowPart == size.u.LowPart)
4871 return TRUE;
4873 if (newSize.u.LowPart < size.u.LowPart)
4875 BlockChainStream_Shrink(This, newSize);
4877 else
4879 BlockChainStream_Enlarge(This, newSize);
4882 return TRUE;
4885 /******************************************************************************
4886 * BlockChainStream_GetSize
4888 * Returns the size of this chain.
4889 * Will return the block count if this chain doesn't have a property.
4891 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4893 StgProperty chainProperty;
4895 if(This->headOfStreamPlaceHolder == NULL)
4898 * This chain is a data stream read the property and return
4899 * the appropriate size
4901 StorageImpl_ReadProperty(
4902 This->parentStorage,
4903 This->ownerPropertyIndex,
4904 &chainProperty);
4906 return chainProperty.size;
4908 else
4911 * this chain is a chain that does not have a property, figure out the
4912 * size by making the product number of used blocks times the
4913 * size of them
4915 ULARGE_INTEGER result;
4916 result.u.HighPart = 0;
4918 result.u.LowPart =
4919 BlockChainStream_GetCount(This) *
4920 This->parentStorage->bigBlockSize;
4922 return result;
4926 /******************************************************************************
4927 ** SmallBlockChainStream implementation
4930 SmallBlockChainStream* SmallBlockChainStream_Construct(
4931 StorageImpl* parentStorage,
4932 ULONG propertyIndex)
4934 SmallBlockChainStream* newStream;
4936 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4938 newStream->parentStorage = parentStorage;
4939 newStream->ownerPropertyIndex = propertyIndex;
4941 return newStream;
4944 void SmallBlockChainStream_Destroy(
4945 SmallBlockChainStream* This)
4947 HeapFree(GetProcessHeap(), 0, This);
4950 /******************************************************************************
4951 * SmallBlockChainStream_GetHeadOfChain
4953 * Returns the head of this chain of small blocks.
4955 static ULONG SmallBlockChainStream_GetHeadOfChain(
4956 SmallBlockChainStream* This)
4958 StgProperty chainProperty;
4959 BOOL readSuccessful;
4961 if (This->ownerPropertyIndex)
4963 readSuccessful = StorageImpl_ReadProperty(
4964 This->parentStorage,
4965 This->ownerPropertyIndex,
4966 &chainProperty);
4968 if (readSuccessful)
4970 return chainProperty.startingBlock;
4975 return BLOCK_END_OF_CHAIN;
4978 /******************************************************************************
4979 * SmallBlockChainStream_GetNextBlockInChain
4981 * Returns the index of the next small block in this chain.
4983 * Return Values:
4984 * - BLOCK_END_OF_CHAIN: end of this chain
4985 * - BLOCK_UNUSED: small block 'blockIndex' is free
4987 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4988 SmallBlockChainStream* This,
4989 ULONG blockIndex,
4990 ULONG* nextBlockInChain)
4992 ULARGE_INTEGER offsetOfBlockInDepot;
4993 DWORD buffer;
4994 ULONG bytesRead;
4995 HRESULT res;
4997 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4999 offsetOfBlockInDepot.u.HighPart = 0;
5000 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5003 * Read those bytes in the buffer from the small block file.
5005 res = BlockChainStream_ReadAt(
5006 This->parentStorage->smallBlockDepotChain,
5007 offsetOfBlockInDepot,
5008 sizeof(DWORD),
5009 &buffer,
5010 &bytesRead);
5012 if (SUCCEEDED(res))
5014 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5015 return S_OK;
5018 return res;
5021 /******************************************************************************
5022 * SmallBlockChainStream_SetNextBlockInChain
5024 * Writes the index of the next block of the specified block in the small
5025 * block depot.
5026 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5027 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5029 static void SmallBlockChainStream_SetNextBlockInChain(
5030 SmallBlockChainStream* This,
5031 ULONG blockIndex,
5032 ULONG nextBlock)
5034 ULARGE_INTEGER offsetOfBlockInDepot;
5035 DWORD buffer;
5036 ULONG bytesWritten;
5038 offsetOfBlockInDepot.u.HighPart = 0;
5039 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5041 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5044 * Read those bytes in the buffer from the small block file.
5046 BlockChainStream_WriteAt(
5047 This->parentStorage->smallBlockDepotChain,
5048 offsetOfBlockInDepot,
5049 sizeof(DWORD),
5050 &buffer,
5051 &bytesWritten);
5054 /******************************************************************************
5055 * SmallBlockChainStream_FreeBlock
5057 * Flag small block 'blockIndex' as free in the small block depot.
5059 static void SmallBlockChainStream_FreeBlock(
5060 SmallBlockChainStream* This,
5061 ULONG blockIndex)
5063 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5066 /******************************************************************************
5067 * SmallBlockChainStream_GetNextFreeBlock
5069 * Returns the index of a free small block. The small block depot will be
5070 * enlarged if necessary. The small block chain will also be enlarged if
5071 * necessary.
5073 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5074 SmallBlockChainStream* This)
5076 ULARGE_INTEGER offsetOfBlockInDepot;
5077 DWORD buffer;
5078 ULONG bytesRead;
5079 ULONG blockIndex = 0;
5080 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5081 HRESULT res = S_OK;
5082 ULONG smallBlocksPerBigBlock;
5084 offsetOfBlockInDepot.u.HighPart = 0;
5087 * Scan the small block depot for a free block
5089 while (nextBlockIndex != BLOCK_UNUSED)
5091 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5093 res = BlockChainStream_ReadAt(
5094 This->parentStorage->smallBlockDepotChain,
5095 offsetOfBlockInDepot,
5096 sizeof(DWORD),
5097 &buffer,
5098 &bytesRead);
5101 * If we run out of space for the small block depot, enlarge it
5103 if (SUCCEEDED(res))
5105 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5107 if (nextBlockIndex != BLOCK_UNUSED)
5108 blockIndex++;
5110 else
5112 ULONG count =
5113 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5115 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5116 ULONG nextBlock, newsbdIndex;
5117 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5119 nextBlock = sbdIndex;
5120 while (nextBlock != BLOCK_END_OF_CHAIN)
5122 sbdIndex = nextBlock;
5123 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5126 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5127 if (sbdIndex != BLOCK_END_OF_CHAIN)
5128 StorageImpl_SetNextBlockInChain(
5129 This->parentStorage,
5130 sbdIndex,
5131 newsbdIndex);
5133 StorageImpl_SetNextBlockInChain(
5134 This->parentStorage,
5135 newsbdIndex,
5136 BLOCK_END_OF_CHAIN);
5139 * Initialize all the small blocks to free
5141 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5142 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5144 if (count == 0)
5147 * We have just created the small block depot.
5149 StgProperty rootProp;
5150 ULONG sbStartIndex;
5153 * Save it in the header
5155 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5156 StorageImpl_SaveFileHeader(This->parentStorage);
5159 * And allocate the first big block that will contain small blocks
5161 sbStartIndex =
5162 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5164 StorageImpl_SetNextBlockInChain(
5165 This->parentStorage,
5166 sbStartIndex,
5167 BLOCK_END_OF_CHAIN);
5169 StorageImpl_ReadProperty(
5170 This->parentStorage,
5171 This->parentStorage->base.rootPropertySetIndex,
5172 &rootProp);
5174 rootProp.startingBlock = sbStartIndex;
5175 rootProp.size.u.HighPart = 0;
5176 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5178 StorageImpl_WriteProperty(
5179 This->parentStorage,
5180 This->parentStorage->base.rootPropertySetIndex,
5181 &rootProp);
5183 else
5184 StorageImpl_SaveFileHeader(This->parentStorage);
5188 smallBlocksPerBigBlock =
5189 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5192 * Verify if we have to allocate big blocks to contain small blocks
5194 if (blockIndex % smallBlocksPerBigBlock == 0)
5196 StgProperty rootProp;
5197 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5199 StorageImpl_ReadProperty(
5200 This->parentStorage,
5201 This->parentStorage->base.rootPropertySetIndex,
5202 &rootProp);
5204 if (rootProp.size.u.LowPart <
5205 (blocksRequired * This->parentStorage->bigBlockSize))
5207 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5209 BlockChainStream_SetSize(
5210 This->parentStorage->smallBlockRootChain,
5211 rootProp.size);
5213 StorageImpl_WriteProperty(
5214 This->parentStorage,
5215 This->parentStorage->base.rootPropertySetIndex,
5216 &rootProp);
5220 return blockIndex;
5223 /******************************************************************************
5224 * SmallBlockChainStream_ReadAt
5226 * Reads a specified number of bytes from this chain at the specified offset.
5227 * bytesRead may be NULL.
5228 * Failure will be returned if the specified number of bytes has not been read.
5230 HRESULT SmallBlockChainStream_ReadAt(
5231 SmallBlockChainStream* This,
5232 ULARGE_INTEGER offset,
5233 ULONG size,
5234 void* buffer,
5235 ULONG* bytesRead)
5237 HRESULT rc = S_OK;
5238 ULARGE_INTEGER offsetInBigBlockFile;
5239 ULONG blockNoInSequence =
5240 offset.u.LowPart / This->parentStorage->smallBlockSize;
5242 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5243 ULONG bytesToReadInBuffer;
5244 ULONG blockIndex;
5245 ULONG bytesReadFromBigBlockFile;
5246 BYTE* bufferWalker;
5249 * This should never happen on a small block file.
5251 assert(offset.u.HighPart==0);
5254 * Find the first block in the stream that contains part of the buffer.
5256 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5258 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5260 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5261 if(FAILED(rc))
5262 return rc;
5263 blockNoInSequence--;
5267 * Start reading the buffer.
5269 *bytesRead = 0;
5270 bufferWalker = buffer;
5272 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5275 * Calculate how many bytes we can copy from this small block.
5277 bytesToReadInBuffer =
5278 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5281 * Calculate the offset of the small block in the small block file.
5283 offsetInBigBlockFile.u.HighPart = 0;
5284 offsetInBigBlockFile.u.LowPart =
5285 blockIndex * This->parentStorage->smallBlockSize;
5287 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5290 * Read those bytes in the buffer from the small block file.
5291 * The small block has already been identified so it shouldn't fail
5292 * unless the file is corrupt.
5294 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5295 offsetInBigBlockFile,
5296 bytesToReadInBuffer,
5297 bufferWalker,
5298 &bytesReadFromBigBlockFile);
5300 if (FAILED(rc))
5301 return rc;
5304 * Step to the next big block.
5306 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5307 if(FAILED(rc))
5308 return STG_E_DOCFILECORRUPT;
5310 bufferWalker += bytesReadFromBigBlockFile;
5311 size -= bytesReadFromBigBlockFile;
5312 *bytesRead += bytesReadFromBigBlockFile;
5313 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5316 return (size == 0) ? S_OK : STG_E_READFAULT;
5319 /******************************************************************************
5320 * SmallBlockChainStream_WriteAt
5322 * Writes the specified number of bytes to this chain at the specified offset.
5323 * bytesWritten may be NULL.
5324 * Will fail if not all specified number of bytes have been written.
5326 HRESULT SmallBlockChainStream_WriteAt(
5327 SmallBlockChainStream* This,
5328 ULARGE_INTEGER offset,
5329 ULONG size,
5330 const void* buffer,
5331 ULONG* bytesWritten)
5333 ULARGE_INTEGER offsetInBigBlockFile;
5334 ULONG blockNoInSequence =
5335 offset.u.LowPart / This->parentStorage->smallBlockSize;
5337 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5338 ULONG bytesToWriteInBuffer;
5339 ULONG blockIndex;
5340 ULONG bytesWrittenToBigBlockFile;
5341 const BYTE* bufferWalker;
5342 HRESULT res;
5345 * This should never happen on a small block file.
5347 assert(offset.u.HighPart==0);
5350 * Find the first block in the stream that contains part of the buffer.
5352 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5354 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5356 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5357 return STG_E_DOCFILECORRUPT;
5358 blockNoInSequence--;
5362 * Start writing the buffer.
5364 * Here, I'm casting away the constness on the buffer variable
5365 * This is OK since we don't intend to modify that buffer.
5367 *bytesWritten = 0;
5368 bufferWalker = buffer;
5369 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5372 * Calculate how many bytes we can copy to this small block.
5374 bytesToWriteInBuffer =
5375 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5378 * Calculate the offset of the small block in the small block file.
5380 offsetInBigBlockFile.u.HighPart = 0;
5381 offsetInBigBlockFile.u.LowPart =
5382 blockIndex * This->parentStorage->smallBlockSize;
5384 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5387 * Write those bytes in the buffer to the small block file.
5389 res = BlockChainStream_WriteAt(
5390 This->parentStorage->smallBlockRootChain,
5391 offsetInBigBlockFile,
5392 bytesToWriteInBuffer,
5393 bufferWalker,
5394 &bytesWrittenToBigBlockFile);
5395 if (FAILED(res))
5396 return res;
5399 * Step to the next big block.
5401 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5402 &blockIndex)))
5403 return FALSE;
5404 bufferWalker += bytesWrittenToBigBlockFile;
5405 size -= bytesWrittenToBigBlockFile;
5406 *bytesWritten += bytesWrittenToBigBlockFile;
5407 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5410 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5413 /******************************************************************************
5414 * SmallBlockChainStream_Shrink
5416 * Shrinks this chain in the small block depot.
5418 static BOOL SmallBlockChainStream_Shrink(
5419 SmallBlockChainStream* This,
5420 ULARGE_INTEGER newSize)
5422 ULONG blockIndex, extraBlock;
5423 ULONG numBlocks;
5424 ULONG count = 0;
5426 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5428 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5429 numBlocks++;
5431 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5434 * Go to the new end of chain
5436 while (count < numBlocks)
5438 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5439 &blockIndex)))
5440 return FALSE;
5441 count++;
5445 * If the count is 0, we have a special case, the head of the chain was
5446 * just freed.
5448 if (count == 0)
5450 StgProperty chainProp;
5452 StorageImpl_ReadProperty(This->parentStorage,
5453 This->ownerPropertyIndex,
5454 &chainProp);
5456 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5458 StorageImpl_WriteProperty(This->parentStorage,
5459 This->ownerPropertyIndex,
5460 &chainProp);
5463 * We start freeing the chain at the head block.
5465 extraBlock = blockIndex;
5467 else
5469 /* Get the next block before marking the new end */
5470 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5471 &extraBlock)))
5472 return FALSE;
5474 /* Mark the new end of chain */
5475 SmallBlockChainStream_SetNextBlockInChain(
5476 This,
5477 blockIndex,
5478 BLOCK_END_OF_CHAIN);
5482 * Mark the extra blocks as free
5484 while (extraBlock != BLOCK_END_OF_CHAIN)
5486 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5487 &blockIndex)))
5488 return FALSE;
5489 SmallBlockChainStream_FreeBlock(This, extraBlock);
5490 extraBlock = blockIndex;
5493 return TRUE;
5496 /******************************************************************************
5497 * SmallBlockChainStream_Enlarge
5499 * Grows this chain in the small block depot.
5501 static BOOL SmallBlockChainStream_Enlarge(
5502 SmallBlockChainStream* This,
5503 ULARGE_INTEGER newSize)
5505 ULONG blockIndex, currentBlock;
5506 ULONG newNumBlocks;
5507 ULONG oldNumBlocks = 0;
5509 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5512 * Empty chain
5514 if (blockIndex == BLOCK_END_OF_CHAIN)
5517 StgProperty chainProp;
5519 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5520 &chainProp);
5522 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5524 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5525 &chainProp);
5527 blockIndex = chainProp.startingBlock;
5528 SmallBlockChainStream_SetNextBlockInChain(
5529 This,
5530 blockIndex,
5531 BLOCK_END_OF_CHAIN);
5534 currentBlock = blockIndex;
5537 * Figure out how many blocks are needed to contain this stream
5539 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5541 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5542 newNumBlocks++;
5545 * Go to the current end of chain
5547 while (blockIndex != BLOCK_END_OF_CHAIN)
5549 oldNumBlocks++;
5550 currentBlock = blockIndex;
5551 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5552 return FALSE;
5556 * Add new blocks to the chain
5558 while (oldNumBlocks < newNumBlocks)
5560 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5561 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5563 SmallBlockChainStream_SetNextBlockInChain(
5564 This,
5565 blockIndex,
5566 BLOCK_END_OF_CHAIN);
5568 currentBlock = blockIndex;
5569 oldNumBlocks++;
5572 return TRUE;
5575 /******************************************************************************
5576 * SmallBlockChainStream_SetSize
5578 * Sets the size of this stream.
5579 * The file will grow if we grow the chain.
5581 * TODO: Free the actual blocks in the file when we shrink the chain.
5582 * Currently, the blocks are still in the file. So the file size
5583 * doesn't shrink even if we shrink streams.
5585 BOOL SmallBlockChainStream_SetSize(
5586 SmallBlockChainStream* This,
5587 ULARGE_INTEGER newSize)
5589 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5591 if (newSize.u.LowPart == size.u.LowPart)
5592 return TRUE;
5594 if (newSize.u.LowPart < size.u.LowPart)
5596 SmallBlockChainStream_Shrink(This, newSize);
5598 else
5600 SmallBlockChainStream_Enlarge(This, newSize);
5603 return TRUE;
5606 /******************************************************************************
5607 * SmallBlockChainStream_GetSize
5609 * Returns the size of this chain.
5611 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5613 StgProperty chainProperty;
5615 StorageImpl_ReadProperty(
5616 This->parentStorage,
5617 This->ownerPropertyIndex,
5618 &chainProperty);
5620 return chainProperty.size;
5623 /******************************************************************************
5624 * StgCreateDocfile [OLE32.@]
5625 * Creates a new compound file storage object
5627 * PARAMS
5628 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5629 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5630 * reserved [ ?] unused?, usually 0
5631 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5633 * RETURNS
5634 * S_OK if the file was successfully created
5635 * some STG_E_ value if error
5636 * NOTES
5637 * if pwcsName is NULL, create file with new unique name
5638 * the function can returns
5639 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5640 * (unrealized now)
5642 HRESULT WINAPI StgCreateDocfile(
5643 LPCOLESTR pwcsName,
5644 DWORD grfMode,
5645 DWORD reserved,
5646 IStorage **ppstgOpen)
5648 StorageImpl* newStorage = 0;
5649 HANDLE hFile = INVALID_HANDLE_VALUE;
5650 HRESULT hr = STG_E_INVALIDFLAG;
5651 DWORD shareMode;
5652 DWORD accessMode;
5653 DWORD creationMode;
5654 DWORD fileAttributes;
5655 WCHAR tempFileName[MAX_PATH];
5657 TRACE("(%s, %x, %d, %p)\n",
5658 debugstr_w(pwcsName), grfMode,
5659 reserved, ppstgOpen);
5662 * Validate the parameters
5664 if (ppstgOpen == 0)
5665 return STG_E_INVALIDPOINTER;
5666 if (reserved != 0)
5667 return STG_E_INVALIDPARAMETER;
5669 /* if no share mode given then DENY_NONE is the default */
5670 if (STGM_SHARE_MODE(grfMode) == 0)
5671 grfMode |= STGM_SHARE_DENY_NONE;
5674 * Validate the STGM flags
5676 if ( FAILED( validateSTGM(grfMode) ))
5677 goto end;
5679 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5680 switch(STGM_ACCESS_MODE(grfMode))
5682 case STGM_WRITE:
5683 case STGM_READWRITE:
5684 break;
5685 default:
5686 goto end;
5689 /* in direct mode, can only use SHARE_EXCLUSIVE */
5690 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5691 goto end;
5693 /* but in transacted mode, any share mode is valid */
5696 * Generate a unique name.
5698 if (pwcsName == 0)
5700 WCHAR tempPath[MAX_PATH];
5701 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5703 memset(tempPath, 0, sizeof(tempPath));
5704 memset(tempFileName, 0, sizeof(tempFileName));
5706 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5707 tempPath[0] = '.';
5709 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5710 pwcsName = tempFileName;
5711 else
5713 hr = STG_E_INSUFFICIENTMEMORY;
5714 goto end;
5717 creationMode = TRUNCATE_EXISTING;
5719 else
5721 creationMode = GetCreationModeFromSTGM(grfMode);
5725 * Interpret the STGM value grfMode
5727 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5728 accessMode = GetAccessModeFromSTGM(grfMode);
5730 if (grfMode & STGM_DELETEONRELEASE)
5731 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5732 else
5733 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5735 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5736 FIXME("Storage share mode not implemented.\n");
5738 if (grfMode & STGM_TRANSACTED)
5739 FIXME("Transacted mode not implemented.\n");
5742 * Initialize the "out" parameter.
5744 *ppstgOpen = 0;
5746 hFile = CreateFileW(pwcsName,
5747 accessMode,
5748 shareMode,
5749 NULL,
5750 creationMode,
5751 fileAttributes,
5754 if (hFile == INVALID_HANDLE_VALUE)
5756 if(GetLastError() == ERROR_FILE_EXISTS)
5757 hr = STG_E_FILEALREADYEXISTS;
5758 else
5759 hr = E_FAIL;
5760 goto end;
5764 * Allocate and initialize the new IStorage32object.
5766 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5768 if (newStorage == 0)
5770 hr = STG_E_INSUFFICIENTMEMORY;
5771 goto end;
5774 hr = StorageImpl_Construct(
5775 newStorage,
5776 hFile,
5777 pwcsName,
5778 NULL,
5779 grfMode,
5780 TRUE,
5781 TRUE);
5783 if (FAILED(hr))
5785 HeapFree(GetProcessHeap(), 0, newStorage);
5786 goto end;
5790 * Get an "out" pointer for the caller.
5792 hr = StorageBaseImpl_QueryInterface(
5793 (IStorage*)newStorage,
5794 (REFIID)&IID_IStorage,
5795 (void**)ppstgOpen);
5796 end:
5797 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5799 return hr;
5802 /******************************************************************************
5803 * StgCreateStorageEx [OLE32.@]
5805 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5807 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5808 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5810 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5812 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5813 return STG_E_INVALIDPARAMETER;
5816 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5818 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5819 return STG_E_INVALIDPARAMETER;
5822 if (stgfmt == STGFMT_FILE)
5824 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5825 return STG_E_INVALIDPARAMETER;
5828 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5830 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5831 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5834 ERR("Invalid stgfmt argument\n");
5835 return STG_E_INVALIDPARAMETER;
5838 /******************************************************************************
5839 * StgCreatePropSetStg [OLE32.@]
5841 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5842 IPropertySetStorage **ppPropSetStg)
5844 HRESULT hr;
5846 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5847 if (reserved)
5848 hr = STG_E_INVALIDPARAMETER;
5849 else
5850 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5851 (void**)ppPropSetStg);
5852 return hr;
5855 /******************************************************************************
5856 * StgOpenStorageEx [OLE32.@]
5858 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5860 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5861 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5863 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5865 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5866 return STG_E_INVALIDPARAMETER;
5869 switch (stgfmt)
5871 case STGFMT_FILE:
5872 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5873 return STG_E_INVALIDPARAMETER;
5875 case STGFMT_STORAGE:
5876 break;
5878 case STGFMT_DOCFILE:
5879 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5881 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5882 return STG_E_INVALIDPARAMETER;
5884 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5885 break;
5887 case STGFMT_ANY:
5888 WARN("STGFMT_ANY assuming storage\n");
5889 break;
5891 default:
5892 return STG_E_INVALIDPARAMETER;
5895 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5899 /******************************************************************************
5900 * StgOpenStorage [OLE32.@]
5902 HRESULT WINAPI StgOpenStorage(
5903 const OLECHAR *pwcsName,
5904 IStorage *pstgPriority,
5905 DWORD grfMode,
5906 SNB snbExclude,
5907 DWORD reserved,
5908 IStorage **ppstgOpen)
5910 StorageImpl* newStorage = 0;
5911 HRESULT hr = S_OK;
5912 HANDLE hFile = 0;
5913 DWORD shareMode;
5914 DWORD accessMode;
5915 WCHAR fullname[MAX_PATH];
5917 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5918 debugstr_w(pwcsName), pstgPriority, grfMode,
5919 snbExclude, reserved, ppstgOpen);
5922 * Perform sanity checks
5924 if (pwcsName == 0)
5926 hr = STG_E_INVALIDNAME;
5927 goto end;
5930 if (ppstgOpen == 0)
5932 hr = STG_E_INVALIDPOINTER;
5933 goto end;
5936 if (reserved)
5938 hr = STG_E_INVALIDPARAMETER;
5939 goto end;
5942 if (grfMode & STGM_PRIORITY)
5944 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5945 return STG_E_INVALIDFLAG;
5946 if (grfMode & STGM_DELETEONRELEASE)
5947 return STG_E_INVALIDFUNCTION;
5948 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5949 return STG_E_INVALIDFLAG;
5950 grfMode &= ~0xf0; /* remove the existing sharing mode */
5951 grfMode |= STGM_SHARE_DENY_NONE;
5953 /* STGM_PRIORITY stops other IStorage objects on the same file from
5954 * committing until the STGM_PRIORITY IStorage is closed. it also
5955 * stops non-transacted mode StgOpenStorage calls with write access from
5956 * succeeding. obviously, both of these cannot be achieved through just
5957 * file share flags */
5958 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5962 * Validate the sharing mode
5964 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5965 switch(STGM_SHARE_MODE(grfMode))
5967 case STGM_SHARE_EXCLUSIVE:
5968 case STGM_SHARE_DENY_WRITE:
5969 break;
5970 default:
5971 hr = STG_E_INVALIDFLAG;
5972 goto end;
5976 * Validate the STGM flags
5978 if ( FAILED( validateSTGM(grfMode) ) ||
5979 (grfMode&STGM_CREATE))
5981 hr = STG_E_INVALIDFLAG;
5982 goto end;
5985 /* shared reading requires transacted mode */
5986 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5987 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5988 !(grfMode&STGM_TRANSACTED) )
5990 hr = STG_E_INVALIDFLAG;
5991 goto end;
5995 * Interpret the STGM value grfMode
5997 shareMode = GetShareModeFromSTGM(grfMode);
5998 accessMode = GetAccessModeFromSTGM(grfMode);
6001 * Initialize the "out" parameter.
6003 *ppstgOpen = 0;
6005 hFile = CreateFileW( pwcsName,
6006 accessMode,
6007 shareMode,
6008 NULL,
6009 OPEN_EXISTING,
6010 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6013 if (hFile==INVALID_HANDLE_VALUE)
6015 DWORD last_error = GetLastError();
6017 hr = E_FAIL;
6019 switch (last_error)
6021 case ERROR_FILE_NOT_FOUND:
6022 hr = STG_E_FILENOTFOUND;
6023 break;
6025 case ERROR_PATH_NOT_FOUND:
6026 hr = STG_E_PATHNOTFOUND;
6027 break;
6029 case ERROR_ACCESS_DENIED:
6030 case ERROR_WRITE_PROTECT:
6031 hr = STG_E_ACCESSDENIED;
6032 break;
6034 case ERROR_SHARING_VIOLATION:
6035 hr = STG_E_SHAREVIOLATION;
6036 break;
6038 default:
6039 hr = E_FAIL;
6042 goto end;
6046 * Refuse to open the file if it's too small to be a structured storage file
6047 * FIXME: verify the file when reading instead of here
6049 if (GetFileSize(hFile, NULL) < 0x100)
6051 CloseHandle(hFile);
6052 hr = STG_E_FILEALREADYEXISTS;
6053 goto end;
6057 * Allocate and initialize the new IStorage32object.
6059 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6061 if (newStorage == 0)
6063 hr = STG_E_INSUFFICIENTMEMORY;
6064 goto end;
6067 /* Initialize the storage */
6068 hr = StorageImpl_Construct(
6069 newStorage,
6070 hFile,
6071 pwcsName,
6072 NULL,
6073 grfMode,
6074 TRUE,
6075 FALSE );
6077 if (FAILED(hr))
6079 HeapFree(GetProcessHeap(), 0, newStorage);
6081 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6083 if(hr == STG_E_INVALIDHEADER)
6084 hr = STG_E_FILEALREADYEXISTS;
6085 goto end;
6088 /* prepare the file name string given in lieu of the root property name */
6089 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6090 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6091 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6094 * Get an "out" pointer for the caller.
6096 hr = StorageBaseImpl_QueryInterface(
6097 (IStorage*)newStorage,
6098 (REFIID)&IID_IStorage,
6099 (void**)ppstgOpen);
6101 end:
6102 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6103 return hr;
6106 /******************************************************************************
6107 * StgCreateDocfileOnILockBytes [OLE32.@]
6109 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6110 ILockBytes *plkbyt,
6111 DWORD grfMode,
6112 DWORD reserved,
6113 IStorage** ppstgOpen)
6115 StorageImpl* newStorage = 0;
6116 HRESULT hr = S_OK;
6119 * Validate the parameters
6121 if ((ppstgOpen == 0) || (plkbyt == 0))
6122 return STG_E_INVALIDPOINTER;
6125 * Allocate and initialize the new IStorage object.
6127 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6129 if (newStorage == 0)
6130 return STG_E_INSUFFICIENTMEMORY;
6132 hr = StorageImpl_Construct(
6133 newStorage,
6136 plkbyt,
6137 grfMode,
6138 FALSE,
6139 TRUE);
6141 if (FAILED(hr))
6143 HeapFree(GetProcessHeap(), 0, newStorage);
6144 return hr;
6148 * Get an "out" pointer for the caller.
6150 hr = StorageBaseImpl_QueryInterface(
6151 (IStorage*)newStorage,
6152 (REFIID)&IID_IStorage,
6153 (void**)ppstgOpen);
6155 return hr;
6158 /******************************************************************************
6159 * StgOpenStorageOnILockBytes [OLE32.@]
6161 HRESULT WINAPI StgOpenStorageOnILockBytes(
6162 ILockBytes *plkbyt,
6163 IStorage *pstgPriority,
6164 DWORD grfMode,
6165 SNB snbExclude,
6166 DWORD reserved,
6167 IStorage **ppstgOpen)
6169 StorageImpl* newStorage = 0;
6170 HRESULT hr = S_OK;
6173 * Perform a sanity check
6175 if ((plkbyt == 0) || (ppstgOpen == 0))
6176 return STG_E_INVALIDPOINTER;
6179 * Validate the STGM flags
6181 if ( FAILED( validateSTGM(grfMode) ))
6182 return STG_E_INVALIDFLAG;
6185 * Initialize the "out" parameter.
6187 *ppstgOpen = 0;
6190 * Allocate and initialize the new IStorage object.
6192 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6194 if (newStorage == 0)
6195 return STG_E_INSUFFICIENTMEMORY;
6197 hr = StorageImpl_Construct(
6198 newStorage,
6201 plkbyt,
6202 grfMode,
6203 FALSE,
6204 FALSE);
6206 if (FAILED(hr))
6208 HeapFree(GetProcessHeap(), 0, newStorage);
6209 return hr;
6213 * Get an "out" pointer for the caller.
6215 hr = StorageBaseImpl_QueryInterface(
6216 (IStorage*)newStorage,
6217 (REFIID)&IID_IStorage,
6218 (void**)ppstgOpen);
6220 return hr;
6223 /******************************************************************************
6224 * StgSetTimes [ole32.@]
6225 * StgSetTimes [OLE32.@]
6229 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6230 FILETIME const *patime, FILETIME const *pmtime)
6232 IStorage *stg = NULL;
6233 HRESULT r;
6235 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6237 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6238 0, 0, &stg);
6239 if( SUCCEEDED(r) )
6241 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6242 IStorage_Release(stg);
6245 return r;
6248 /******************************************************************************
6249 * StgIsStorageILockBytes [OLE32.@]
6251 * Determines if the ILockBytes contains a storage object.
6253 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6255 BYTE sig[8];
6256 ULARGE_INTEGER offset;
6258 offset.u.HighPart = 0;
6259 offset.u.LowPart = 0;
6261 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6263 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6264 return S_OK;
6266 return S_FALSE;
6269 /******************************************************************************
6270 * WriteClassStg [OLE32.@]
6272 * This method will store the specified CLSID in the specified storage object
6274 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6276 HRESULT hRes;
6278 if(!pStg)
6279 return E_INVALIDARG;
6281 if(!rclsid)
6282 return STG_E_INVALIDPOINTER;
6284 hRes = IStorage_SetClass(pStg, rclsid);
6286 return hRes;
6289 /***********************************************************************
6290 * ReadClassStg (OLE32.@)
6292 * This method reads the CLSID previously written to a storage object with
6293 * the WriteClassStg.
6295 * PARAMS
6296 * pstg [I] IStorage pointer
6297 * pclsid [O] Pointer to where the CLSID is written
6299 * RETURNS
6300 * Success: S_OK.
6301 * Failure: HRESULT code.
6303 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6305 STATSTG pstatstg;
6306 HRESULT hRes;
6308 TRACE("(%p, %p)\n", pstg, pclsid);
6310 if(!pstg || !pclsid)
6311 return E_INVALIDARG;
6314 * read a STATSTG structure (contains the clsid) from the storage
6316 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6318 if(SUCCEEDED(hRes))
6319 *pclsid=pstatstg.clsid;
6321 return hRes;
6324 /***********************************************************************
6325 * OleLoadFromStream (OLE32.@)
6327 * This function loads an object from stream
6329 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6331 CLSID clsid;
6332 HRESULT res;
6333 LPPERSISTSTREAM xstm;
6335 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6337 res=ReadClassStm(pStm,&clsid);
6338 if (FAILED(res))
6339 return res;
6340 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6341 if (FAILED(res))
6342 return res;
6343 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6344 if (FAILED(res)) {
6345 IUnknown_Release((IUnknown*)*ppvObj);
6346 return res;
6348 res=IPersistStream_Load(xstm,pStm);
6349 IPersistStream_Release(xstm);
6350 /* FIXME: all refcounts ok at this point? I think they should be:
6351 * pStm : unchanged
6352 * ppvObj : 1
6353 * xstm : 0 (released)
6355 return res;
6358 /***********************************************************************
6359 * OleSaveToStream (OLE32.@)
6361 * This function saves an object with the IPersistStream interface on it
6362 * to the specified stream.
6364 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6367 CLSID clsid;
6368 HRESULT res;
6370 TRACE("(%p,%p)\n",pPStm,pStm);
6372 res=IPersistStream_GetClassID(pPStm,&clsid);
6374 if (SUCCEEDED(res)){
6376 res=WriteClassStm(pStm,&clsid);
6378 if (SUCCEEDED(res))
6380 res=IPersistStream_Save(pPStm,pStm,TRUE);
6383 TRACE("Finished Save\n");
6384 return res;
6387 /****************************************************************************
6388 * This method validate a STGM parameter that can contain the values below
6390 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6391 * The stgm values contained in 0xffff0000 are bitmasks.
6393 * STGM_DIRECT 0x00000000
6394 * STGM_TRANSACTED 0x00010000
6395 * STGM_SIMPLE 0x08000000
6397 * STGM_READ 0x00000000
6398 * STGM_WRITE 0x00000001
6399 * STGM_READWRITE 0x00000002
6401 * STGM_SHARE_DENY_NONE 0x00000040
6402 * STGM_SHARE_DENY_READ 0x00000030
6403 * STGM_SHARE_DENY_WRITE 0x00000020
6404 * STGM_SHARE_EXCLUSIVE 0x00000010
6406 * STGM_PRIORITY 0x00040000
6407 * STGM_DELETEONRELEASE 0x04000000
6409 * STGM_CREATE 0x00001000
6410 * STGM_CONVERT 0x00020000
6411 * STGM_FAILIFTHERE 0x00000000
6413 * STGM_NOSCRATCH 0x00100000
6414 * STGM_NOSNAPSHOT 0x00200000
6416 static HRESULT validateSTGM(DWORD stgm)
6418 DWORD access = STGM_ACCESS_MODE(stgm);
6419 DWORD share = STGM_SHARE_MODE(stgm);
6420 DWORD create = STGM_CREATE_MODE(stgm);
6422 if (stgm&~STGM_KNOWN_FLAGS)
6424 ERR("unknown flags %08x\n", stgm);
6425 return E_FAIL;
6428 switch (access)
6430 case STGM_READ:
6431 case STGM_WRITE:
6432 case STGM_READWRITE:
6433 break;
6434 default:
6435 return E_FAIL;
6438 switch (share)
6440 case STGM_SHARE_DENY_NONE:
6441 case STGM_SHARE_DENY_READ:
6442 case STGM_SHARE_DENY_WRITE:
6443 case STGM_SHARE_EXCLUSIVE:
6444 break;
6445 default:
6446 return E_FAIL;
6449 switch (create)
6451 case STGM_CREATE:
6452 case STGM_FAILIFTHERE:
6453 break;
6454 default:
6455 return E_FAIL;
6459 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6461 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6462 return E_FAIL;
6465 * STGM_CREATE | STGM_CONVERT
6466 * if both are false, STGM_FAILIFTHERE is set to TRUE
6468 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6469 return E_FAIL;
6472 * STGM_NOSCRATCH requires STGM_TRANSACTED
6474 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6475 return E_FAIL;
6478 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6479 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6481 if ( (stgm & STGM_NOSNAPSHOT) &&
6482 (!(stgm & STGM_TRANSACTED) ||
6483 share == STGM_SHARE_EXCLUSIVE ||
6484 share == STGM_SHARE_DENY_WRITE) )
6485 return E_FAIL;
6487 return S_OK;
6490 /****************************************************************************
6491 * GetShareModeFromSTGM
6493 * This method will return a share mode flag from a STGM value.
6494 * The STGM value is assumed valid.
6496 static DWORD GetShareModeFromSTGM(DWORD stgm)
6498 switch (STGM_SHARE_MODE(stgm))
6500 case STGM_SHARE_DENY_NONE:
6501 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6502 case STGM_SHARE_DENY_READ:
6503 return FILE_SHARE_WRITE;
6504 case STGM_SHARE_DENY_WRITE:
6505 return FILE_SHARE_READ;
6506 case STGM_SHARE_EXCLUSIVE:
6507 return 0;
6509 ERR("Invalid share mode!\n");
6510 assert(0);
6511 return 0;
6514 /****************************************************************************
6515 * GetAccessModeFromSTGM
6517 * This method will return an access mode flag from a STGM value.
6518 * The STGM value is assumed valid.
6520 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6522 switch (STGM_ACCESS_MODE(stgm))
6524 case STGM_READ:
6525 return GENERIC_READ;
6526 case STGM_WRITE:
6527 case STGM_READWRITE:
6528 return GENERIC_READ | GENERIC_WRITE;
6530 ERR("Invalid access mode!\n");
6531 assert(0);
6532 return 0;
6535 /****************************************************************************
6536 * GetCreationModeFromSTGM
6538 * This method will return a creation mode flag from a STGM value.
6539 * The STGM value is assumed valid.
6541 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6543 switch(STGM_CREATE_MODE(stgm))
6545 case STGM_CREATE:
6546 return CREATE_ALWAYS;
6547 case STGM_CONVERT:
6548 FIXME("STGM_CONVERT not implemented!\n");
6549 return CREATE_NEW;
6550 case STGM_FAILIFTHERE:
6551 return CREATE_NEW;
6553 ERR("Invalid create mode!\n");
6554 assert(0);
6555 return 0;
6559 /*************************************************************************
6560 * OLECONVERT_LoadOLE10 [Internal]
6562 * Loads the OLE10 STREAM to memory
6564 * PARAMS
6565 * pOleStream [I] The OLESTREAM
6566 * pData [I] Data Structure for the OLESTREAM Data
6568 * RETURNS
6569 * Success: S_OK
6570 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6571 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6573 * NOTES
6574 * This function is used by OleConvertOLESTREAMToIStorage only.
6576 * Memory allocated for pData must be freed by the caller
6578 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6580 DWORD dwSize;
6581 HRESULT hRes = S_OK;
6582 int nTryCnt=0;
6583 int max_try = 6;
6585 pData->pData = NULL;
6586 pData->pstrOleObjFileName = NULL;
6588 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6590 /* Get the OleID */
6591 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6592 if(dwSize != sizeof(pData->dwOleID))
6594 hRes = CONVERT10_E_OLESTREAM_GET;
6596 else if(pData->dwOleID != OLESTREAM_ID)
6598 hRes = CONVERT10_E_OLESTREAM_FMT;
6600 else
6602 hRes = S_OK;
6603 break;
6607 if(hRes == S_OK)
6609 /* Get the TypeID... more info needed for this field */
6610 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6611 if(dwSize != sizeof(pData->dwTypeID))
6613 hRes = CONVERT10_E_OLESTREAM_GET;
6616 if(hRes == S_OK)
6618 if(pData->dwTypeID != 0)
6620 /* Get the length of the OleTypeName */
6621 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6622 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6624 hRes = CONVERT10_E_OLESTREAM_GET;
6627 if(hRes == S_OK)
6629 if(pData->dwOleTypeNameLength > 0)
6631 /* Get the OleTypeName */
6632 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6633 if(dwSize != pData->dwOleTypeNameLength)
6635 hRes = CONVERT10_E_OLESTREAM_GET;
6639 if(bStrem1)
6641 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6642 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6644 hRes = CONVERT10_E_OLESTREAM_GET;
6646 if(hRes == S_OK)
6648 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6649 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6650 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6651 if(pData->pstrOleObjFileName)
6653 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6654 if(dwSize != pData->dwOleObjFileNameLength)
6656 hRes = CONVERT10_E_OLESTREAM_GET;
6659 else
6660 hRes = CONVERT10_E_OLESTREAM_GET;
6663 else
6665 /* Get the Width of the Metafile */
6666 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6667 if(dwSize != sizeof(pData->dwMetaFileWidth))
6669 hRes = CONVERT10_E_OLESTREAM_GET;
6671 if(hRes == S_OK)
6673 /* Get the Height of the Metafile */
6674 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6675 if(dwSize != sizeof(pData->dwMetaFileHeight))
6677 hRes = CONVERT10_E_OLESTREAM_GET;
6681 if(hRes == S_OK)
6683 /* Get the Length of the Data */
6684 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6685 if(dwSize != sizeof(pData->dwDataLength))
6687 hRes = CONVERT10_E_OLESTREAM_GET;
6691 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6693 if(!bStrem1) /* if it is a second OLE stream data */
6695 pData->dwDataLength -= 8;
6696 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6697 if(dwSize != sizeof(pData->strUnknown))
6699 hRes = CONVERT10_E_OLESTREAM_GET;
6703 if(hRes == S_OK)
6705 if(pData->dwDataLength > 0)
6707 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6709 /* Get Data (ex. IStorage, Metafile, or BMP) */
6710 if(pData->pData)
6712 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6713 if(dwSize != pData->dwDataLength)
6715 hRes = CONVERT10_E_OLESTREAM_GET;
6718 else
6720 hRes = CONVERT10_E_OLESTREAM_GET;
6726 return hRes;
6729 /*************************************************************************
6730 * OLECONVERT_SaveOLE10 [Internal]
6732 * Saves the OLE10 STREAM From memory
6734 * PARAMS
6735 * pData [I] Data Structure for the OLESTREAM Data
6736 * pOleStream [I] The OLESTREAM to save
6738 * RETURNS
6739 * Success: S_OK
6740 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6742 * NOTES
6743 * This function is used by OleConvertIStorageToOLESTREAM only.
6746 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6748 DWORD dwSize;
6749 HRESULT hRes = S_OK;
6752 /* Set the OleID */
6753 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6754 if(dwSize != sizeof(pData->dwOleID))
6756 hRes = CONVERT10_E_OLESTREAM_PUT;
6759 if(hRes == S_OK)
6761 /* Set the TypeID */
6762 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6763 if(dwSize != sizeof(pData->dwTypeID))
6765 hRes = CONVERT10_E_OLESTREAM_PUT;
6769 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6771 /* Set the Length of the OleTypeName */
6772 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6773 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6775 hRes = CONVERT10_E_OLESTREAM_PUT;
6778 if(hRes == S_OK)
6780 if(pData->dwOleTypeNameLength > 0)
6782 /* Set the OleTypeName */
6783 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6784 if(dwSize != pData->dwOleTypeNameLength)
6786 hRes = CONVERT10_E_OLESTREAM_PUT;
6791 if(hRes == S_OK)
6793 /* Set the width of the Metafile */
6794 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6795 if(dwSize != sizeof(pData->dwMetaFileWidth))
6797 hRes = CONVERT10_E_OLESTREAM_PUT;
6801 if(hRes == S_OK)
6803 /* Set the height of the Metafile */
6804 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6805 if(dwSize != sizeof(pData->dwMetaFileHeight))
6807 hRes = CONVERT10_E_OLESTREAM_PUT;
6811 if(hRes == S_OK)
6813 /* Set the length of the Data */
6814 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6815 if(dwSize != sizeof(pData->dwDataLength))
6817 hRes = CONVERT10_E_OLESTREAM_PUT;
6821 if(hRes == S_OK)
6823 if(pData->dwDataLength > 0)
6825 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6826 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6827 if(dwSize != pData->dwDataLength)
6829 hRes = CONVERT10_E_OLESTREAM_PUT;
6834 return hRes;
6837 /*************************************************************************
6838 * OLECONVERT_GetOLE20FromOLE10[Internal]
6840 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6841 * opens it, and copies the content to the dest IStorage for
6842 * OleConvertOLESTREAMToIStorage
6845 * PARAMS
6846 * pDestStorage [I] The IStorage to copy the data to
6847 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6848 * nBufferLength [I] The size of the buffer
6850 * RETURNS
6851 * Nothing
6853 * NOTES
6857 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6859 HRESULT hRes;
6860 HANDLE hFile;
6861 IStorage *pTempStorage;
6862 DWORD dwNumOfBytesWritten;
6863 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6864 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6866 /* Create a temp File */
6867 GetTempPathW(MAX_PATH, wstrTempDir);
6868 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6869 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6871 if(hFile != INVALID_HANDLE_VALUE)
6873 /* Write IStorage Data to File */
6874 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6875 CloseHandle(hFile);
6877 /* Open and copy temp storage to the Dest Storage */
6878 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6879 if(hRes == S_OK)
6881 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6882 StorageBaseImpl_Release(pTempStorage);
6884 DeleteFileW(wstrTempFile);
6889 /*************************************************************************
6890 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6892 * Saves the OLE10 STREAM From memory
6894 * PARAMS
6895 * pStorage [I] The Src IStorage to copy
6896 * pData [I] The Dest Memory to write to.
6898 * RETURNS
6899 * The size in bytes allocated for pData
6901 * NOTES
6902 * Memory allocated for pData must be freed by the caller
6904 * Used by OleConvertIStorageToOLESTREAM only.
6907 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6909 HANDLE hFile;
6910 HRESULT hRes;
6911 DWORD nDataLength = 0;
6912 IStorage *pTempStorage;
6913 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6914 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6916 *pData = NULL;
6918 /* Create temp Storage */
6919 GetTempPathW(MAX_PATH, wstrTempDir);
6920 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6921 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6923 if(hRes == S_OK)
6925 /* Copy Src Storage to the Temp Storage */
6926 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6927 StorageBaseImpl_Release(pTempStorage);
6929 /* Open Temp Storage as a file and copy to memory */
6930 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6931 if(hFile != INVALID_HANDLE_VALUE)
6933 nDataLength = GetFileSize(hFile, NULL);
6934 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6935 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6936 CloseHandle(hFile);
6938 DeleteFileW(wstrTempFile);
6940 return nDataLength;
6943 /*************************************************************************
6944 * OLECONVERT_CreateOleStream [Internal]
6946 * Creates the "\001OLE" stream in the IStorage if necessary.
6948 * PARAMS
6949 * pStorage [I] Dest storage to create the stream in
6951 * RETURNS
6952 * Nothing
6954 * NOTES
6955 * This function is used by OleConvertOLESTREAMToIStorage only.
6957 * This stream is still unknown, MS Word seems to have extra data
6958 * but since the data is stored in the OLESTREAM there should be
6959 * no need to recreate the stream. If the stream is manually
6960 * deleted it will create it with this default data.
6963 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6965 HRESULT hRes;
6966 IStream *pStream;
6967 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6968 BYTE pOleStreamHeader [] =
6970 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6971 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6972 0x00, 0x00, 0x00, 0x00
6975 /* Create stream if not present */
6976 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6977 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6979 if(hRes == S_OK)
6981 /* Write default Data */
6982 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6983 IStream_Release(pStream);
6987 /* write a string to a stream, preceded by its length */
6988 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6990 HRESULT r;
6991 LPSTR str;
6992 DWORD len = 0;
6994 if( string )
6995 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6996 r = IStream_Write( stm, &len, sizeof(len), NULL);
6997 if( FAILED( r ) )
6998 return r;
6999 if(len == 0)
7000 return r;
7001 str = CoTaskMemAlloc( len );
7002 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7003 r = IStream_Write( stm, str, len, NULL);
7004 CoTaskMemFree( str );
7005 return r;
7008 /* read a string preceded by its length from a stream */
7009 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7011 HRESULT r;
7012 DWORD len, count = 0;
7013 LPSTR str;
7014 LPWSTR wstr;
7016 r = IStream_Read( stm, &len, sizeof(len), &count );
7017 if( FAILED( r ) )
7018 return r;
7019 if( count != sizeof(len) )
7020 return E_OUTOFMEMORY;
7022 TRACE("%d bytes\n",len);
7024 str = CoTaskMemAlloc( len );
7025 if( !str )
7026 return E_OUTOFMEMORY;
7027 count = 0;
7028 r = IStream_Read( stm, str, len, &count );
7029 if( FAILED( r ) )
7030 return r;
7031 if( count != len )
7033 CoTaskMemFree( str );
7034 return E_OUTOFMEMORY;
7037 TRACE("Read string %s\n",debugstr_an(str,len));
7039 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7040 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7041 if( wstr )
7042 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7043 CoTaskMemFree( str );
7045 *string = wstr;
7047 return r;
7051 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7052 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7054 IStream *pstm;
7055 HRESULT r = S_OK;
7056 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7058 static const BYTE unknown1[12] =
7059 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7060 0xFF, 0xFF, 0xFF, 0xFF};
7061 static const BYTE unknown2[16] =
7062 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7063 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7065 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7066 debugstr_w(lpszUserType), debugstr_w(szClipName),
7067 debugstr_w(szProgIDName));
7069 /* Create a CompObj stream if it doesn't exist */
7070 r = IStorage_CreateStream(pstg, szwStreamName,
7071 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7072 if( FAILED (r) )
7073 return r;
7075 /* Write CompObj Structure to stream */
7076 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7078 if( SUCCEEDED( r ) )
7079 r = WriteClassStm( pstm, clsid );
7081 if( SUCCEEDED( r ) )
7082 r = STREAM_WriteString( pstm, lpszUserType );
7083 if( SUCCEEDED( r ) )
7084 r = STREAM_WriteString( pstm, szClipName );
7085 if( SUCCEEDED( r ) )
7086 r = STREAM_WriteString( pstm, szProgIDName );
7087 if( SUCCEEDED( r ) )
7088 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7090 IStream_Release( pstm );
7092 return r;
7095 /***********************************************************************
7096 * WriteFmtUserTypeStg (OLE32.@)
7098 HRESULT WINAPI WriteFmtUserTypeStg(
7099 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7101 HRESULT r;
7102 WCHAR szwClipName[0x40];
7103 CLSID clsid = CLSID_NULL;
7104 LPWSTR wstrProgID = NULL;
7105 DWORD n;
7107 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7109 /* get the clipboard format name */
7110 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7111 szwClipName[n]=0;
7113 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7115 /* FIXME: There's room to save a CLSID and its ProgID, but
7116 the CLSID is not looked up in the registry and in all the
7117 tests I wrote it was CLSID_NULL. Where does it come from?
7120 /* get the real program ID. This may fail, but that's fine */
7121 ProgIDFromCLSID(&clsid, &wstrProgID);
7123 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7125 r = STORAGE_WriteCompObj( pstg, &clsid,
7126 lpszUserType, szwClipName, wstrProgID );
7128 CoTaskMemFree(wstrProgID);
7130 return r;
7134 /******************************************************************************
7135 * ReadFmtUserTypeStg [OLE32.@]
7137 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7139 HRESULT r;
7140 IStream *stm = 0;
7141 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7142 unsigned char unknown1[12];
7143 unsigned char unknown2[16];
7144 DWORD count;
7145 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7146 CLSID clsid;
7148 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7150 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7151 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7152 if( FAILED ( r ) )
7154 WARN("Failed to open stream r = %08x\n", r);
7155 return r;
7158 /* read the various parts of the structure */
7159 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7160 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7161 goto end;
7162 r = ReadClassStm( stm, &clsid );
7163 if( FAILED( r ) )
7164 goto end;
7166 r = STREAM_ReadString( stm, &szCLSIDName );
7167 if( FAILED( r ) )
7168 goto end;
7170 r = STREAM_ReadString( stm, &szOleTypeName );
7171 if( FAILED( r ) )
7172 goto end;
7174 r = STREAM_ReadString( stm, &szProgIDName );
7175 if( FAILED( r ) )
7176 goto end;
7178 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7179 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7180 goto end;
7182 /* ok, success... now we just need to store what we found */
7183 if( pcf )
7184 *pcf = RegisterClipboardFormatW( szOleTypeName );
7185 CoTaskMemFree( szOleTypeName );
7187 if( lplpszUserType )
7188 *lplpszUserType = szCLSIDName;
7189 CoTaskMemFree( szProgIDName );
7191 end:
7192 IStream_Release( stm );
7194 return r;
7198 /*************************************************************************
7199 * OLECONVERT_CreateCompObjStream [Internal]
7201 * Creates a "\001CompObj" is the destination IStorage if necessary.
7203 * PARAMS
7204 * pStorage [I] The dest IStorage to create the CompObj Stream
7205 * if necessary.
7206 * strOleTypeName [I] The ProgID
7208 * RETURNS
7209 * Success: S_OK
7210 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7212 * NOTES
7213 * This function is used by OleConvertOLESTREAMToIStorage only.
7215 * The stream data is stored in the OLESTREAM and there should be
7216 * no need to recreate the stream. If the stream is manually
7217 * deleted it will attempt to create it by querying the registry.
7221 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7223 IStream *pStream;
7224 HRESULT hStorageRes, hRes = S_OK;
7225 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7226 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7227 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7229 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7230 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7232 /* Initialize the CompObj structure */
7233 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7234 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7235 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7238 /* Create a CompObj stream if it doesn't exist */
7239 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7240 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7241 if(hStorageRes == S_OK)
7243 /* copy the OleTypeName to the compobj struct */
7244 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7245 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7247 /* copy the OleTypeName to the compobj struct */
7248 /* Note: in the test made, these were Identical */
7249 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7250 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7252 /* Get the CLSID */
7253 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7254 bufferW, OLESTREAM_MAX_STR_LEN );
7255 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7257 if(hRes == S_OK)
7259 HKEY hKey;
7260 LONG hErr;
7261 /* Get the CLSID Default Name from the Registry */
7262 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7263 if(hErr == ERROR_SUCCESS)
7265 char strTemp[OLESTREAM_MAX_STR_LEN];
7266 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7267 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7268 if(hErr == ERROR_SUCCESS)
7270 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7272 RegCloseKey(hKey);
7276 /* Write CompObj Structure to stream */
7277 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7279 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7281 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7282 if(IStorageCompObj.dwCLSIDNameLength > 0)
7284 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7286 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7287 if(IStorageCompObj.dwOleTypeNameLength > 0)
7289 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7291 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7292 if(IStorageCompObj.dwProgIDNameLength > 0)
7294 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7296 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7297 IStream_Release(pStream);
7299 return hRes;
7303 /*************************************************************************
7304 * OLECONVERT_CreateOlePresStream[Internal]
7306 * Creates the "\002OlePres000" Stream with the Metafile data
7308 * PARAMS
7309 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7310 * dwExtentX [I] Width of the Metafile
7311 * dwExtentY [I] Height of the Metafile
7312 * pData [I] Metafile data
7313 * dwDataLength [I] Size of the Metafile data
7315 * RETURNS
7316 * Success: S_OK
7317 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7319 * NOTES
7320 * This function is used by OleConvertOLESTREAMToIStorage only.
7323 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7325 HRESULT hRes;
7326 IStream *pStream;
7327 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7328 BYTE pOlePresStreamHeader [] =
7330 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7331 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7332 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7333 0x00, 0x00, 0x00, 0x00
7336 BYTE pOlePresStreamHeaderEmpty [] =
7338 0x00, 0x00, 0x00, 0x00,
7339 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7340 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7341 0x00, 0x00, 0x00, 0x00
7344 /* Create the OlePres000 Stream */
7345 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7346 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7348 if(hRes == S_OK)
7350 DWORD nHeaderSize;
7351 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7353 memset(&OlePres, 0, sizeof(OlePres));
7354 /* Do we have any metafile data to save */
7355 if(dwDataLength > 0)
7357 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7358 nHeaderSize = sizeof(pOlePresStreamHeader);
7360 else
7362 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7363 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7365 /* Set width and height of the metafile */
7366 OlePres.dwExtentX = dwExtentX;
7367 OlePres.dwExtentY = -dwExtentY;
7369 /* Set Data and Length */
7370 if(dwDataLength > sizeof(METAFILEPICT16))
7372 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7373 OlePres.pData = &(pData[8]);
7375 /* Save OlePres000 Data to Stream */
7376 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7377 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7378 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7379 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7380 if(OlePres.dwSize > 0)
7382 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7384 IStream_Release(pStream);
7388 /*************************************************************************
7389 * OLECONVERT_CreateOle10NativeStream [Internal]
7391 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7393 * PARAMS
7394 * pStorage [I] Dest storage to create the stream in
7395 * pData [I] Ole10 Native Data (ex. bmp)
7396 * dwDataLength [I] Size of the Ole10 Native Data
7398 * RETURNS
7399 * Nothing
7401 * NOTES
7402 * This function is used by OleConvertOLESTREAMToIStorage only.
7404 * Might need to verify the data and return appropriate error message
7407 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7409 HRESULT hRes;
7410 IStream *pStream;
7411 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7413 /* Create the Ole10Native Stream */
7414 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7415 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7417 if(hRes == S_OK)
7419 /* Write info to stream */
7420 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7421 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7422 IStream_Release(pStream);
7427 /*************************************************************************
7428 * OLECONVERT_GetOLE10ProgID [Internal]
7430 * Finds the ProgID (or OleTypeID) from the IStorage
7432 * PARAMS
7433 * pStorage [I] The Src IStorage to get the ProgID
7434 * strProgID [I] the ProgID string to get
7435 * dwSize [I] the size of the string
7437 * RETURNS
7438 * Success: S_OK
7439 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7441 * NOTES
7442 * This function is used by OleConvertIStorageToOLESTREAM only.
7446 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7448 HRESULT hRes;
7449 IStream *pStream;
7450 LARGE_INTEGER iSeekPos;
7451 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7452 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7454 /* Open the CompObj Stream */
7455 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7456 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7457 if(hRes == S_OK)
7460 /*Get the OleType from the CompObj Stream */
7461 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7462 iSeekPos.u.HighPart = 0;
7464 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7465 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7466 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7467 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7468 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7469 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7470 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7472 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7473 if(*dwSize > 0)
7475 IStream_Read(pStream, strProgID, *dwSize, NULL);
7477 IStream_Release(pStream);
7479 else
7481 STATSTG stat;
7482 LPOLESTR wstrProgID;
7484 /* Get the OleType from the registry */
7485 REFCLSID clsid = &(stat.clsid);
7486 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7487 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7488 if(hRes == S_OK)
7490 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7494 return hRes;
7497 /*************************************************************************
7498 * OLECONVERT_GetOle10PresData [Internal]
7500 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7502 * PARAMS
7503 * pStorage [I] Src IStroage
7504 * pOleStream [I] Dest OleStream Mem Struct
7506 * RETURNS
7507 * Nothing
7509 * NOTES
7510 * This function is used by OleConvertIStorageToOLESTREAM only.
7512 * Memory allocated for pData must be freed by the caller
7516 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7519 HRESULT hRes;
7520 IStream *pStream;
7521 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7523 /* Initialize Default data for OLESTREAM */
7524 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7525 pOleStreamData[0].dwTypeID = 2;
7526 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7527 pOleStreamData[1].dwTypeID = 0;
7528 pOleStreamData[0].dwMetaFileWidth = 0;
7529 pOleStreamData[0].dwMetaFileHeight = 0;
7530 pOleStreamData[0].pData = NULL;
7531 pOleStreamData[1].pData = NULL;
7533 /* Open Ole10Native Stream */
7534 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7535 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7536 if(hRes == S_OK)
7539 /* Read Size and Data */
7540 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7541 if(pOleStreamData->dwDataLength > 0)
7543 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7544 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7546 IStream_Release(pStream);
7552 /*************************************************************************
7553 * OLECONVERT_GetOle20PresData[Internal]
7555 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7557 * PARAMS
7558 * pStorage [I] Src IStroage
7559 * pOleStreamData [I] Dest OleStream Mem Struct
7561 * RETURNS
7562 * Nothing
7564 * NOTES
7565 * This function is used by OleConvertIStorageToOLESTREAM only.
7567 * Memory allocated for pData must be freed by the caller
7569 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7571 HRESULT hRes;
7572 IStream *pStream;
7573 OLECONVERT_ISTORAGE_OLEPRES olePress;
7574 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7576 /* Initialize Default data for OLESTREAM */
7577 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7578 pOleStreamData[0].dwTypeID = 2;
7579 pOleStreamData[0].dwMetaFileWidth = 0;
7580 pOleStreamData[0].dwMetaFileHeight = 0;
7581 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7582 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7583 pOleStreamData[1].dwTypeID = 0;
7584 pOleStreamData[1].dwOleTypeNameLength = 0;
7585 pOleStreamData[1].strOleTypeName[0] = 0;
7586 pOleStreamData[1].dwMetaFileWidth = 0;
7587 pOleStreamData[1].dwMetaFileHeight = 0;
7588 pOleStreamData[1].pData = NULL;
7589 pOleStreamData[1].dwDataLength = 0;
7592 /* Open OlePress000 stream */
7593 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7594 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7595 if(hRes == S_OK)
7597 LARGE_INTEGER iSeekPos;
7598 METAFILEPICT16 MetaFilePict;
7599 static const char strMetafilePictName[] = "METAFILEPICT";
7601 /* Set the TypeID for a Metafile */
7602 pOleStreamData[1].dwTypeID = 5;
7604 /* Set the OleTypeName to Metafile */
7605 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7606 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7608 iSeekPos.u.HighPart = 0;
7609 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7611 /* Get Presentation Data */
7612 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7613 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7614 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7615 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7617 /*Set width and Height */
7618 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7619 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7620 if(olePress.dwSize > 0)
7622 /* Set Length */
7623 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7625 /* Set MetaFilePict struct */
7626 MetaFilePict.mm = 8;
7627 MetaFilePict.xExt = olePress.dwExtentX;
7628 MetaFilePict.yExt = olePress.dwExtentY;
7629 MetaFilePict.hMF = 0;
7631 /* Get Metafile Data */
7632 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7633 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7634 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7636 IStream_Release(pStream);
7640 /*************************************************************************
7641 * OleConvertOLESTREAMToIStorage [OLE32.@]
7643 * Read info on MSDN
7645 * TODO
7646 * DVTARGETDEVICE parameter is not handled
7647 * Still unsure of some mem fields for OLE 10 Stream
7648 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7649 * and "\001OLE" streams
7652 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7653 LPOLESTREAM pOleStream,
7654 LPSTORAGE pstg,
7655 const DVTARGETDEVICE* ptd)
7657 int i;
7658 HRESULT hRes=S_OK;
7659 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7661 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7663 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7665 if(ptd != NULL)
7667 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7670 if(pstg == NULL || pOleStream == NULL)
7672 hRes = E_INVALIDARG;
7675 if(hRes == S_OK)
7677 /* Load the OLESTREAM to Memory */
7678 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7681 if(hRes == S_OK)
7683 /* Load the OLESTREAM to Memory (part 2)*/
7684 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7687 if(hRes == S_OK)
7690 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7692 /* Do we have the IStorage Data in the OLESTREAM */
7693 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7695 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7696 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7698 else
7700 /* It must be an original OLE 1.0 source */
7701 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7704 else
7706 /* It must be an original OLE 1.0 source */
7707 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7710 /* Create CompObj Stream if necessary */
7711 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7712 if(hRes == S_OK)
7714 /*Create the Ole Stream if necessary */
7715 OLECONVERT_CreateOleStream(pstg);
7720 /* Free allocated memory */
7721 for(i=0; i < 2; i++)
7723 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7724 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7725 pOleStreamData[i].pstrOleObjFileName = NULL;
7727 return hRes;
7730 /*************************************************************************
7731 * OleConvertIStorageToOLESTREAM [OLE32.@]
7733 * Read info on MSDN
7735 * Read info on MSDN
7737 * TODO
7738 * Still unsure of some mem fields for OLE 10 Stream
7739 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7740 * and "\001OLE" streams.
7743 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7744 LPSTORAGE pstg,
7745 LPOLESTREAM pOleStream)
7747 int i;
7748 HRESULT hRes = S_OK;
7749 IStream *pStream;
7750 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7751 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7753 TRACE("%p %p\n", pstg, pOleStream);
7755 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7757 if(pstg == NULL || pOleStream == NULL)
7759 hRes = E_INVALIDARG;
7761 if(hRes == S_OK)
7763 /* Get the ProgID */
7764 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7765 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7767 if(hRes == S_OK)
7769 /* Was it originally Ole10 */
7770 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7771 if(hRes == S_OK)
7773 IStream_Release(pStream);
7774 /* Get Presentation Data for Ole10Native */
7775 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7777 else
7779 /* Get Presentation Data (OLE20) */
7780 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7783 /* Save OLESTREAM */
7784 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7785 if(hRes == S_OK)
7787 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7792 /* Free allocated memory */
7793 for(i=0; i < 2; i++)
7795 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7798 return hRes;
7801 /***********************************************************************
7802 * GetConvertStg (OLE32.@)
7804 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7805 FIXME("unimplemented stub!\n");
7806 return E_FAIL;
7809 /******************************************************************************
7810 * StgIsStorageFile [OLE32.@]
7811 * Verify if the file contains a storage object
7813 * PARAMS
7814 * fn [ I] Filename
7816 * RETURNS
7817 * S_OK if file has magic bytes as a storage object
7818 * S_FALSE if file is not storage
7820 HRESULT WINAPI
7821 StgIsStorageFile(LPCOLESTR fn)
7823 HANDLE hf;
7824 BYTE magic[8];
7825 DWORD bytes_read;
7827 TRACE("%s\n", debugstr_w(fn));
7828 hf = CreateFileW(fn, GENERIC_READ,
7829 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7830 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7832 if (hf == INVALID_HANDLE_VALUE)
7833 return STG_E_FILENOTFOUND;
7835 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7837 WARN(" unable to read file\n");
7838 CloseHandle(hf);
7839 return S_FALSE;
7842 CloseHandle(hf);
7844 if (bytes_read != 8) {
7845 WARN(" too short\n");
7846 return S_FALSE;
7849 if (!memcmp(magic,STORAGE_magic,8)) {
7850 WARN(" -> YES\n");
7851 return S_OK;
7854 WARN(" -> Invalid header.\n");
7855 return S_FALSE;
7858 /***********************************************************************
7859 * WriteClassStm (OLE32.@)
7861 * Writes a CLSID to a stream.
7863 * PARAMS
7864 * pStm [I] Stream to write to.
7865 * rclsid [I] CLSID to write.
7867 * RETURNS
7868 * Success: S_OK.
7869 * Failure: HRESULT code.
7871 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7873 TRACE("(%p,%p)\n",pStm,rclsid);
7875 if (!pStm || !rclsid)
7876 return E_INVALIDARG;
7878 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7881 /***********************************************************************
7882 * ReadClassStm (OLE32.@)
7884 * Reads a CLSID from a stream.
7886 * PARAMS
7887 * pStm [I] Stream to read from.
7888 * rclsid [O] CLSID to read.
7890 * RETURNS
7891 * Success: S_OK.
7892 * Failure: HRESULT code.
7894 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7896 ULONG nbByte;
7897 HRESULT res;
7899 TRACE("(%p,%p)\n",pStm,pclsid);
7901 if (!pStm || !pclsid)
7902 return E_INVALIDARG;
7904 /* clear the output args */
7905 *pclsid = CLSID_NULL;
7907 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7909 if (FAILED(res))
7910 return res;
7912 if (nbByte != sizeof(CLSID))
7913 return STG_E_READFAULT;
7914 else
7915 return S_OK;