rpcrt4/tests: Fix a few more broken tests on NT4.
[wine.git] / dlls / ole32 / storage32.c
blob9266bb74c8dc66cf012c04d92f14b2db1c05098f
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;
996 * Initialize the out parameter
998 *ppstm = 0;
1001 * Create a property enumeration to search the properties
1003 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1004 This->rootPropertySetIndex);
1006 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1007 pwcsName,
1008 &currentProperty);
1010 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1012 if (foundPropertyIndex != PROPERTY_NULL)
1015 * An element with this name already exists
1017 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1019 StgStreamImpl *strm;
1021 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
1023 if (strm->ownerProperty == foundPropertyIndex)
1025 TRACE("Stream deleted %p\n", strm);
1026 strm->parentStorage = NULL;
1027 list_remove(&strm->StrmListEntry);
1030 IStorage_DestroyElement(iface, pwcsName);
1032 else
1033 return STG_E_FILEALREADYEXISTS;
1035 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1037 WARN("read-only storage\n");
1038 return STG_E_ACCESSDENIED;
1042 * memset the empty property
1044 memset(&newStreamProperty, 0, sizeof(StgProperty));
1046 newStreamProperty.sizeOfNameString =
1047 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1049 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1050 return STG_E_INVALIDNAME;
1052 strcpyW(newStreamProperty.name, pwcsName);
1054 newStreamProperty.propertyType = PROPTYPE_STREAM;
1055 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1056 newStreamProperty.size.u.LowPart = 0;
1057 newStreamProperty.size.u.HighPart = 0;
1059 newStreamProperty.previousProperty = PROPERTY_NULL;
1060 newStreamProperty.nextProperty = PROPERTY_NULL;
1061 newStreamProperty.dirProperty = PROPERTY_NULL;
1063 /* call CoFileTime to get the current time
1064 newStreamProperty.timeStampS1
1065 newStreamProperty.timeStampD1
1066 newStreamProperty.timeStampS2
1067 newStreamProperty.timeStampD2
1070 /* newStreamProperty.propertyUniqueID */
1073 * Get a free property or create a new one
1075 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1078 * Save the new property into the new property spot
1080 StorageImpl_WriteProperty(
1081 This->ancestorStorage,
1082 newPropertyIndex,
1083 &newStreamProperty);
1086 * Find a spot in the property chain for our newly created property.
1088 updatePropertyChain(
1089 (StorageImpl*)This,
1090 newPropertyIndex,
1091 newStreamProperty);
1094 * Open the stream to return it.
1096 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1098 if (newStream != 0)
1100 *ppstm = (IStream*)newStream;
1103 * Since we are returning a pointer to the interface, we have to nail down
1104 * the reference.
1106 IStream_AddRef(*ppstm);
1108 else
1110 return STG_E_INSUFFICIENTMEMORY;
1113 return S_OK;
1116 /************************************************************************
1117 * Storage32BaseImpl_SetClass (IStorage)
1119 * This method will write the specified CLSID in the property of this
1120 * storage.
1122 * See Windows documentation for more details on IStorage methods.
1124 static HRESULT WINAPI StorageBaseImpl_SetClass(
1125 IStorage* iface,
1126 REFCLSID clsid) /* [in] */
1128 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1129 HRESULT hRes = E_FAIL;
1130 StgProperty curProperty;
1131 BOOL success;
1133 TRACE("(%p, %p)\n", iface, clsid);
1135 success = StorageImpl_ReadProperty(This->ancestorStorage,
1136 This->rootPropertySetIndex,
1137 &curProperty);
1138 if (success)
1140 curProperty.propertyUniqueID = *clsid;
1142 success = StorageImpl_WriteProperty(This->ancestorStorage,
1143 This->rootPropertySetIndex,
1144 &curProperty);
1145 if (success)
1146 hRes = S_OK;
1149 return hRes;
1152 /************************************************************************
1153 ** Storage32Impl implementation
1156 /************************************************************************
1157 * Storage32Impl_CreateStorage (IStorage)
1159 * This method will create the storage object within the provided storage.
1161 * See Windows documentation for more details on IStorage methods.
1163 static HRESULT WINAPI StorageImpl_CreateStorage(
1164 IStorage* iface,
1165 const OLECHAR *pwcsName, /* [string][in] */
1166 DWORD grfMode, /* [in] */
1167 DWORD reserved1, /* [in] */
1168 DWORD reserved2, /* [in] */
1169 IStorage **ppstg) /* [out] */
1171 StorageImpl* const This=(StorageImpl*)iface;
1173 IEnumSTATSTGImpl *propertyEnumeration;
1174 StgProperty currentProperty;
1175 StgProperty newProperty;
1176 ULONG foundPropertyIndex;
1177 ULONG newPropertyIndex;
1178 HRESULT hr;
1180 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1181 iface, debugstr_w(pwcsName), grfMode,
1182 reserved1, reserved2, ppstg);
1185 * Validate parameters
1187 if (ppstg == 0)
1188 return STG_E_INVALIDPOINTER;
1190 if (pwcsName == 0)
1191 return STG_E_INVALIDNAME;
1194 * Initialize the out parameter
1196 *ppstg = NULL;
1199 * Validate the STGM flags
1201 if ( FAILED( validateSTGM(grfMode) ) ||
1202 (grfMode & STGM_DELETEONRELEASE) )
1204 WARN("bad grfMode: 0x%x\n", grfMode);
1205 return STG_E_INVALIDFLAG;
1209 * Check that we're compatible with the parent's storage mode
1211 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1213 WARN("access denied\n");
1214 return STG_E_ACCESSDENIED;
1218 * Create a property enumeration and search the properties
1220 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1221 This->base.rootPropertySetIndex);
1223 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1224 pwcsName,
1225 &currentProperty);
1226 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1228 if (foundPropertyIndex != PROPERTY_NULL)
1231 * An element with this name already exists
1233 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1234 STGM_ACCESS_MODE(This->base.openFlags) != STGM_READ)
1236 hr = IStorage_DestroyElement(iface, pwcsName);
1237 if (FAILED(hr))
1238 return hr;
1240 else
1242 WARN("file already exists\n");
1243 return STG_E_FILEALREADYEXISTS;
1246 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1248 WARN("read-only storage\n");
1249 return STG_E_ACCESSDENIED;
1253 * memset the empty property
1255 memset(&newProperty, 0, sizeof(StgProperty));
1257 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1259 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1261 FIXME("name too long\n");
1262 return STG_E_INVALIDNAME;
1265 strcpyW(newProperty.name, pwcsName);
1267 newProperty.propertyType = PROPTYPE_STORAGE;
1268 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1269 newProperty.size.u.LowPart = 0;
1270 newProperty.size.u.HighPart = 0;
1272 newProperty.previousProperty = PROPERTY_NULL;
1273 newProperty.nextProperty = PROPERTY_NULL;
1274 newProperty.dirProperty = PROPERTY_NULL;
1276 /* call CoFileTime to get the current time
1277 newProperty.timeStampS1
1278 newProperty.timeStampD1
1279 newProperty.timeStampS2
1280 newProperty.timeStampD2
1283 /* newStorageProperty.propertyUniqueID */
1286 * Obtain a free property in the property chain
1288 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1291 * Save the new property into the new property spot
1293 StorageImpl_WriteProperty(
1294 This->base.ancestorStorage,
1295 newPropertyIndex,
1296 &newProperty);
1299 * Find a spot in the property chain for our newly created property.
1301 updatePropertyChain(
1302 This,
1303 newPropertyIndex,
1304 newProperty);
1307 * Open it to get a pointer to return.
1309 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1311 if( (hr != S_OK) || (*ppstg == NULL))
1313 return hr;
1317 return S_OK;
1321 /***************************************************************************
1323 * Internal Method
1325 * Get a free property or create a new one.
1327 static ULONG getFreeProperty(
1328 StorageImpl *storage)
1330 ULONG currentPropertyIndex = 0;
1331 ULONG newPropertyIndex = PROPERTY_NULL;
1332 BOOL readSuccessful = TRUE;
1333 StgProperty currentProperty;
1338 * Start by reading the root property
1340 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1341 currentPropertyIndex,
1342 &currentProperty);
1343 if (readSuccessful)
1345 if (currentProperty.sizeOfNameString == 0)
1348 * The property existis and is available, we found it.
1350 newPropertyIndex = currentPropertyIndex;
1353 else
1356 * We exhausted the property list, we will create more space below
1358 newPropertyIndex = currentPropertyIndex;
1360 currentPropertyIndex++;
1362 } while (newPropertyIndex == PROPERTY_NULL);
1365 * grow the property chain
1367 if (! readSuccessful)
1369 StgProperty emptyProperty;
1370 ULARGE_INTEGER newSize;
1371 ULONG propertyIndex;
1372 ULONG lastProperty = 0;
1373 ULONG blockCount = 0;
1376 * obtain the new count of property blocks
1378 blockCount = BlockChainStream_GetCount(
1379 storage->base.ancestorStorage->rootBlockChain)+1;
1382 * initialize the size used by the property stream
1384 newSize.u.HighPart = 0;
1385 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1388 * add a property block to the property chain
1390 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1393 * memset the empty property in order to initialize the unused newly
1394 * created property
1396 memset(&emptyProperty, 0, sizeof(StgProperty));
1399 * initialize them
1401 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1403 for(
1404 propertyIndex = newPropertyIndex;
1405 propertyIndex < lastProperty;
1406 propertyIndex++)
1408 StorageImpl_WriteProperty(
1409 storage->base.ancestorStorage,
1410 propertyIndex,
1411 &emptyProperty);
1415 return newPropertyIndex;
1418 /****************************************************************************
1420 * Internal Method
1422 * Case insensitive comparison of StgProperty.name by first considering
1423 * their size.
1425 * Returns <0 when newProperty < currentProperty
1426 * >0 when newProperty > currentProperty
1427 * 0 when newProperty == currentProperty
1429 static LONG propertyNameCmp(
1430 const OLECHAR *newProperty,
1431 const OLECHAR *currentProperty)
1433 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1435 if (diff == 0)
1438 * We compare the string themselves only when they are of the same length
1440 diff = lstrcmpiW( newProperty, currentProperty);
1443 return diff;
1446 /****************************************************************************
1448 * Internal Method
1450 * Properly link this new element in the property chain.
1452 static void updatePropertyChain(
1453 StorageImpl *storage,
1454 ULONG newPropertyIndex,
1455 StgProperty newProperty)
1457 StgProperty currentProperty;
1460 * Read the root property
1462 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1463 storage->base.rootPropertySetIndex,
1464 &currentProperty);
1466 if (currentProperty.dirProperty != PROPERTY_NULL)
1469 * The root storage contains some element, therefore, start the research
1470 * for the appropriate location.
1472 BOOL found = 0;
1473 ULONG current, next, previous, currentPropertyId;
1476 * Keep the StgProperty sequence number of the storage first property
1478 currentPropertyId = currentProperty.dirProperty;
1481 * Read
1483 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1484 currentProperty.dirProperty,
1485 &currentProperty);
1487 previous = currentProperty.previousProperty;
1488 next = currentProperty.nextProperty;
1489 current = currentPropertyId;
1491 while (found == 0)
1493 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1495 if (diff < 0)
1497 if (previous != PROPERTY_NULL)
1499 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1500 previous,
1501 &currentProperty);
1502 current = previous;
1504 else
1506 currentProperty.previousProperty = newPropertyIndex;
1507 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1508 current,
1509 &currentProperty);
1510 found = 1;
1513 else if (diff > 0)
1515 if (next != PROPERTY_NULL)
1517 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1518 next,
1519 &currentProperty);
1520 current = next;
1522 else
1524 currentProperty.nextProperty = newPropertyIndex;
1525 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1526 current,
1527 &currentProperty);
1528 found = 1;
1531 else
1534 * Trying to insert an item with the same name in the
1535 * subtree structure.
1537 assert(FALSE);
1540 previous = currentProperty.previousProperty;
1541 next = currentProperty.nextProperty;
1544 else
1547 * The root storage is empty, link the new property to its dir property
1549 currentProperty.dirProperty = newPropertyIndex;
1550 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1551 storage->base.rootPropertySetIndex,
1552 &currentProperty);
1557 /*************************************************************************
1558 * CopyTo (IStorage)
1560 static HRESULT WINAPI StorageImpl_CopyTo(
1561 IStorage* iface,
1562 DWORD ciidExclude, /* [in] */
1563 const IID* rgiidExclude, /* [size_is][unique][in] */
1564 SNB snbExclude, /* [unique][in] */
1565 IStorage* pstgDest) /* [unique][in] */
1567 IEnumSTATSTG *elements = 0;
1568 STATSTG curElement, strStat;
1569 HRESULT hr;
1570 IStorage *pstgTmp, *pstgChild;
1571 IStream *pstrTmp, *pstrChild;
1573 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1574 FIXME("Exclude option not implemented\n");
1576 TRACE("(%p, %d, %p, %p, %p)\n",
1577 iface, ciidExclude, rgiidExclude,
1578 snbExclude, pstgDest);
1581 * Perform a sanity check
1583 if ( pstgDest == 0 )
1584 return STG_E_INVALIDPOINTER;
1587 * Enumerate the elements
1589 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1591 if ( hr != S_OK )
1592 return hr;
1595 * set the class ID
1597 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1598 IStorage_SetClass( pstgDest, &curElement.clsid );
1603 * Obtain the next element
1605 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1607 if ( hr == S_FALSE )
1609 hr = S_OK; /* done, every element has been copied */
1610 break;
1613 if (curElement.type == STGTY_STORAGE)
1616 * open child source storage
1618 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1619 STGM_READ|STGM_SHARE_EXCLUSIVE,
1620 NULL, 0, &pstgChild );
1622 if (hr != S_OK)
1623 break;
1626 * Check if destination storage is not a child of the source
1627 * storage, which will cause an infinite loop
1629 if (pstgChild == pstgDest)
1631 IEnumSTATSTG_Release(elements);
1633 return STG_E_ACCESSDENIED;
1637 * create a new storage in destination storage
1639 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1640 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1641 0, 0,
1642 &pstgTmp );
1644 * if it already exist, don't create a new one use this one
1646 if (hr == STG_E_FILEALREADYEXISTS)
1648 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1649 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1650 NULL, 0, &pstgTmp );
1653 if (hr != S_OK)
1654 break;
1658 * do the copy recursively
1660 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1661 snbExclude, pstgTmp );
1663 IStorage_Release( pstgTmp );
1664 IStorage_Release( pstgChild );
1666 else if (curElement.type == STGTY_STREAM)
1669 * create a new stream in destination storage. If the stream already
1670 * exist, it will be deleted and a new one will be created.
1672 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1673 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1674 0, 0, &pstrTmp );
1676 if (hr != S_OK)
1677 break;
1680 * open child stream storage
1682 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1683 STGM_READ|STGM_SHARE_EXCLUSIVE,
1684 0, &pstrChild );
1686 if (hr != S_OK)
1687 break;
1690 * Get the size of the source stream
1692 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1695 * Set the size of the destination stream.
1697 IStream_SetSize(pstrTmp, strStat.cbSize);
1700 * do the copy
1702 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1703 NULL, NULL );
1705 IStream_Release( pstrTmp );
1706 IStream_Release( pstrChild );
1708 else
1710 WARN("unknown element type: %d\n", curElement.type);
1713 } while (hr == S_OK);
1716 * Clean-up
1718 IEnumSTATSTG_Release(elements);
1720 return hr;
1723 /*************************************************************************
1724 * MoveElementTo (IStorage)
1726 static HRESULT WINAPI StorageImpl_MoveElementTo(
1727 IStorage* iface,
1728 const OLECHAR *pwcsName, /* [string][in] */
1729 IStorage *pstgDest, /* [unique][in] */
1730 const OLECHAR *pwcsNewName,/* [string][in] */
1731 DWORD grfFlags) /* [in] */
1733 FIXME("(%p %s %p %s %u): stub\n", iface,
1734 debugstr_w(pwcsName), pstgDest,
1735 debugstr_w(pwcsNewName), grfFlags);
1736 return E_NOTIMPL;
1739 /*************************************************************************
1740 * Commit (IStorage)
1742 * Ensures that any changes made to a storage object open in transacted mode
1743 * are reflected in the parent storage
1745 * NOTES
1746 * Wine doesn't implement transacted mode, which seems to be a basic
1747 * optimization, so we can ignore this stub for now.
1749 static HRESULT WINAPI StorageImpl_Commit(
1750 IStorage* iface,
1751 DWORD grfCommitFlags)/* [in] */
1753 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1754 return S_OK;
1757 /*************************************************************************
1758 * Revert (IStorage)
1760 * Discard all changes that have been made since the last commit operation
1762 static HRESULT WINAPI StorageImpl_Revert(
1763 IStorage* iface)
1765 FIXME("(%p): stub\n", iface);
1766 return E_NOTIMPL;
1769 /*************************************************************************
1770 * DestroyElement (IStorage)
1772 * Strategy: This implementation is built this way for simplicity not for speed.
1773 * I always delete the topmost element of the enumeration and adjust
1774 * the deleted element pointer all the time. This takes longer to
1775 * do but allow to reinvoke DestroyElement whenever we encounter a
1776 * storage object. The optimisation resides in the usage of another
1777 * enumeration strategy that would give all the leaves of a storage
1778 * first. (postfix order)
1780 static HRESULT WINAPI StorageImpl_DestroyElement(
1781 IStorage* iface,
1782 const OLECHAR *pwcsName)/* [string][in] */
1784 StorageImpl* const This=(StorageImpl*)iface;
1786 IEnumSTATSTGImpl* propertyEnumeration;
1787 HRESULT hr = S_OK;
1788 BOOL res;
1789 StgProperty propertyToDelete;
1790 StgProperty parentProperty;
1791 ULONG foundPropertyIndexToDelete;
1792 ULONG typeOfRelation;
1793 ULONG parentPropertyId = 0;
1795 TRACE("(%p, %s)\n",
1796 iface, debugstr_w(pwcsName));
1799 * Perform a sanity check on the parameters.
1801 if (pwcsName==NULL)
1802 return STG_E_INVALIDPOINTER;
1804 if ( STGM_ACCESS_MODE( This->base.openFlags ) == STGM_READ )
1805 return STG_E_ACCESSDENIED;
1808 * Create a property enumeration to search the property with the given name
1810 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1811 This->base.ancestorStorage,
1812 This->base.rootPropertySetIndex);
1814 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1815 propertyEnumeration,
1816 pwcsName,
1817 &propertyToDelete);
1819 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1821 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1823 return STG_E_FILENOTFOUND;
1827 * Find the parent property of the property to delete (the one that
1828 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1829 * the parent is This. Otherwise, the parent is one of its sibling...
1833 * First, read This's StgProperty..
1835 res = StorageImpl_ReadProperty(
1836 This->base.ancestorStorage,
1837 This->base.rootPropertySetIndex,
1838 &parentProperty);
1840 assert(res);
1843 * Second, check to see if by any chance the actual storage (This) is not
1844 * the parent of the property to delete... We never know...
1846 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1849 * Set data as it would have been done in the else part...
1851 typeOfRelation = PROPERTY_RELATION_DIR;
1852 parentPropertyId = This->base.rootPropertySetIndex;
1854 else
1857 * Create a property enumeration to search the parent properties, and
1858 * delete it once done.
1860 IEnumSTATSTGImpl* propertyEnumeration2;
1862 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1863 This->base.ancestorStorage,
1864 This->base.rootPropertySetIndex);
1866 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1867 propertyEnumeration2,
1868 foundPropertyIndexToDelete,
1869 &parentProperty,
1870 &parentPropertyId);
1872 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1875 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1877 hr = deleteStorageProperty(
1878 This,
1879 foundPropertyIndexToDelete,
1880 propertyToDelete);
1882 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1884 hr = deleteStreamProperty(
1885 This,
1886 foundPropertyIndexToDelete,
1887 propertyToDelete);
1890 if (hr!=S_OK)
1891 return hr;
1894 * Adjust the property chain
1896 hr = adjustPropertyChain(
1897 This,
1898 propertyToDelete,
1899 parentProperty,
1900 parentPropertyId,
1901 typeOfRelation);
1903 return hr;
1907 /************************************************************************
1908 * StorageImpl_Stat (IStorage)
1910 * This method will retrieve information about this storage object.
1912 * See Windows documentation for more details on IStorage methods.
1914 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1915 STATSTG* pstatstg, /* [out] */
1916 DWORD grfStatFlag) /* [in] */
1918 StorageImpl* const This = (StorageImpl*)iface;
1919 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1921 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1923 CoTaskMemFree(pstatstg->pwcsName);
1924 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1925 strcpyW(pstatstg->pwcsName, This->pwcsName);
1928 return result;
1931 /******************************************************************************
1932 * Internal stream list handlers
1935 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1937 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1938 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1941 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1943 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1944 list_remove(&(strm->StrmListEntry));
1947 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1949 struct list *cur, *cur2;
1950 StgStreamImpl *strm=NULL;
1952 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1953 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1954 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1955 strm->parentStorage = NULL;
1956 list_remove(cur);
1961 /*********************************************************************
1963 * Internal Method
1965 * Perform the deletion of a complete storage node
1968 static HRESULT deleteStorageProperty(
1969 StorageImpl *parentStorage,
1970 ULONG indexOfPropertyToDelete,
1971 StgProperty propertyToDelete)
1973 IEnumSTATSTG *elements = 0;
1974 IStorage *childStorage = 0;
1975 STATSTG currentElement;
1976 HRESULT hr;
1977 HRESULT destroyHr = S_OK;
1980 * Open the storage and enumerate it
1982 hr = StorageBaseImpl_OpenStorage(
1983 (IStorage*)parentStorage,
1984 propertyToDelete.name,
1986 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1989 &childStorage);
1991 if (hr != S_OK)
1993 return hr;
1997 * Enumerate the elements
1999 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2004 * Obtain the next element
2006 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2007 if (hr==S_OK)
2009 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
2011 CoTaskMemFree(currentElement.pwcsName);
2015 * We need to Reset the enumeration every time because we delete elements
2016 * and the enumeration could be invalid
2018 IEnumSTATSTG_Reset(elements);
2020 } while ((hr == S_OK) && (destroyHr == S_OK));
2023 * Invalidate the property by zeroing its name member.
2025 propertyToDelete.sizeOfNameString = 0;
2027 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2028 indexOfPropertyToDelete,
2029 &propertyToDelete);
2031 IStorage_Release(childStorage);
2032 IEnumSTATSTG_Release(elements);
2034 return destroyHr;
2037 /*********************************************************************
2039 * Internal Method
2041 * Perform the deletion of a stream node
2044 static HRESULT deleteStreamProperty(
2045 StorageImpl *parentStorage,
2046 ULONG indexOfPropertyToDelete,
2047 StgProperty propertyToDelete)
2049 IStream *pis;
2050 HRESULT hr;
2051 ULARGE_INTEGER size;
2053 size.u.HighPart = 0;
2054 size.u.LowPart = 0;
2056 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2057 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2059 if (hr!=S_OK)
2061 return(hr);
2065 * Zap the stream
2067 hr = IStream_SetSize(pis, size);
2069 if(hr != S_OK)
2071 return hr;
2075 * Release the stream object.
2077 IStream_Release(pis);
2080 * Invalidate the property by zeroing its name member.
2082 propertyToDelete.sizeOfNameString = 0;
2085 * Here we should re-read the property so we get the updated pointer
2086 * but since we are here to zap it, I don't do it...
2088 StorageImpl_WriteProperty(
2089 parentStorage->base.ancestorStorage,
2090 indexOfPropertyToDelete,
2091 &propertyToDelete);
2093 return S_OK;
2096 /*********************************************************************
2098 * Internal Method
2100 * Finds a placeholder for the StgProperty within the Storage
2103 static HRESULT findPlaceholder(
2104 StorageImpl *storage,
2105 ULONG propertyIndexToStore,
2106 ULONG storePropertyIndex,
2107 INT typeOfRelation)
2109 StgProperty storeProperty;
2110 BOOL res = TRUE;
2113 * Read the storage property
2115 res = StorageImpl_ReadProperty(
2116 storage->base.ancestorStorage,
2117 storePropertyIndex,
2118 &storeProperty);
2120 if(! res)
2122 return E_FAIL;
2125 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2127 if (storeProperty.previousProperty != PROPERTY_NULL)
2129 return findPlaceholder(
2130 storage,
2131 propertyIndexToStore,
2132 storeProperty.previousProperty,
2133 typeOfRelation);
2135 else
2137 storeProperty.previousProperty = propertyIndexToStore;
2140 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2142 if (storeProperty.nextProperty != PROPERTY_NULL)
2144 return findPlaceholder(
2145 storage,
2146 propertyIndexToStore,
2147 storeProperty.nextProperty,
2148 typeOfRelation);
2150 else
2152 storeProperty.nextProperty = propertyIndexToStore;
2155 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2157 if (storeProperty.dirProperty != PROPERTY_NULL)
2159 return findPlaceholder(
2160 storage,
2161 propertyIndexToStore,
2162 storeProperty.dirProperty,
2163 typeOfRelation);
2165 else
2167 storeProperty.dirProperty = propertyIndexToStore;
2171 res = StorageImpl_WriteProperty(
2172 storage->base.ancestorStorage,
2173 storePropertyIndex,
2174 &storeProperty);
2176 if(!res)
2178 return E_FAIL;
2181 return S_OK;
2184 /*************************************************************************
2186 * Internal Method
2188 * This method takes the previous and the next property link of a property
2189 * to be deleted and find them a place in the Storage.
2191 static HRESULT adjustPropertyChain(
2192 StorageImpl *This,
2193 StgProperty propertyToDelete,
2194 StgProperty parentProperty,
2195 ULONG parentPropertyId,
2196 INT typeOfRelation)
2198 ULONG newLinkProperty = PROPERTY_NULL;
2199 BOOL needToFindAPlaceholder = FALSE;
2200 ULONG storeNode = PROPERTY_NULL;
2201 ULONG toStoreNode = PROPERTY_NULL;
2202 INT relationType = 0;
2203 HRESULT hr = S_OK;
2204 BOOL res = TRUE;
2206 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2208 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2211 * Set the parent previous to the property to delete previous
2213 newLinkProperty = propertyToDelete.previousProperty;
2215 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2218 * We also need to find a storage for the other link, setup variables
2219 * to do this at the end...
2221 needToFindAPlaceholder = TRUE;
2222 storeNode = propertyToDelete.previousProperty;
2223 toStoreNode = propertyToDelete.nextProperty;
2224 relationType = PROPERTY_RELATION_NEXT;
2227 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2230 * Set the parent previous to the property to delete next
2232 newLinkProperty = propertyToDelete.nextProperty;
2236 * Link it for real...
2238 parentProperty.previousProperty = newLinkProperty;
2241 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2243 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2246 * Set the parent next to the property to delete next previous
2248 newLinkProperty = propertyToDelete.previousProperty;
2250 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2253 * We also need to find a storage for the other link, setup variables
2254 * to do this at the end...
2256 needToFindAPlaceholder = TRUE;
2257 storeNode = propertyToDelete.previousProperty;
2258 toStoreNode = propertyToDelete.nextProperty;
2259 relationType = PROPERTY_RELATION_NEXT;
2262 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2265 * Set the parent next to the property to delete next
2267 newLinkProperty = propertyToDelete.nextProperty;
2271 * Link it for real...
2273 parentProperty.nextProperty = newLinkProperty;
2275 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2277 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2280 * Set the parent dir to the property to delete previous
2282 newLinkProperty = propertyToDelete.previousProperty;
2284 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2287 * We also need to find a storage for the other link, setup variables
2288 * to do this at the end...
2290 needToFindAPlaceholder = TRUE;
2291 storeNode = propertyToDelete.previousProperty;
2292 toStoreNode = propertyToDelete.nextProperty;
2293 relationType = PROPERTY_RELATION_NEXT;
2296 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2299 * Set the parent dir to the property to delete next
2301 newLinkProperty = propertyToDelete.nextProperty;
2305 * Link it for real...
2307 parentProperty.dirProperty = newLinkProperty;
2311 * Write back the parent property
2313 res = StorageImpl_WriteProperty(
2314 This->base.ancestorStorage,
2315 parentPropertyId,
2316 &parentProperty);
2317 if(! res)
2319 return E_FAIL;
2323 * If a placeholder is required for the other link, then, find one and
2324 * get out of here...
2326 if (needToFindAPlaceholder)
2328 hr = findPlaceholder(
2329 This,
2330 toStoreNode,
2331 storeNode,
2332 relationType);
2335 return hr;
2339 /******************************************************************************
2340 * SetElementTimes (IStorage)
2342 static HRESULT WINAPI StorageImpl_SetElementTimes(
2343 IStorage* iface,
2344 const OLECHAR *pwcsName,/* [string][in] */
2345 const FILETIME *pctime, /* [in] */
2346 const FILETIME *patime, /* [in] */
2347 const FILETIME *pmtime) /* [in] */
2349 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2350 return S_OK;
2353 /******************************************************************************
2354 * SetStateBits (IStorage)
2356 static HRESULT WINAPI StorageImpl_SetStateBits(
2357 IStorage* iface,
2358 DWORD grfStateBits,/* [in] */
2359 DWORD grfMask) /* [in] */
2361 StorageImpl* const This = (StorageImpl*)iface;
2362 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2363 return S_OK;
2367 * Virtual function table for the IStorage32Impl class.
2369 static const IStorageVtbl Storage32Impl_Vtbl =
2371 StorageBaseImpl_QueryInterface,
2372 StorageBaseImpl_AddRef,
2373 StorageBaseImpl_Release,
2374 StorageBaseImpl_CreateStream,
2375 StorageBaseImpl_OpenStream,
2376 StorageImpl_CreateStorage,
2377 StorageBaseImpl_OpenStorage,
2378 StorageImpl_CopyTo,
2379 StorageImpl_MoveElementTo,
2380 StorageImpl_Commit,
2381 StorageImpl_Revert,
2382 StorageBaseImpl_EnumElements,
2383 StorageImpl_DestroyElement,
2384 StorageBaseImpl_RenameElement,
2385 StorageImpl_SetElementTimes,
2386 StorageBaseImpl_SetClass,
2387 StorageImpl_SetStateBits,
2388 StorageImpl_Stat
2391 static HRESULT StorageImpl_Construct(
2392 StorageImpl* This,
2393 HANDLE hFile,
2394 LPCOLESTR pwcsName,
2395 ILockBytes* pLkbyt,
2396 DWORD openFlags,
2397 BOOL fileBased,
2398 BOOL fileCreate)
2400 HRESULT hr = S_OK;
2401 StgProperty currentProperty;
2402 BOOL readSuccessful;
2403 ULONG currentPropertyIndex;
2405 if ( FAILED( validateSTGM(openFlags) ))
2406 return STG_E_INVALIDFLAG;
2408 memset(This, 0, sizeof(StorageImpl));
2411 * Initialize stream list
2414 list_init(&This->base.strmHead);
2417 * Initialize the virtual function table.
2419 This->base.lpVtbl = &Storage32Impl_Vtbl;
2420 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2421 This->base.v_destructor = StorageImpl_Destroy;
2422 This->base.openFlags = (openFlags & ~STGM_CREATE);
2425 * This is the top-level storage so initialize the ancestor pointer
2426 * to this.
2428 This->base.ancestorStorage = This;
2431 * Initialize the physical support of the storage.
2433 This->hFile = hFile;
2436 * Store copy of file path.
2438 if(pwcsName) {
2439 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2440 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2441 if (!This->pwcsName)
2442 return STG_E_INSUFFICIENTMEMORY;
2443 strcpyW(This->pwcsName, pwcsName);
2447 * Initialize the big block cache.
2449 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2450 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2451 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2452 pLkbyt,
2453 openFlags,
2454 This->bigBlockSize,
2455 fileBased);
2457 if (This->bigBlockFile == 0)
2458 return E_FAIL;
2460 if (fileCreate)
2462 ULARGE_INTEGER size;
2463 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2466 * Initialize all header variables:
2467 * - The big block depot consists of one block and it is at block 0
2468 * - The properties start at block 1
2469 * - There is no small block depot
2471 memset( This->bigBlockDepotStart,
2472 BLOCK_UNUSED,
2473 sizeof(This->bigBlockDepotStart));
2475 This->bigBlockDepotCount = 1;
2476 This->bigBlockDepotStart[0] = 0;
2477 This->rootStartBlock = 1;
2478 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2479 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2480 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2481 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2482 This->extBigBlockDepotCount = 0;
2484 StorageImpl_SaveFileHeader(This);
2487 * Add one block for the big block depot and one block for the properties
2489 size.u.HighPart = 0;
2490 size.u.LowPart = This->bigBlockSize * 3;
2491 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2494 * Initialize the big block depot
2496 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2497 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2498 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2499 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2501 else
2504 * Load the header for the file.
2506 hr = StorageImpl_LoadFileHeader(This);
2508 if (FAILED(hr))
2510 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2512 return hr;
2517 * There is no block depot cached yet.
2519 This->indexBlockDepotCached = 0xFFFFFFFF;
2522 * Start searching for free blocks with block 0.
2524 This->prevFreeBlock = 0;
2527 * Create the block chain abstractions.
2529 if(!(This->rootBlockChain =
2530 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2531 return STG_E_READFAULT;
2533 if(!(This->smallBlockDepotChain =
2534 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2535 PROPERTY_NULL)))
2536 return STG_E_READFAULT;
2539 * Write the root property (memory only)
2541 if (fileCreate)
2543 StgProperty rootProp;
2545 * Initialize the property chain
2547 memset(&rootProp, 0, sizeof(rootProp));
2548 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2549 sizeof(rootProp.name)/sizeof(WCHAR) );
2550 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2551 rootProp.propertyType = PROPTYPE_ROOT;
2552 rootProp.previousProperty = PROPERTY_NULL;
2553 rootProp.nextProperty = PROPERTY_NULL;
2554 rootProp.dirProperty = PROPERTY_NULL;
2555 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2556 rootProp.size.u.HighPart = 0;
2557 rootProp.size.u.LowPart = 0;
2559 StorageImpl_WriteProperty(This, 0, &rootProp);
2563 * Find the ID of the root in the property sets.
2565 currentPropertyIndex = 0;
2569 readSuccessful = StorageImpl_ReadProperty(
2570 This,
2571 currentPropertyIndex,
2572 &currentProperty);
2574 if (readSuccessful)
2576 if ( (currentProperty.sizeOfNameString != 0 ) &&
2577 (currentProperty.propertyType == PROPTYPE_ROOT) )
2579 This->base.rootPropertySetIndex = currentPropertyIndex;
2583 currentPropertyIndex++;
2585 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2587 if (!readSuccessful)
2589 /* TODO CLEANUP */
2590 return STG_E_READFAULT;
2594 * Create the block chain abstraction for the small block root chain.
2596 if(!(This->smallBlockRootChain =
2597 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2598 return STG_E_READFAULT;
2600 return hr;
2603 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2605 StorageImpl *This = (StorageImpl*) iface;
2606 TRACE("(%p)\n", This);
2608 StorageBaseImpl_DeleteAll(&This->base);
2610 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2612 BlockChainStream_Destroy(This->smallBlockRootChain);
2613 BlockChainStream_Destroy(This->rootBlockChain);
2614 BlockChainStream_Destroy(This->smallBlockDepotChain);
2616 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2617 HeapFree(GetProcessHeap(), 0, This);
2620 /******************************************************************************
2621 * Storage32Impl_GetNextFreeBigBlock
2623 * Returns the index of the next free big block.
2624 * If the big block depot is filled, this method will enlarge it.
2627 static ULONG StorageImpl_GetNextFreeBigBlock(
2628 StorageImpl* This)
2630 ULONG depotBlockIndexPos;
2631 BYTE depotBuffer[BIG_BLOCK_SIZE];
2632 BOOL success;
2633 ULONG depotBlockOffset;
2634 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2635 ULONG nextBlockIndex = BLOCK_SPECIAL;
2636 int depotIndex = 0;
2637 ULONG freeBlock = BLOCK_UNUSED;
2639 depotIndex = This->prevFreeBlock / blocksPerDepot;
2640 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2643 * Scan the entire big block depot until we find a block marked free
2645 while (nextBlockIndex != BLOCK_UNUSED)
2647 if (depotIndex < COUNT_BBDEPOTINHEADER)
2649 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2652 * Grow the primary depot.
2654 if (depotBlockIndexPos == BLOCK_UNUSED)
2656 depotBlockIndexPos = depotIndex*blocksPerDepot;
2659 * Add a block depot.
2661 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2662 This->bigBlockDepotCount++;
2663 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2666 * Flag it as a block depot.
2668 StorageImpl_SetNextBlockInChain(This,
2669 depotBlockIndexPos,
2670 BLOCK_SPECIAL);
2672 /* Save new header information.
2674 StorageImpl_SaveFileHeader(This);
2677 else
2679 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2681 if (depotBlockIndexPos == BLOCK_UNUSED)
2684 * Grow the extended depot.
2686 ULONG extIndex = BLOCK_UNUSED;
2687 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2688 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2690 if (extBlockOffset == 0)
2692 /* We need an extended block.
2694 extIndex = Storage32Impl_AddExtBlockDepot(This);
2695 This->extBigBlockDepotCount++;
2696 depotBlockIndexPos = extIndex + 1;
2698 else
2699 depotBlockIndexPos = depotIndex * blocksPerDepot;
2702 * Add a block depot and mark it in the extended block.
2704 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2705 This->bigBlockDepotCount++;
2706 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2708 /* Flag the block depot.
2710 StorageImpl_SetNextBlockInChain(This,
2711 depotBlockIndexPos,
2712 BLOCK_SPECIAL);
2714 /* If necessary, flag the extended depot block.
2716 if (extIndex != BLOCK_UNUSED)
2717 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2719 /* Save header information.
2721 StorageImpl_SaveFileHeader(This);
2725 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2727 if (success)
2729 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2730 ( nextBlockIndex != BLOCK_UNUSED))
2732 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2734 if (nextBlockIndex == BLOCK_UNUSED)
2736 freeBlock = (depotIndex * blocksPerDepot) +
2737 (depotBlockOffset/sizeof(ULONG));
2740 depotBlockOffset += sizeof(ULONG);
2744 depotIndex++;
2745 depotBlockOffset = 0;
2749 * make sure that the block physically exists before using it
2751 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2753 This->prevFreeBlock = freeBlock;
2755 return freeBlock;
2758 /******************************************************************************
2759 * Storage32Impl_AddBlockDepot
2761 * This will create a depot block, essentially it is a block initialized
2762 * to BLOCK_UNUSEDs.
2764 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2766 BYTE blockBuffer[BIG_BLOCK_SIZE];
2769 * Initialize blocks as free
2771 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2772 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2775 /******************************************************************************
2776 * Storage32Impl_GetExtDepotBlock
2778 * Returns the index of the block that corresponds to the specified depot
2779 * index. This method is only for depot indexes equal or greater than
2780 * COUNT_BBDEPOTINHEADER.
2782 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2784 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2785 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2786 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2787 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2788 ULONG blockIndex = BLOCK_UNUSED;
2789 ULONG extBlockIndex = This->extBigBlockDepotStart;
2791 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2793 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2794 return BLOCK_UNUSED;
2796 while (extBlockCount > 0)
2798 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2799 extBlockCount--;
2802 if (extBlockIndex != BLOCK_UNUSED)
2803 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2804 extBlockOffset * sizeof(ULONG), &blockIndex);
2806 return blockIndex;
2809 /******************************************************************************
2810 * Storage32Impl_SetExtDepotBlock
2812 * Associates the specified block index to the specified depot index.
2813 * This method is only for depot indexes equal or greater than
2814 * COUNT_BBDEPOTINHEADER.
2816 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2818 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2819 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2820 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2821 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2822 ULONG extBlockIndex = This->extBigBlockDepotStart;
2824 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2826 while (extBlockCount > 0)
2828 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2829 extBlockCount--;
2832 if (extBlockIndex != BLOCK_UNUSED)
2834 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2835 extBlockOffset * sizeof(ULONG),
2836 blockIndex);
2840 /******************************************************************************
2841 * Storage32Impl_AddExtBlockDepot
2843 * Creates an extended depot block.
2845 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2847 ULONG numExtBlocks = This->extBigBlockDepotCount;
2848 ULONG nextExtBlock = This->extBigBlockDepotStart;
2849 BYTE depotBuffer[BIG_BLOCK_SIZE];
2850 ULONG index = BLOCK_UNUSED;
2851 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2852 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2853 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2855 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2856 blocksPerDepotBlock;
2858 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2861 * The first extended block.
2863 This->extBigBlockDepotStart = index;
2865 else
2867 unsigned int i;
2869 * Follow the chain to the last one.
2871 for (i = 0; i < (numExtBlocks - 1); i++)
2873 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2877 * Add the new extended block to the chain.
2879 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2880 index);
2884 * Initialize this block.
2886 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2887 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2889 return index;
2892 /******************************************************************************
2893 * Storage32Impl_FreeBigBlock
2895 * This method will flag the specified block as free in the big block depot.
2897 static void StorageImpl_FreeBigBlock(
2898 StorageImpl* This,
2899 ULONG blockIndex)
2901 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2903 if (blockIndex < This->prevFreeBlock)
2904 This->prevFreeBlock = blockIndex;
2907 /************************************************************************
2908 * Storage32Impl_GetNextBlockInChain
2910 * This method will retrieve the block index of the next big block in
2911 * in the chain.
2913 * Params: This - Pointer to the Storage object.
2914 * blockIndex - Index of the block to retrieve the chain
2915 * for.
2916 * nextBlockIndex - receives the return value.
2918 * Returns: This method returns the index of the next block in the chain.
2919 * It will return the constants:
2920 * BLOCK_SPECIAL - If the block given was not part of a
2921 * chain.
2922 * BLOCK_END_OF_CHAIN - If the block given was the last in
2923 * a chain.
2924 * BLOCK_UNUSED - If the block given was not past of a chain
2925 * and is available.
2926 * BLOCK_EXTBBDEPOT - This block is part of the extended
2927 * big block depot.
2929 * See Windows documentation for more details on IStorage methods.
2931 static HRESULT StorageImpl_GetNextBlockInChain(
2932 StorageImpl* This,
2933 ULONG blockIndex,
2934 ULONG* nextBlockIndex)
2936 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2937 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2938 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2939 BYTE depotBuffer[BIG_BLOCK_SIZE];
2940 BOOL success;
2941 ULONG depotBlockIndexPos;
2942 int index;
2944 *nextBlockIndex = BLOCK_SPECIAL;
2946 if(depotBlockCount >= This->bigBlockDepotCount)
2948 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2949 This->bigBlockDepotCount);
2950 return STG_E_READFAULT;
2954 * Cache the currently accessed depot block.
2956 if (depotBlockCount != This->indexBlockDepotCached)
2958 This->indexBlockDepotCached = depotBlockCount;
2960 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2962 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2964 else
2967 * We have to look in the extended depot.
2969 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2972 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2974 if (!success)
2975 return STG_E_READFAULT;
2977 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2979 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2980 This->blockDepotCached[index] = *nextBlockIndex;
2984 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2986 return S_OK;
2989 /******************************************************************************
2990 * Storage32Impl_GetNextExtendedBlock
2992 * Given an extended block this method will return the next extended block.
2994 * NOTES:
2995 * The last ULONG of an extended block is the block index of the next
2996 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2997 * depot.
2999 * Return values:
3000 * - The index of the next extended block
3001 * - BLOCK_UNUSED: there is no next extended block.
3002 * - Any other return values denotes failure.
3004 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3006 ULONG nextBlockIndex = BLOCK_SPECIAL;
3007 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3009 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3010 &nextBlockIndex);
3012 return nextBlockIndex;
3015 /******************************************************************************
3016 * Storage32Impl_SetNextBlockInChain
3018 * This method will write the index of the specified block's next block
3019 * in the big block depot.
3021 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3022 * do the following
3024 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3025 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3026 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3029 static void StorageImpl_SetNextBlockInChain(
3030 StorageImpl* This,
3031 ULONG blockIndex,
3032 ULONG nextBlock)
3034 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3035 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3036 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3037 ULONG depotBlockIndexPos;
3039 assert(depotBlockCount < This->bigBlockDepotCount);
3040 assert(blockIndex != nextBlock);
3042 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3044 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3046 else
3049 * We have to look in the extended depot.
3051 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3054 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3055 nextBlock);
3057 * Update the cached block depot, if necessary.
3059 if (depotBlockCount == This->indexBlockDepotCached)
3061 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3065 /******************************************************************************
3066 * Storage32Impl_LoadFileHeader
3068 * This method will read in the file header, i.e. big block index -1.
3070 static HRESULT StorageImpl_LoadFileHeader(
3071 StorageImpl* This)
3073 HRESULT hr = STG_E_FILENOTFOUND;
3074 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3075 BOOL success;
3076 int index;
3078 TRACE("\n");
3080 * Get a pointer to the big block of data containing the header.
3082 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3085 * Extract the information from the header.
3087 if (success)
3090 * Check for the "magic number" signature and return an error if it is not
3091 * found.
3093 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3095 return STG_E_OLDFORMAT;
3098 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3100 return STG_E_INVALIDHEADER;
3103 StorageUtl_ReadWord(
3104 headerBigBlock,
3105 OFFSET_BIGBLOCKSIZEBITS,
3106 &This->bigBlockSizeBits);
3108 StorageUtl_ReadWord(
3109 headerBigBlock,
3110 OFFSET_SMALLBLOCKSIZEBITS,
3111 &This->smallBlockSizeBits);
3113 StorageUtl_ReadDWord(
3114 headerBigBlock,
3115 OFFSET_BBDEPOTCOUNT,
3116 &This->bigBlockDepotCount);
3118 StorageUtl_ReadDWord(
3119 headerBigBlock,
3120 OFFSET_ROOTSTARTBLOCK,
3121 &This->rootStartBlock);
3123 StorageUtl_ReadDWord(
3124 headerBigBlock,
3125 OFFSET_SBDEPOTSTART,
3126 &This->smallBlockDepotStart);
3128 StorageUtl_ReadDWord(
3129 headerBigBlock,
3130 OFFSET_EXTBBDEPOTSTART,
3131 &This->extBigBlockDepotStart);
3133 StorageUtl_ReadDWord(
3134 headerBigBlock,
3135 OFFSET_EXTBBDEPOTCOUNT,
3136 &This->extBigBlockDepotCount);
3138 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3140 StorageUtl_ReadDWord(
3141 headerBigBlock,
3142 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3143 &(This->bigBlockDepotStart[index]));
3147 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3149 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3150 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3153 * Right now, the code is making some assumptions about the size of the
3154 * blocks, just make sure they are what we're expecting.
3156 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3157 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3159 WARN("Broken OLE storage file\n");
3160 hr = STG_E_INVALIDHEADER;
3162 else
3163 hr = S_OK;
3166 return hr;
3169 /******************************************************************************
3170 * Storage32Impl_SaveFileHeader
3172 * This method will save to the file the header, i.e. big block -1.
3174 static void StorageImpl_SaveFileHeader(
3175 StorageImpl* This)
3177 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3178 int index;
3179 BOOL success;
3182 * Get a pointer to the big block of data containing the header.
3184 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3187 * If the block read failed, the file is probably new.
3189 if (!success)
3192 * Initialize for all unknown fields.
3194 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3197 * Initialize the magic number.
3199 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3202 * And a bunch of things we don't know what they mean
3204 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3205 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3206 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3207 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3211 * Write the information to the header.
3213 StorageUtl_WriteWord(
3214 headerBigBlock,
3215 OFFSET_BIGBLOCKSIZEBITS,
3216 This->bigBlockSizeBits);
3218 StorageUtl_WriteWord(
3219 headerBigBlock,
3220 OFFSET_SMALLBLOCKSIZEBITS,
3221 This->smallBlockSizeBits);
3223 StorageUtl_WriteDWord(
3224 headerBigBlock,
3225 OFFSET_BBDEPOTCOUNT,
3226 This->bigBlockDepotCount);
3228 StorageUtl_WriteDWord(
3229 headerBigBlock,
3230 OFFSET_ROOTSTARTBLOCK,
3231 This->rootStartBlock);
3233 StorageUtl_WriteDWord(
3234 headerBigBlock,
3235 OFFSET_SBDEPOTSTART,
3236 This->smallBlockDepotStart);
3238 StorageUtl_WriteDWord(
3239 headerBigBlock,
3240 OFFSET_SBDEPOTCOUNT,
3241 This->smallBlockDepotChain ?
3242 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3244 StorageUtl_WriteDWord(
3245 headerBigBlock,
3246 OFFSET_EXTBBDEPOTSTART,
3247 This->extBigBlockDepotStart);
3249 StorageUtl_WriteDWord(
3250 headerBigBlock,
3251 OFFSET_EXTBBDEPOTCOUNT,
3252 This->extBigBlockDepotCount);
3254 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3256 StorageUtl_WriteDWord(
3257 headerBigBlock,
3258 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3259 (This->bigBlockDepotStart[index]));
3263 * Write the big block back to the file.
3265 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3268 /******************************************************************************
3269 * Storage32Impl_ReadProperty
3271 * This method will read the specified property from the property chain.
3273 BOOL StorageImpl_ReadProperty(
3274 StorageImpl* This,
3275 ULONG index,
3276 StgProperty* buffer)
3278 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3279 ULARGE_INTEGER offsetInPropSet;
3280 HRESULT readRes;
3281 ULONG bytesRead;
3283 offsetInPropSet.u.HighPart = 0;
3284 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3286 readRes = BlockChainStream_ReadAt(
3287 This->rootBlockChain,
3288 offsetInPropSet,
3289 PROPSET_BLOCK_SIZE,
3290 currentProperty,
3291 &bytesRead);
3293 if (SUCCEEDED(readRes))
3295 /* replace the name of root entry (often "Root Entry") by the file name */
3296 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3297 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3299 memset(buffer->name, 0, sizeof(buffer->name));
3300 memcpy(
3301 buffer->name,
3302 propName,
3303 PROPERTY_NAME_BUFFER_LEN );
3304 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3306 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3308 StorageUtl_ReadWord(
3309 currentProperty,
3310 OFFSET_PS_NAMELENGTH,
3311 &buffer->sizeOfNameString);
3313 StorageUtl_ReadDWord(
3314 currentProperty,
3315 OFFSET_PS_PREVIOUSPROP,
3316 &buffer->previousProperty);
3318 StorageUtl_ReadDWord(
3319 currentProperty,
3320 OFFSET_PS_NEXTPROP,
3321 &buffer->nextProperty);
3323 StorageUtl_ReadDWord(
3324 currentProperty,
3325 OFFSET_PS_DIRPROP,
3326 &buffer->dirProperty);
3328 StorageUtl_ReadGUID(
3329 currentProperty,
3330 OFFSET_PS_GUID,
3331 &buffer->propertyUniqueID);
3333 StorageUtl_ReadDWord(
3334 currentProperty,
3335 OFFSET_PS_TSS1,
3336 &buffer->timeStampS1);
3338 StorageUtl_ReadDWord(
3339 currentProperty,
3340 OFFSET_PS_TSD1,
3341 &buffer->timeStampD1);
3343 StorageUtl_ReadDWord(
3344 currentProperty,
3345 OFFSET_PS_TSS2,
3346 &buffer->timeStampS2);
3348 StorageUtl_ReadDWord(
3349 currentProperty,
3350 OFFSET_PS_TSD2,
3351 &buffer->timeStampD2);
3353 StorageUtl_ReadDWord(
3354 currentProperty,
3355 OFFSET_PS_STARTBLOCK,
3356 &buffer->startingBlock);
3358 StorageUtl_ReadDWord(
3359 currentProperty,
3360 OFFSET_PS_SIZE,
3361 &buffer->size.u.LowPart);
3363 buffer->size.u.HighPart = 0;
3366 return SUCCEEDED(readRes) ? TRUE : FALSE;
3369 /*********************************************************************
3370 * Write the specified property into the property chain
3372 BOOL StorageImpl_WriteProperty(
3373 StorageImpl* This,
3374 ULONG index,
3375 const StgProperty* buffer)
3377 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3378 ULARGE_INTEGER offsetInPropSet;
3379 HRESULT writeRes;
3380 ULONG bytesWritten;
3382 offsetInPropSet.u.HighPart = 0;
3383 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3385 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3387 memcpy(
3388 currentProperty + OFFSET_PS_NAME,
3389 buffer->name,
3390 PROPERTY_NAME_BUFFER_LEN );
3392 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3394 StorageUtl_WriteWord(
3395 currentProperty,
3396 OFFSET_PS_NAMELENGTH,
3397 buffer->sizeOfNameString);
3399 StorageUtl_WriteDWord(
3400 currentProperty,
3401 OFFSET_PS_PREVIOUSPROP,
3402 buffer->previousProperty);
3404 StorageUtl_WriteDWord(
3405 currentProperty,
3406 OFFSET_PS_NEXTPROP,
3407 buffer->nextProperty);
3409 StorageUtl_WriteDWord(
3410 currentProperty,
3411 OFFSET_PS_DIRPROP,
3412 buffer->dirProperty);
3414 StorageUtl_WriteGUID(
3415 currentProperty,
3416 OFFSET_PS_GUID,
3417 &buffer->propertyUniqueID);
3419 StorageUtl_WriteDWord(
3420 currentProperty,
3421 OFFSET_PS_TSS1,
3422 buffer->timeStampS1);
3424 StorageUtl_WriteDWord(
3425 currentProperty,
3426 OFFSET_PS_TSD1,
3427 buffer->timeStampD1);
3429 StorageUtl_WriteDWord(
3430 currentProperty,
3431 OFFSET_PS_TSS2,
3432 buffer->timeStampS2);
3434 StorageUtl_WriteDWord(
3435 currentProperty,
3436 OFFSET_PS_TSD2,
3437 buffer->timeStampD2);
3439 StorageUtl_WriteDWord(
3440 currentProperty,
3441 OFFSET_PS_STARTBLOCK,
3442 buffer->startingBlock);
3444 StorageUtl_WriteDWord(
3445 currentProperty,
3446 OFFSET_PS_SIZE,
3447 buffer->size.u.LowPart);
3449 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3450 offsetInPropSet,
3451 PROPSET_BLOCK_SIZE,
3452 currentProperty,
3453 &bytesWritten);
3454 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3457 static BOOL StorageImpl_ReadBigBlock(
3458 StorageImpl* This,
3459 ULONG blockIndex,
3460 void* buffer)
3462 ULARGE_INTEGER ulOffset;
3463 DWORD read;
3465 ulOffset.u.HighPart = 0;
3466 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3468 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3469 return (read == This->bigBlockSize);
3472 static BOOL StorageImpl_ReadDWordFromBigBlock(
3473 StorageImpl* This,
3474 ULONG blockIndex,
3475 ULONG offset,
3476 DWORD* value)
3478 ULARGE_INTEGER ulOffset;
3479 DWORD read;
3480 DWORD tmp;
3482 ulOffset.u.HighPart = 0;
3483 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3484 ulOffset.u.LowPart += offset;
3486 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3487 *value = lendian32toh(tmp);
3488 return (read == sizeof(DWORD));
3491 static BOOL StorageImpl_WriteBigBlock(
3492 StorageImpl* This,
3493 ULONG blockIndex,
3494 const void* buffer)
3496 ULARGE_INTEGER ulOffset;
3497 DWORD wrote;
3499 ulOffset.u.HighPart = 0;
3500 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3502 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3503 return (wrote == This->bigBlockSize);
3506 static BOOL StorageImpl_WriteDWordToBigBlock(
3507 StorageImpl* This,
3508 ULONG blockIndex,
3509 ULONG offset,
3510 DWORD value)
3512 ULARGE_INTEGER ulOffset;
3513 DWORD wrote;
3515 ulOffset.u.HighPart = 0;
3516 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3517 ulOffset.u.LowPart += offset;
3519 value = htole32(value);
3520 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3521 return (wrote == sizeof(DWORD));
3524 /******************************************************************************
3525 * Storage32Impl_SmallBlocksToBigBlocks
3527 * This method will convert a small block chain to a big block chain.
3528 * The small block chain will be destroyed.
3530 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3531 StorageImpl* This,
3532 SmallBlockChainStream** ppsbChain)
3534 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3535 ULARGE_INTEGER size, offset;
3536 ULONG cbRead, cbWritten;
3537 ULARGE_INTEGER cbTotalRead;
3538 ULONG propertyIndex;
3539 HRESULT resWrite = S_OK;
3540 HRESULT resRead;
3541 StgProperty chainProperty;
3542 BYTE *buffer;
3543 BlockChainStream *bbTempChain = NULL;
3544 BlockChainStream *bigBlockChain = NULL;
3547 * Create a temporary big block chain that doesn't have
3548 * an associated property. This temporary chain will be
3549 * used to copy data from small blocks to big blocks.
3551 bbTempChain = BlockChainStream_Construct(This,
3552 &bbHeadOfChain,
3553 PROPERTY_NULL);
3554 if(!bbTempChain) return NULL;
3556 * Grow the big block chain.
3558 size = SmallBlockChainStream_GetSize(*ppsbChain);
3559 BlockChainStream_SetSize(bbTempChain, size);
3562 * Copy the contents of the small block chain to the big block chain
3563 * by small block size increments.
3565 offset.u.LowPart = 0;
3566 offset.u.HighPart = 0;
3567 cbTotalRead.QuadPart = 0;
3569 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3572 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3573 offset,
3574 This->smallBlockSize,
3575 buffer,
3576 &cbRead);
3577 if (FAILED(resRead))
3578 break;
3580 if (cbRead > 0)
3582 cbTotalRead.QuadPart += cbRead;
3584 resWrite = BlockChainStream_WriteAt(bbTempChain,
3585 offset,
3586 cbRead,
3587 buffer,
3588 &cbWritten);
3590 if (FAILED(resWrite))
3591 break;
3593 offset.u.LowPart += This->smallBlockSize;
3595 } while (cbTotalRead.QuadPart < size.QuadPart);
3596 HeapFree(GetProcessHeap(),0,buffer);
3598 if (FAILED(resRead) || FAILED(resWrite))
3600 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3601 BlockChainStream_Destroy(bbTempChain);
3602 return NULL;
3606 * Destroy the small block chain.
3608 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3609 size.u.HighPart = 0;
3610 size.u.LowPart = 0;
3611 SmallBlockChainStream_SetSize(*ppsbChain, size);
3612 SmallBlockChainStream_Destroy(*ppsbChain);
3613 *ppsbChain = 0;
3616 * Change the property information. This chain is now a big block chain
3617 * and it doesn't reside in the small blocks chain anymore.
3619 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3621 chainProperty.startingBlock = bbHeadOfChain;
3623 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3626 * Destroy the temporary propertyless big block chain.
3627 * Create a new big block chain associated with this property.
3629 BlockChainStream_Destroy(bbTempChain);
3630 bigBlockChain = BlockChainStream_Construct(This,
3631 NULL,
3632 propertyIndex);
3634 return bigBlockChain;
3637 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3639 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3641 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3642 HeapFree(GetProcessHeap(), 0, This);
3645 /******************************************************************************
3647 ** Storage32InternalImpl_Commit
3649 ** The non-root storages cannot be opened in transacted mode thus this function
3650 ** does nothing.
3652 static HRESULT WINAPI StorageInternalImpl_Commit(
3653 IStorage* iface,
3654 DWORD grfCommitFlags) /* [in] */
3656 return S_OK;
3659 /******************************************************************************
3661 ** Storage32InternalImpl_Revert
3663 ** The non-root storages cannot be opened in transacted mode thus this function
3664 ** does nothing.
3666 static HRESULT WINAPI StorageInternalImpl_Revert(
3667 IStorage* iface)
3669 return S_OK;
3672 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3674 IStorage_Release((IStorage*)This->parentStorage);
3675 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3676 HeapFree(GetProcessHeap(), 0, This);
3679 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3680 IEnumSTATSTG* iface,
3681 REFIID riid,
3682 void** ppvObject)
3684 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3687 * Perform a sanity check on the parameters.
3689 if (ppvObject==0)
3690 return E_INVALIDARG;
3693 * Initialize the return parameter.
3695 *ppvObject = 0;
3698 * Compare the riid with the interface IDs implemented by this object.
3700 if (IsEqualGUID(&IID_IUnknown, riid) ||
3701 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3703 *ppvObject = This;
3704 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3705 return S_OK;
3708 return E_NOINTERFACE;
3711 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3712 IEnumSTATSTG* iface)
3714 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3715 return InterlockedIncrement(&This->ref);
3718 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3719 IEnumSTATSTG* iface)
3721 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3723 ULONG newRef;
3725 newRef = InterlockedDecrement(&This->ref);
3728 * If the reference count goes down to 0, perform suicide.
3730 if (newRef==0)
3732 IEnumSTATSTGImpl_Destroy(This);
3735 return newRef;
3738 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3739 IEnumSTATSTG* iface,
3740 ULONG celt,
3741 STATSTG* rgelt,
3742 ULONG* pceltFetched)
3744 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3746 StgProperty currentProperty;
3747 STATSTG* currentReturnStruct = rgelt;
3748 ULONG objectFetched = 0;
3749 ULONG currentSearchNode;
3752 * Perform a sanity check on the parameters.
3754 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3755 return E_INVALIDARG;
3758 * To avoid the special case, get another pointer to a ULONG value if
3759 * the caller didn't supply one.
3761 if (pceltFetched==0)
3762 pceltFetched = &objectFetched;
3765 * Start the iteration, we will iterate until we hit the end of the
3766 * linked list or until we hit the number of items to iterate through
3768 *pceltFetched = 0;
3771 * Start with the node at the top of the stack.
3773 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3775 while ( ( *pceltFetched < celt) &&
3776 ( currentSearchNode!=PROPERTY_NULL) )
3779 * Remove the top node from the stack
3781 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3784 * Read the property from the storage.
3786 StorageImpl_ReadProperty(This->parentStorage,
3787 currentSearchNode,
3788 &currentProperty);
3791 * Copy the information to the return buffer.
3793 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3794 &currentProperty,
3795 STATFLAG_DEFAULT);
3798 * Step to the next item in the iteration
3800 (*pceltFetched)++;
3801 currentReturnStruct++;
3804 * Push the next search node in the search stack.
3806 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3809 * continue the iteration.
3811 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3814 if (*pceltFetched == celt)
3815 return S_OK;
3817 return S_FALSE;
3821 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3822 IEnumSTATSTG* iface,
3823 ULONG celt)
3825 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3827 StgProperty currentProperty;
3828 ULONG objectFetched = 0;
3829 ULONG currentSearchNode;
3832 * Start with the node at the top of the stack.
3834 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3836 while ( (objectFetched < celt) &&
3837 (currentSearchNode!=PROPERTY_NULL) )
3840 * Remove the top node from the stack
3842 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3845 * Read the property from the storage.
3847 StorageImpl_ReadProperty(This->parentStorage,
3848 currentSearchNode,
3849 &currentProperty);
3852 * Step to the next item in the iteration
3854 objectFetched++;
3857 * Push the next search node in the search stack.
3859 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3862 * continue the iteration.
3864 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3867 if (objectFetched == celt)
3868 return S_OK;
3870 return S_FALSE;
3873 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3874 IEnumSTATSTG* iface)
3876 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3878 StgProperty rootProperty;
3879 BOOL readSuccessful;
3882 * Re-initialize the search stack to an empty stack
3884 This->stackSize = 0;
3887 * Read the root property from the storage.
3889 readSuccessful = StorageImpl_ReadProperty(
3890 This->parentStorage,
3891 This->firstPropertyNode,
3892 &rootProperty);
3894 if (readSuccessful)
3896 assert(rootProperty.sizeOfNameString!=0);
3899 * Push the search node in the search stack.
3901 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3904 return S_OK;
3907 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3908 IEnumSTATSTG* iface,
3909 IEnumSTATSTG** ppenum)
3911 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3913 IEnumSTATSTGImpl* newClone;
3916 * Perform a sanity check on the parameters.
3918 if (ppenum==0)
3919 return E_INVALIDARG;
3921 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3922 This->firstPropertyNode);
3926 * The new clone enumeration must point to the same current node as
3927 * the ole one.
3929 newClone->stackSize = This->stackSize ;
3930 newClone->stackMaxSize = This->stackMaxSize ;
3931 newClone->stackToVisit =
3932 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3934 memcpy(
3935 newClone->stackToVisit,
3936 This->stackToVisit,
3937 sizeof(ULONG) * newClone->stackSize);
3939 *ppenum = (IEnumSTATSTG*)newClone;
3942 * Don't forget to nail down a reference to the clone before
3943 * returning it.
3945 IEnumSTATSTGImpl_AddRef(*ppenum);
3947 return S_OK;
3950 static INT IEnumSTATSTGImpl_FindParentProperty(
3951 IEnumSTATSTGImpl *This,
3952 ULONG childProperty,
3953 StgProperty *currentProperty,
3954 ULONG *thisNodeId)
3956 ULONG currentSearchNode;
3957 ULONG foundNode;
3960 * To avoid the special case, get another pointer to a ULONG value if
3961 * the caller didn't supply one.
3963 if (thisNodeId==0)
3964 thisNodeId = &foundNode;
3967 * Start with the node at the top of the stack.
3969 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3972 while (currentSearchNode!=PROPERTY_NULL)
3975 * Store the current node in the returned parameters
3977 *thisNodeId = currentSearchNode;
3980 * Remove the top node from the stack
3982 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3985 * Read the property from the storage.
3987 StorageImpl_ReadProperty(
3988 This->parentStorage,
3989 currentSearchNode,
3990 currentProperty);
3992 if (currentProperty->previousProperty == childProperty)
3993 return PROPERTY_RELATION_PREVIOUS;
3995 else if (currentProperty->nextProperty == childProperty)
3996 return PROPERTY_RELATION_NEXT;
3998 else if (currentProperty->dirProperty == childProperty)
3999 return PROPERTY_RELATION_DIR;
4002 * Push the next search node in the search stack.
4004 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4007 * continue the iteration.
4009 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4012 return PROPERTY_NULL;
4015 static ULONG IEnumSTATSTGImpl_FindProperty(
4016 IEnumSTATSTGImpl* This,
4017 const OLECHAR* lpszPropName,
4018 StgProperty* currentProperty)
4020 ULONG currentSearchNode;
4023 * Start with the node at the top of the stack.
4025 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4027 while (currentSearchNode!=PROPERTY_NULL)
4030 * Remove the top node from the stack
4032 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4035 * Read the property from the storage.
4037 StorageImpl_ReadProperty(This->parentStorage,
4038 currentSearchNode,
4039 currentProperty);
4041 if (propertyNameCmp(currentProperty->name, lpszPropName) == 0)
4042 return currentSearchNode;
4045 * Push the next search node in the search stack.
4047 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4050 * continue the iteration.
4052 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4055 return PROPERTY_NULL;
4058 static void IEnumSTATSTGImpl_PushSearchNode(
4059 IEnumSTATSTGImpl* This,
4060 ULONG nodeToPush)
4062 StgProperty rootProperty;
4063 BOOL readSuccessful;
4066 * First, make sure we're not trying to push an unexisting node.
4068 if (nodeToPush==PROPERTY_NULL)
4069 return;
4072 * First push the node to the stack
4074 if (This->stackSize == This->stackMaxSize)
4076 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4078 This->stackToVisit = HeapReAlloc(
4079 GetProcessHeap(),
4081 This->stackToVisit,
4082 sizeof(ULONG) * This->stackMaxSize);
4085 This->stackToVisit[This->stackSize] = nodeToPush;
4086 This->stackSize++;
4089 * Read the root property from the storage.
4091 readSuccessful = StorageImpl_ReadProperty(
4092 This->parentStorage,
4093 nodeToPush,
4094 &rootProperty);
4096 if (readSuccessful)
4098 assert(rootProperty.sizeOfNameString!=0);
4101 * Push the previous search node in the search stack.
4103 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4107 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4108 IEnumSTATSTGImpl* This,
4109 BOOL remove)
4111 ULONG topNode;
4113 if (This->stackSize == 0)
4114 return PROPERTY_NULL;
4116 topNode = This->stackToVisit[This->stackSize-1];
4118 if (remove)
4119 This->stackSize--;
4121 return topNode;
4125 * Virtual function table for the IEnumSTATSTGImpl class.
4127 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4129 IEnumSTATSTGImpl_QueryInterface,
4130 IEnumSTATSTGImpl_AddRef,
4131 IEnumSTATSTGImpl_Release,
4132 IEnumSTATSTGImpl_Next,
4133 IEnumSTATSTGImpl_Skip,
4134 IEnumSTATSTGImpl_Reset,
4135 IEnumSTATSTGImpl_Clone
4138 /******************************************************************************
4139 ** IEnumSTATSTGImpl implementation
4142 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4143 StorageImpl* parentStorage,
4144 ULONG firstPropertyNode)
4146 IEnumSTATSTGImpl* newEnumeration;
4148 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4150 if (newEnumeration!=0)
4153 * Set-up the virtual function table and reference count.
4155 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4156 newEnumeration->ref = 0;
4159 * We want to nail-down the reference to the storage in case the
4160 * enumeration out-lives the storage in the client application.
4162 newEnumeration->parentStorage = parentStorage;
4163 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4165 newEnumeration->firstPropertyNode = firstPropertyNode;
4168 * Initialize the search stack
4170 newEnumeration->stackSize = 0;
4171 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4172 newEnumeration->stackToVisit =
4173 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4176 * Make sure the current node of the iterator is the first one.
4178 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4181 return newEnumeration;
4185 * Virtual function table for the Storage32InternalImpl class.
4187 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4189 StorageBaseImpl_QueryInterface,
4190 StorageBaseImpl_AddRef,
4191 StorageBaseImpl_Release,
4192 StorageBaseImpl_CreateStream,
4193 StorageBaseImpl_OpenStream,
4194 StorageImpl_CreateStorage,
4195 StorageBaseImpl_OpenStorage,
4196 StorageImpl_CopyTo,
4197 StorageImpl_MoveElementTo,
4198 StorageInternalImpl_Commit,
4199 StorageInternalImpl_Revert,
4200 StorageBaseImpl_EnumElements,
4201 StorageImpl_DestroyElement,
4202 StorageBaseImpl_RenameElement,
4203 StorageImpl_SetElementTimes,
4204 StorageBaseImpl_SetClass,
4205 StorageImpl_SetStateBits,
4206 StorageBaseImpl_Stat
4209 /******************************************************************************
4210 ** Storage32InternalImpl implementation
4213 static StorageInternalImpl* StorageInternalImpl_Construct(
4214 StorageImpl* ancestorStorage,
4215 DWORD openFlags,
4216 ULONG rootPropertyIndex)
4218 StorageInternalImpl* newStorage;
4221 * Allocate space for the new storage object
4223 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4225 if (newStorage!=0)
4228 * Initialize the stream list
4230 list_init(&newStorage->base.strmHead);
4233 * Initialize the virtual function table.
4235 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4236 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
4237 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4240 * Keep the ancestor storage pointer and nail a reference to it.
4242 newStorage->base.ancestorStorage = ancestorStorage;
4243 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4246 * Keep the index of the root property set for this storage,
4248 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4250 return newStorage;
4253 return 0;
4256 /******************************************************************************
4257 ** StorageUtl implementation
4260 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4262 WORD tmp;
4264 memcpy(&tmp, buffer+offset, sizeof(WORD));
4265 *value = lendian16toh(tmp);
4268 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4270 value = htole16(value);
4271 memcpy(buffer+offset, &value, sizeof(WORD));
4274 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4276 DWORD tmp;
4278 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4279 *value = lendian32toh(tmp);
4282 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4284 value = htole32(value);
4285 memcpy(buffer+offset, &value, sizeof(DWORD));
4288 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4289 ULARGE_INTEGER* value)
4291 #ifdef WORDS_BIGENDIAN
4292 ULARGE_INTEGER tmp;
4294 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4295 value->u.LowPart = htole32(tmp.u.HighPart);
4296 value->u.HighPart = htole32(tmp.u.LowPart);
4297 #else
4298 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4299 #endif
4302 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4303 const ULARGE_INTEGER *value)
4305 #ifdef WORDS_BIGENDIAN
4306 ULARGE_INTEGER tmp;
4308 tmp.u.LowPart = htole32(value->u.HighPart);
4309 tmp.u.HighPart = htole32(value->u.LowPart);
4310 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4311 #else
4312 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4313 #endif
4316 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4318 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4319 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4320 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4322 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4325 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4327 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4328 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4329 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4331 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4334 void StorageUtl_CopyPropertyToSTATSTG(
4335 STATSTG* destination,
4336 const StgProperty* source,
4337 int statFlags)
4340 * The copy of the string occurs only when the flag is not set
4342 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4343 (source->name == NULL) ||
4344 (source->name[0] == 0) )
4346 destination->pwcsName = 0;
4348 else
4350 destination->pwcsName =
4351 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4353 strcpyW(destination->pwcsName, source->name);
4356 switch (source->propertyType)
4358 case PROPTYPE_STORAGE:
4359 case PROPTYPE_ROOT:
4360 destination->type = STGTY_STORAGE;
4361 break;
4362 case PROPTYPE_STREAM:
4363 destination->type = STGTY_STREAM;
4364 break;
4365 default:
4366 destination->type = STGTY_STREAM;
4367 break;
4370 destination->cbSize = source->size;
4372 currentReturnStruct->mtime = {0}; TODO
4373 currentReturnStruct->ctime = {0};
4374 currentReturnStruct->atime = {0};
4376 destination->grfMode = 0;
4377 destination->grfLocksSupported = 0;
4378 destination->clsid = source->propertyUniqueID;
4379 destination->grfStateBits = 0;
4380 destination->reserved = 0;
4383 /******************************************************************************
4384 ** BlockChainStream implementation
4387 BlockChainStream* BlockChainStream_Construct(
4388 StorageImpl* parentStorage,
4389 ULONG* headOfStreamPlaceHolder,
4390 ULONG propertyIndex)
4392 BlockChainStream* newStream;
4393 ULONG blockIndex;
4395 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4397 newStream->parentStorage = parentStorage;
4398 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4399 newStream->ownerPropertyIndex = propertyIndex;
4400 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4401 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4402 newStream->numBlocks = 0;
4404 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4406 while (blockIndex != BLOCK_END_OF_CHAIN)
4408 newStream->numBlocks++;
4409 newStream->tailIndex = blockIndex;
4411 if(FAILED(StorageImpl_GetNextBlockInChain(
4412 parentStorage,
4413 blockIndex,
4414 &blockIndex)))
4416 HeapFree(GetProcessHeap(), 0, newStream);
4417 return NULL;
4421 return newStream;
4424 void BlockChainStream_Destroy(BlockChainStream* This)
4426 HeapFree(GetProcessHeap(), 0, This);
4429 /******************************************************************************
4430 * BlockChainStream_GetHeadOfChain
4432 * Returns the head of this stream chain.
4433 * Some special chains don't have properties, their heads are kept in
4434 * This->headOfStreamPlaceHolder.
4437 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4439 StgProperty chainProperty;
4440 BOOL readSuccessful;
4442 if (This->headOfStreamPlaceHolder != 0)
4443 return *(This->headOfStreamPlaceHolder);
4445 if (This->ownerPropertyIndex != PROPERTY_NULL)
4447 readSuccessful = StorageImpl_ReadProperty(
4448 This->parentStorage,
4449 This->ownerPropertyIndex,
4450 &chainProperty);
4452 if (readSuccessful)
4454 return chainProperty.startingBlock;
4458 return BLOCK_END_OF_CHAIN;
4461 /******************************************************************************
4462 * BlockChainStream_GetCount
4464 * Returns the number of blocks that comprises this chain.
4465 * This is not the size of the stream as the last block may not be full!
4468 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4470 ULONG blockIndex;
4471 ULONG count = 0;
4473 blockIndex = BlockChainStream_GetHeadOfChain(This);
4475 while (blockIndex != BLOCK_END_OF_CHAIN)
4477 count++;
4479 if(FAILED(StorageImpl_GetNextBlockInChain(
4480 This->parentStorage,
4481 blockIndex,
4482 &blockIndex)))
4483 return 0;
4486 return count;
4489 /******************************************************************************
4490 * BlockChainStream_ReadAt
4492 * Reads a specified number of bytes from this chain at the specified offset.
4493 * bytesRead may be NULL.
4494 * Failure will be returned if the specified number of bytes has not been read.
4496 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4497 ULARGE_INTEGER offset,
4498 ULONG size,
4499 void* buffer,
4500 ULONG* bytesRead)
4502 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4503 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4504 ULONG bytesToReadInBuffer;
4505 ULONG blockIndex;
4506 BYTE* bufferWalker;
4508 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4511 * Find the first block in the stream that contains part of the buffer.
4513 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4514 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4515 (blockNoInSequence < This->lastBlockNoInSequence) )
4517 blockIndex = BlockChainStream_GetHeadOfChain(This);
4518 This->lastBlockNoInSequence = blockNoInSequence;
4520 else
4522 ULONG temp = blockNoInSequence;
4524 blockIndex = This->lastBlockNoInSequenceIndex;
4525 blockNoInSequence -= This->lastBlockNoInSequence;
4526 This->lastBlockNoInSequence = temp;
4529 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4531 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4532 return STG_E_DOCFILECORRUPT;
4533 blockNoInSequence--;
4536 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4537 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4539 This->lastBlockNoInSequenceIndex = blockIndex;
4542 * Start reading the buffer.
4544 *bytesRead = 0;
4545 bufferWalker = buffer;
4547 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4549 ULARGE_INTEGER ulOffset;
4550 DWORD bytesReadAt;
4552 * Calculate how many bytes we can copy from this big block.
4554 bytesToReadInBuffer =
4555 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4557 TRACE("block %i\n",blockIndex);
4558 ulOffset.u.HighPart = 0;
4559 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4560 offsetInBlock;
4562 StorageImpl_ReadAt(This->parentStorage,
4563 ulOffset,
4564 bufferWalker,
4565 bytesToReadInBuffer,
4566 &bytesReadAt);
4568 * Step to the next big block.
4570 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4571 return STG_E_DOCFILECORRUPT;
4573 bufferWalker += bytesReadAt;
4574 size -= bytesReadAt;
4575 *bytesRead += bytesReadAt;
4576 offsetInBlock = 0; /* There is no offset on the next block */
4578 if (bytesToReadInBuffer != bytesReadAt)
4579 break;
4582 return (size == 0) ? S_OK : STG_E_READFAULT;
4585 /******************************************************************************
4586 * BlockChainStream_WriteAt
4588 * Writes the specified number of bytes to this chain at the specified offset.
4589 * bytesWritten may be NULL.
4590 * Will fail if not all specified number of bytes have been written.
4592 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4593 ULARGE_INTEGER offset,
4594 ULONG size,
4595 const void* buffer,
4596 ULONG* bytesWritten)
4598 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4599 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4600 ULONG bytesToWrite;
4601 ULONG blockIndex;
4602 const BYTE* bufferWalker;
4605 * Find the first block in the stream that contains part of the buffer.
4607 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4608 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4609 (blockNoInSequence < This->lastBlockNoInSequence) )
4611 blockIndex = BlockChainStream_GetHeadOfChain(This);
4612 This->lastBlockNoInSequence = blockNoInSequence;
4614 else
4616 ULONG temp = blockNoInSequence;
4618 blockIndex = This->lastBlockNoInSequenceIndex;
4619 blockNoInSequence -= This->lastBlockNoInSequence;
4620 This->lastBlockNoInSequence = temp;
4623 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4625 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4626 &blockIndex)))
4627 return STG_E_DOCFILECORRUPT;
4628 blockNoInSequence--;
4631 This->lastBlockNoInSequenceIndex = blockIndex;
4633 /* BlockChainStream_SetSize should have already been called to ensure we have
4634 * enough blocks in the chain to write into */
4635 if (blockIndex == BLOCK_END_OF_CHAIN)
4637 ERR("not enough blocks in chain to write data\n");
4638 return STG_E_DOCFILECORRUPT;
4641 *bytesWritten = 0;
4642 bufferWalker = buffer;
4644 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4646 ULARGE_INTEGER ulOffset;
4647 DWORD bytesWrittenAt;
4649 * Calculate how many bytes we can copy from this big block.
4651 bytesToWrite =
4652 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4654 TRACE("block %i\n",blockIndex);
4655 ulOffset.u.HighPart = 0;
4656 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4657 offsetInBlock;
4659 StorageImpl_WriteAt(This->parentStorage,
4660 ulOffset,
4661 bufferWalker,
4662 bytesToWrite,
4663 &bytesWrittenAt);
4666 * Step to the next big block.
4668 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4669 &blockIndex)))
4670 return STG_E_DOCFILECORRUPT;
4672 bufferWalker += bytesWrittenAt;
4673 size -= bytesWrittenAt;
4674 *bytesWritten += bytesWrittenAt;
4675 offsetInBlock = 0; /* There is no offset on the next block */
4677 if (bytesWrittenAt != bytesToWrite)
4678 break;
4681 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4684 /******************************************************************************
4685 * BlockChainStream_Shrink
4687 * Shrinks this chain in the big block depot.
4689 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4690 ULARGE_INTEGER newSize)
4692 ULONG blockIndex, extraBlock;
4693 ULONG numBlocks;
4694 ULONG count = 1;
4697 * Reset the last accessed block cache.
4699 This->lastBlockNoInSequence = 0xFFFFFFFF;
4700 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4703 * Figure out how many blocks are needed to contain the new size
4705 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4707 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4708 numBlocks++;
4710 blockIndex = BlockChainStream_GetHeadOfChain(This);
4713 * Go to the new end of chain
4715 while (count < numBlocks)
4717 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4718 &blockIndex)))
4719 return FALSE;
4720 count++;
4723 /* Get the next block before marking the new end */
4724 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4725 &extraBlock)))
4726 return FALSE;
4728 /* Mark the new end of chain */
4729 StorageImpl_SetNextBlockInChain(
4730 This->parentStorage,
4731 blockIndex,
4732 BLOCK_END_OF_CHAIN);
4734 This->tailIndex = blockIndex;
4735 This->numBlocks = numBlocks;
4738 * Mark the extra blocks as free
4740 while (extraBlock != BLOCK_END_OF_CHAIN)
4742 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4743 &blockIndex)))
4744 return FALSE;
4745 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4746 extraBlock = blockIndex;
4749 return TRUE;
4752 /******************************************************************************
4753 * BlockChainStream_Enlarge
4755 * Grows this chain in the big block depot.
4757 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4758 ULARGE_INTEGER newSize)
4760 ULONG blockIndex, currentBlock;
4761 ULONG newNumBlocks;
4762 ULONG oldNumBlocks = 0;
4764 blockIndex = BlockChainStream_GetHeadOfChain(This);
4767 * Empty chain. Create the head.
4769 if (blockIndex == BLOCK_END_OF_CHAIN)
4771 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4772 StorageImpl_SetNextBlockInChain(This->parentStorage,
4773 blockIndex,
4774 BLOCK_END_OF_CHAIN);
4776 if (This->headOfStreamPlaceHolder != 0)
4778 *(This->headOfStreamPlaceHolder) = blockIndex;
4780 else
4782 StgProperty chainProp;
4783 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4785 StorageImpl_ReadProperty(
4786 This->parentStorage,
4787 This->ownerPropertyIndex,
4788 &chainProp);
4790 chainProp.startingBlock = blockIndex;
4792 StorageImpl_WriteProperty(
4793 This->parentStorage,
4794 This->ownerPropertyIndex,
4795 &chainProp);
4798 This->tailIndex = blockIndex;
4799 This->numBlocks = 1;
4803 * Figure out how many blocks are needed to contain this stream
4805 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4807 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4808 newNumBlocks++;
4811 * Go to the current end of chain
4813 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4815 currentBlock = blockIndex;
4817 while (blockIndex != BLOCK_END_OF_CHAIN)
4819 This->numBlocks++;
4820 currentBlock = blockIndex;
4822 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4823 &blockIndex)))
4824 return FALSE;
4827 This->tailIndex = currentBlock;
4830 currentBlock = This->tailIndex;
4831 oldNumBlocks = This->numBlocks;
4834 * Add new blocks to the chain
4836 if (oldNumBlocks < newNumBlocks)
4838 while (oldNumBlocks < newNumBlocks)
4840 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4842 StorageImpl_SetNextBlockInChain(
4843 This->parentStorage,
4844 currentBlock,
4845 blockIndex);
4847 StorageImpl_SetNextBlockInChain(
4848 This->parentStorage,
4849 blockIndex,
4850 BLOCK_END_OF_CHAIN);
4852 currentBlock = blockIndex;
4853 oldNumBlocks++;
4856 This->tailIndex = blockIndex;
4857 This->numBlocks = newNumBlocks;
4860 return TRUE;
4863 /******************************************************************************
4864 * BlockChainStream_SetSize
4866 * Sets the size of this stream. The big block depot will be updated.
4867 * The file will grow if we grow the chain.
4869 * TODO: Free the actual blocks in the file when we shrink the chain.
4870 * Currently, the blocks are still in the file. So the file size
4871 * doesn't shrink even if we shrink streams.
4873 BOOL BlockChainStream_SetSize(
4874 BlockChainStream* This,
4875 ULARGE_INTEGER newSize)
4877 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4879 if (newSize.u.LowPart == size.u.LowPart)
4880 return TRUE;
4882 if (newSize.u.LowPart < size.u.LowPart)
4884 BlockChainStream_Shrink(This, newSize);
4886 else
4888 BlockChainStream_Enlarge(This, newSize);
4891 return TRUE;
4894 /******************************************************************************
4895 * BlockChainStream_GetSize
4897 * Returns the size of this chain.
4898 * Will return the block count if this chain doesn't have a property.
4900 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4902 StgProperty chainProperty;
4904 if(This->headOfStreamPlaceHolder == NULL)
4907 * This chain is a data stream read the property and return
4908 * the appropriate size
4910 StorageImpl_ReadProperty(
4911 This->parentStorage,
4912 This->ownerPropertyIndex,
4913 &chainProperty);
4915 return chainProperty.size;
4917 else
4920 * this chain is a chain that does not have a property, figure out the
4921 * size by making the product number of used blocks times the
4922 * size of them
4924 ULARGE_INTEGER result;
4925 result.u.HighPart = 0;
4927 result.u.LowPart =
4928 BlockChainStream_GetCount(This) *
4929 This->parentStorage->bigBlockSize;
4931 return result;
4935 /******************************************************************************
4936 ** SmallBlockChainStream implementation
4939 SmallBlockChainStream* SmallBlockChainStream_Construct(
4940 StorageImpl* parentStorage,
4941 ULONG propertyIndex)
4943 SmallBlockChainStream* newStream;
4945 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4947 newStream->parentStorage = parentStorage;
4948 newStream->ownerPropertyIndex = propertyIndex;
4950 return newStream;
4953 void SmallBlockChainStream_Destroy(
4954 SmallBlockChainStream* This)
4956 HeapFree(GetProcessHeap(), 0, This);
4959 /******************************************************************************
4960 * SmallBlockChainStream_GetHeadOfChain
4962 * Returns the head of this chain of small blocks.
4964 static ULONG SmallBlockChainStream_GetHeadOfChain(
4965 SmallBlockChainStream* This)
4967 StgProperty chainProperty;
4968 BOOL readSuccessful;
4970 if (This->ownerPropertyIndex)
4972 readSuccessful = StorageImpl_ReadProperty(
4973 This->parentStorage,
4974 This->ownerPropertyIndex,
4975 &chainProperty);
4977 if (readSuccessful)
4979 return chainProperty.startingBlock;
4984 return BLOCK_END_OF_CHAIN;
4987 /******************************************************************************
4988 * SmallBlockChainStream_GetNextBlockInChain
4990 * Returns the index of the next small block in this chain.
4992 * Return Values:
4993 * - BLOCK_END_OF_CHAIN: end of this chain
4994 * - BLOCK_UNUSED: small block 'blockIndex' is free
4996 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4997 SmallBlockChainStream* This,
4998 ULONG blockIndex,
4999 ULONG* nextBlockInChain)
5001 ULARGE_INTEGER offsetOfBlockInDepot;
5002 DWORD buffer;
5003 ULONG bytesRead;
5004 HRESULT res;
5006 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5008 offsetOfBlockInDepot.u.HighPart = 0;
5009 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5012 * Read those bytes in the buffer from the small block file.
5014 res = BlockChainStream_ReadAt(
5015 This->parentStorage->smallBlockDepotChain,
5016 offsetOfBlockInDepot,
5017 sizeof(DWORD),
5018 &buffer,
5019 &bytesRead);
5021 if (SUCCEEDED(res))
5023 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5024 return S_OK;
5027 return res;
5030 /******************************************************************************
5031 * SmallBlockChainStream_SetNextBlockInChain
5033 * Writes the index of the next block of the specified block in the small
5034 * block depot.
5035 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5036 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5038 static void SmallBlockChainStream_SetNextBlockInChain(
5039 SmallBlockChainStream* This,
5040 ULONG blockIndex,
5041 ULONG nextBlock)
5043 ULARGE_INTEGER offsetOfBlockInDepot;
5044 DWORD buffer;
5045 ULONG bytesWritten;
5047 offsetOfBlockInDepot.u.HighPart = 0;
5048 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5050 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5053 * Read those bytes in the buffer from the small block file.
5055 BlockChainStream_WriteAt(
5056 This->parentStorage->smallBlockDepotChain,
5057 offsetOfBlockInDepot,
5058 sizeof(DWORD),
5059 &buffer,
5060 &bytesWritten);
5063 /******************************************************************************
5064 * SmallBlockChainStream_FreeBlock
5066 * Flag small block 'blockIndex' as free in the small block depot.
5068 static void SmallBlockChainStream_FreeBlock(
5069 SmallBlockChainStream* This,
5070 ULONG blockIndex)
5072 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5075 /******************************************************************************
5076 * SmallBlockChainStream_GetNextFreeBlock
5078 * Returns the index of a free small block. The small block depot will be
5079 * enlarged if necessary. The small block chain will also be enlarged if
5080 * necessary.
5082 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5083 SmallBlockChainStream* This)
5085 ULARGE_INTEGER offsetOfBlockInDepot;
5086 DWORD buffer;
5087 ULONG bytesRead;
5088 ULONG blockIndex = 0;
5089 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5090 HRESULT res = S_OK;
5091 ULONG smallBlocksPerBigBlock;
5093 offsetOfBlockInDepot.u.HighPart = 0;
5096 * Scan the small block depot for a free block
5098 while (nextBlockIndex != BLOCK_UNUSED)
5100 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5102 res = BlockChainStream_ReadAt(
5103 This->parentStorage->smallBlockDepotChain,
5104 offsetOfBlockInDepot,
5105 sizeof(DWORD),
5106 &buffer,
5107 &bytesRead);
5110 * If we run out of space for the small block depot, enlarge it
5112 if (SUCCEEDED(res))
5114 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5116 if (nextBlockIndex != BLOCK_UNUSED)
5117 blockIndex++;
5119 else
5121 ULONG count =
5122 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5124 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5125 ULONG nextBlock, newsbdIndex;
5126 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5128 nextBlock = sbdIndex;
5129 while (nextBlock != BLOCK_END_OF_CHAIN)
5131 sbdIndex = nextBlock;
5132 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5135 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5136 if (sbdIndex != BLOCK_END_OF_CHAIN)
5137 StorageImpl_SetNextBlockInChain(
5138 This->parentStorage,
5139 sbdIndex,
5140 newsbdIndex);
5142 StorageImpl_SetNextBlockInChain(
5143 This->parentStorage,
5144 newsbdIndex,
5145 BLOCK_END_OF_CHAIN);
5148 * Initialize all the small blocks to free
5150 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5151 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5153 if (count == 0)
5156 * We have just created the small block depot.
5158 StgProperty rootProp;
5159 ULONG sbStartIndex;
5162 * Save it in the header
5164 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5165 StorageImpl_SaveFileHeader(This->parentStorage);
5168 * And allocate the first big block that will contain small blocks
5170 sbStartIndex =
5171 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5173 StorageImpl_SetNextBlockInChain(
5174 This->parentStorage,
5175 sbStartIndex,
5176 BLOCK_END_OF_CHAIN);
5178 StorageImpl_ReadProperty(
5179 This->parentStorage,
5180 This->parentStorage->base.rootPropertySetIndex,
5181 &rootProp);
5183 rootProp.startingBlock = sbStartIndex;
5184 rootProp.size.u.HighPart = 0;
5185 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5187 StorageImpl_WriteProperty(
5188 This->parentStorage,
5189 This->parentStorage->base.rootPropertySetIndex,
5190 &rootProp);
5192 else
5193 StorageImpl_SaveFileHeader(This->parentStorage);
5197 smallBlocksPerBigBlock =
5198 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5201 * Verify if we have to allocate big blocks to contain small blocks
5203 if (blockIndex % smallBlocksPerBigBlock == 0)
5205 StgProperty rootProp;
5206 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5208 StorageImpl_ReadProperty(
5209 This->parentStorage,
5210 This->parentStorage->base.rootPropertySetIndex,
5211 &rootProp);
5213 if (rootProp.size.u.LowPart <
5214 (blocksRequired * This->parentStorage->bigBlockSize))
5216 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5218 BlockChainStream_SetSize(
5219 This->parentStorage->smallBlockRootChain,
5220 rootProp.size);
5222 StorageImpl_WriteProperty(
5223 This->parentStorage,
5224 This->parentStorage->base.rootPropertySetIndex,
5225 &rootProp);
5229 return blockIndex;
5232 /******************************************************************************
5233 * SmallBlockChainStream_ReadAt
5235 * Reads a specified number of bytes from this chain at the specified offset.
5236 * bytesRead may be NULL.
5237 * Failure will be returned if the specified number of bytes has not been read.
5239 HRESULT SmallBlockChainStream_ReadAt(
5240 SmallBlockChainStream* This,
5241 ULARGE_INTEGER offset,
5242 ULONG size,
5243 void* buffer,
5244 ULONG* bytesRead)
5246 HRESULT rc = S_OK;
5247 ULARGE_INTEGER offsetInBigBlockFile;
5248 ULONG blockNoInSequence =
5249 offset.u.LowPart / This->parentStorage->smallBlockSize;
5251 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5252 ULONG bytesToReadInBuffer;
5253 ULONG blockIndex;
5254 ULONG bytesReadFromBigBlockFile;
5255 BYTE* bufferWalker;
5258 * This should never happen on a small block file.
5260 assert(offset.u.HighPart==0);
5263 * Find the first block in the stream that contains part of the buffer.
5265 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5267 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5269 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5270 if(FAILED(rc))
5271 return rc;
5272 blockNoInSequence--;
5276 * Start reading the buffer.
5278 *bytesRead = 0;
5279 bufferWalker = buffer;
5281 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5284 * Calculate how many bytes we can copy from this small block.
5286 bytesToReadInBuffer =
5287 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5290 * Calculate the offset of the small block in the small block file.
5292 offsetInBigBlockFile.u.HighPart = 0;
5293 offsetInBigBlockFile.u.LowPart =
5294 blockIndex * This->parentStorage->smallBlockSize;
5296 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5299 * Read those bytes in the buffer from the small block file.
5300 * The small block has already been identified so it shouldn't fail
5301 * unless the file is corrupt.
5303 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5304 offsetInBigBlockFile,
5305 bytesToReadInBuffer,
5306 bufferWalker,
5307 &bytesReadFromBigBlockFile);
5309 if (FAILED(rc))
5310 return rc;
5313 * Step to the next big block.
5315 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5316 if(FAILED(rc))
5317 return STG_E_DOCFILECORRUPT;
5319 bufferWalker += bytesReadFromBigBlockFile;
5320 size -= bytesReadFromBigBlockFile;
5321 *bytesRead += bytesReadFromBigBlockFile;
5322 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5325 return (size == 0) ? S_OK : STG_E_READFAULT;
5328 /******************************************************************************
5329 * SmallBlockChainStream_WriteAt
5331 * Writes the specified number of bytes to this chain at the specified offset.
5332 * bytesWritten may be NULL.
5333 * Will fail if not all specified number of bytes have been written.
5335 HRESULT SmallBlockChainStream_WriteAt(
5336 SmallBlockChainStream* This,
5337 ULARGE_INTEGER offset,
5338 ULONG size,
5339 const void* buffer,
5340 ULONG* bytesWritten)
5342 ULARGE_INTEGER offsetInBigBlockFile;
5343 ULONG blockNoInSequence =
5344 offset.u.LowPart / This->parentStorage->smallBlockSize;
5346 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5347 ULONG bytesToWriteInBuffer;
5348 ULONG blockIndex;
5349 ULONG bytesWrittenToBigBlockFile;
5350 const BYTE* bufferWalker;
5351 HRESULT res;
5354 * This should never happen on a small block file.
5356 assert(offset.u.HighPart==0);
5359 * Find the first block in the stream that contains part of the buffer.
5361 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5363 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5365 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5366 return STG_E_DOCFILECORRUPT;
5367 blockNoInSequence--;
5371 * Start writing the buffer.
5373 * Here, I'm casting away the constness on the buffer variable
5374 * This is OK since we don't intend to modify that buffer.
5376 *bytesWritten = 0;
5377 bufferWalker = buffer;
5378 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5381 * Calculate how many bytes we can copy to this small block.
5383 bytesToWriteInBuffer =
5384 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5387 * Calculate the offset of the small block in the small block file.
5389 offsetInBigBlockFile.u.HighPart = 0;
5390 offsetInBigBlockFile.u.LowPart =
5391 blockIndex * This->parentStorage->smallBlockSize;
5393 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5396 * Write those bytes in the buffer to the small block file.
5398 res = BlockChainStream_WriteAt(
5399 This->parentStorage->smallBlockRootChain,
5400 offsetInBigBlockFile,
5401 bytesToWriteInBuffer,
5402 bufferWalker,
5403 &bytesWrittenToBigBlockFile);
5404 if (FAILED(res))
5405 return res;
5408 * Step to the next big block.
5410 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5411 &blockIndex)))
5412 return FALSE;
5413 bufferWalker += bytesWrittenToBigBlockFile;
5414 size -= bytesWrittenToBigBlockFile;
5415 *bytesWritten += bytesWrittenToBigBlockFile;
5416 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5419 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5422 /******************************************************************************
5423 * SmallBlockChainStream_Shrink
5425 * Shrinks this chain in the small block depot.
5427 static BOOL SmallBlockChainStream_Shrink(
5428 SmallBlockChainStream* This,
5429 ULARGE_INTEGER newSize)
5431 ULONG blockIndex, extraBlock;
5432 ULONG numBlocks;
5433 ULONG count = 0;
5435 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5437 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5438 numBlocks++;
5440 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5443 * Go to the new end of chain
5445 while (count < numBlocks)
5447 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5448 &blockIndex)))
5449 return FALSE;
5450 count++;
5454 * If the count is 0, we have a special case, the head of the chain was
5455 * just freed.
5457 if (count == 0)
5459 StgProperty chainProp;
5461 StorageImpl_ReadProperty(This->parentStorage,
5462 This->ownerPropertyIndex,
5463 &chainProp);
5465 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5467 StorageImpl_WriteProperty(This->parentStorage,
5468 This->ownerPropertyIndex,
5469 &chainProp);
5472 * We start freeing the chain at the head block.
5474 extraBlock = blockIndex;
5476 else
5478 /* Get the next block before marking the new end */
5479 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5480 &extraBlock)))
5481 return FALSE;
5483 /* Mark the new end of chain */
5484 SmallBlockChainStream_SetNextBlockInChain(
5485 This,
5486 blockIndex,
5487 BLOCK_END_OF_CHAIN);
5491 * Mark the extra blocks as free
5493 while (extraBlock != BLOCK_END_OF_CHAIN)
5495 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5496 &blockIndex)))
5497 return FALSE;
5498 SmallBlockChainStream_FreeBlock(This, extraBlock);
5499 extraBlock = blockIndex;
5502 return TRUE;
5505 /******************************************************************************
5506 * SmallBlockChainStream_Enlarge
5508 * Grows this chain in the small block depot.
5510 static BOOL SmallBlockChainStream_Enlarge(
5511 SmallBlockChainStream* This,
5512 ULARGE_INTEGER newSize)
5514 ULONG blockIndex, currentBlock;
5515 ULONG newNumBlocks;
5516 ULONG oldNumBlocks = 0;
5518 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5521 * Empty chain
5523 if (blockIndex == BLOCK_END_OF_CHAIN)
5526 StgProperty chainProp;
5528 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5529 &chainProp);
5531 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5533 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5534 &chainProp);
5536 blockIndex = chainProp.startingBlock;
5537 SmallBlockChainStream_SetNextBlockInChain(
5538 This,
5539 blockIndex,
5540 BLOCK_END_OF_CHAIN);
5543 currentBlock = blockIndex;
5546 * Figure out how many blocks are needed to contain this stream
5548 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5550 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5551 newNumBlocks++;
5554 * Go to the current end of chain
5556 while (blockIndex != BLOCK_END_OF_CHAIN)
5558 oldNumBlocks++;
5559 currentBlock = blockIndex;
5560 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5561 return FALSE;
5565 * Add new blocks to the chain
5567 while (oldNumBlocks < newNumBlocks)
5569 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5570 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5572 SmallBlockChainStream_SetNextBlockInChain(
5573 This,
5574 blockIndex,
5575 BLOCK_END_OF_CHAIN);
5577 currentBlock = blockIndex;
5578 oldNumBlocks++;
5581 return TRUE;
5584 /******************************************************************************
5585 * SmallBlockChainStream_SetSize
5587 * Sets the size of this stream.
5588 * The file will grow if we grow the chain.
5590 * TODO: Free the actual blocks in the file when we shrink the chain.
5591 * Currently, the blocks are still in the file. So the file size
5592 * doesn't shrink even if we shrink streams.
5594 BOOL SmallBlockChainStream_SetSize(
5595 SmallBlockChainStream* This,
5596 ULARGE_INTEGER newSize)
5598 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5600 if (newSize.u.LowPart == size.u.LowPart)
5601 return TRUE;
5603 if (newSize.u.LowPart < size.u.LowPart)
5605 SmallBlockChainStream_Shrink(This, newSize);
5607 else
5609 SmallBlockChainStream_Enlarge(This, newSize);
5612 return TRUE;
5615 /******************************************************************************
5616 * SmallBlockChainStream_GetSize
5618 * Returns the size of this chain.
5620 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5622 StgProperty chainProperty;
5624 StorageImpl_ReadProperty(
5625 This->parentStorage,
5626 This->ownerPropertyIndex,
5627 &chainProperty);
5629 return chainProperty.size;
5632 /******************************************************************************
5633 * StgCreateDocfile [OLE32.@]
5634 * Creates a new compound file storage object
5636 * PARAMS
5637 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5638 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5639 * reserved [ ?] unused?, usually 0
5640 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5642 * RETURNS
5643 * S_OK if the file was successfully created
5644 * some STG_E_ value if error
5645 * NOTES
5646 * if pwcsName is NULL, create file with new unique name
5647 * the function can returns
5648 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5649 * (unrealized now)
5651 HRESULT WINAPI StgCreateDocfile(
5652 LPCOLESTR pwcsName,
5653 DWORD grfMode,
5654 DWORD reserved,
5655 IStorage **ppstgOpen)
5657 StorageImpl* newStorage = 0;
5658 HANDLE hFile = INVALID_HANDLE_VALUE;
5659 HRESULT hr = STG_E_INVALIDFLAG;
5660 DWORD shareMode;
5661 DWORD accessMode;
5662 DWORD creationMode;
5663 DWORD fileAttributes;
5664 WCHAR tempFileName[MAX_PATH];
5666 TRACE("(%s, %x, %d, %p)\n",
5667 debugstr_w(pwcsName), grfMode,
5668 reserved, ppstgOpen);
5671 * Validate the parameters
5673 if (ppstgOpen == 0)
5674 return STG_E_INVALIDPOINTER;
5675 if (reserved != 0)
5676 return STG_E_INVALIDPARAMETER;
5678 /* if no share mode given then DENY_NONE is the default */
5679 if (STGM_SHARE_MODE(grfMode) == 0)
5680 grfMode |= STGM_SHARE_DENY_NONE;
5683 * Validate the STGM flags
5685 if ( FAILED( validateSTGM(grfMode) ))
5686 goto end;
5688 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5689 switch(STGM_ACCESS_MODE(grfMode))
5691 case STGM_WRITE:
5692 case STGM_READWRITE:
5693 break;
5694 default:
5695 goto end;
5698 /* in direct mode, can only use SHARE_EXCLUSIVE */
5699 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5700 goto end;
5702 /* but in transacted mode, any share mode is valid */
5705 * Generate a unique name.
5707 if (pwcsName == 0)
5709 WCHAR tempPath[MAX_PATH];
5710 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5712 memset(tempPath, 0, sizeof(tempPath));
5713 memset(tempFileName, 0, sizeof(tempFileName));
5715 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5716 tempPath[0] = '.';
5718 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5719 pwcsName = tempFileName;
5720 else
5722 hr = STG_E_INSUFFICIENTMEMORY;
5723 goto end;
5726 creationMode = TRUNCATE_EXISTING;
5728 else
5730 creationMode = GetCreationModeFromSTGM(grfMode);
5734 * Interpret the STGM value grfMode
5736 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5737 accessMode = GetAccessModeFromSTGM(grfMode);
5739 if (grfMode & STGM_DELETEONRELEASE)
5740 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5741 else
5742 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5744 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5745 FIXME("Storage share mode not implemented.\n");
5747 if (grfMode & STGM_TRANSACTED)
5748 FIXME("Transacted mode not implemented.\n");
5751 * Initialize the "out" parameter.
5753 *ppstgOpen = 0;
5755 hFile = CreateFileW(pwcsName,
5756 accessMode,
5757 shareMode,
5758 NULL,
5759 creationMode,
5760 fileAttributes,
5763 if (hFile == INVALID_HANDLE_VALUE)
5765 if(GetLastError() == ERROR_FILE_EXISTS)
5766 hr = STG_E_FILEALREADYEXISTS;
5767 else
5768 hr = E_FAIL;
5769 goto end;
5773 * Allocate and initialize the new IStorage32object.
5775 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5777 if (newStorage == 0)
5779 hr = STG_E_INSUFFICIENTMEMORY;
5780 goto end;
5783 hr = StorageImpl_Construct(
5784 newStorage,
5785 hFile,
5786 pwcsName,
5787 NULL,
5788 grfMode,
5789 TRUE,
5790 TRUE);
5792 if (FAILED(hr))
5794 HeapFree(GetProcessHeap(), 0, newStorage);
5795 goto end;
5799 * Get an "out" pointer for the caller.
5801 hr = StorageBaseImpl_QueryInterface(
5802 (IStorage*)newStorage,
5803 (REFIID)&IID_IStorage,
5804 (void**)ppstgOpen);
5805 end:
5806 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5808 return hr;
5811 /******************************************************************************
5812 * StgCreateStorageEx [OLE32.@]
5814 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5816 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5817 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5819 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5821 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5822 return STG_E_INVALIDPARAMETER;
5825 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5827 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5828 return STG_E_INVALIDPARAMETER;
5831 if (stgfmt == STGFMT_FILE)
5833 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5834 return STG_E_INVALIDPARAMETER;
5837 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5839 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5840 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5843 ERR("Invalid stgfmt argument\n");
5844 return STG_E_INVALIDPARAMETER;
5847 /******************************************************************************
5848 * StgCreatePropSetStg [OLE32.@]
5850 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5851 IPropertySetStorage **ppPropSetStg)
5853 HRESULT hr;
5855 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5856 if (reserved)
5857 hr = STG_E_INVALIDPARAMETER;
5858 else
5859 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5860 (void**)ppPropSetStg);
5861 return hr;
5864 /******************************************************************************
5865 * StgOpenStorageEx [OLE32.@]
5867 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5869 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5870 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5872 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5874 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5875 return STG_E_INVALIDPARAMETER;
5878 switch (stgfmt)
5880 case STGFMT_FILE:
5881 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5882 return STG_E_INVALIDPARAMETER;
5884 case STGFMT_STORAGE:
5885 break;
5887 case STGFMT_DOCFILE:
5888 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5890 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5891 return STG_E_INVALIDPARAMETER;
5893 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5894 break;
5896 case STGFMT_ANY:
5897 WARN("STGFMT_ANY assuming storage\n");
5898 break;
5900 default:
5901 return STG_E_INVALIDPARAMETER;
5904 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5908 /******************************************************************************
5909 * StgOpenStorage [OLE32.@]
5911 HRESULT WINAPI StgOpenStorage(
5912 const OLECHAR *pwcsName,
5913 IStorage *pstgPriority,
5914 DWORD grfMode,
5915 SNB snbExclude,
5916 DWORD reserved,
5917 IStorage **ppstgOpen)
5919 StorageImpl* newStorage = 0;
5920 HRESULT hr = S_OK;
5921 HANDLE hFile = 0;
5922 DWORD shareMode;
5923 DWORD accessMode;
5924 WCHAR fullname[MAX_PATH];
5926 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5927 debugstr_w(pwcsName), pstgPriority, grfMode,
5928 snbExclude, reserved, ppstgOpen);
5931 * Perform sanity checks
5933 if (pwcsName == 0)
5935 hr = STG_E_INVALIDNAME;
5936 goto end;
5939 if (ppstgOpen == 0)
5941 hr = STG_E_INVALIDPOINTER;
5942 goto end;
5945 if (reserved)
5947 hr = STG_E_INVALIDPARAMETER;
5948 goto end;
5951 if (grfMode & STGM_PRIORITY)
5953 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5954 return STG_E_INVALIDFLAG;
5955 if (grfMode & STGM_DELETEONRELEASE)
5956 return STG_E_INVALIDFUNCTION;
5957 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5958 return STG_E_INVALIDFLAG;
5959 grfMode &= ~0xf0; /* remove the existing sharing mode */
5960 grfMode |= STGM_SHARE_DENY_NONE;
5962 /* STGM_PRIORITY stops other IStorage objects on the same file from
5963 * committing until the STGM_PRIORITY IStorage is closed. it also
5964 * stops non-transacted mode StgOpenStorage calls with write access from
5965 * succeeding. obviously, both of these cannot be achieved through just
5966 * file share flags */
5967 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5971 * Validate the sharing mode
5973 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5974 switch(STGM_SHARE_MODE(grfMode))
5976 case STGM_SHARE_EXCLUSIVE:
5977 case STGM_SHARE_DENY_WRITE:
5978 break;
5979 default:
5980 hr = STG_E_INVALIDFLAG;
5981 goto end;
5985 * Validate the STGM flags
5987 if ( FAILED( validateSTGM(grfMode) ) ||
5988 (grfMode&STGM_CREATE))
5990 hr = STG_E_INVALIDFLAG;
5991 goto end;
5994 /* shared reading requires transacted mode */
5995 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5996 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5997 !(grfMode&STGM_TRANSACTED) )
5999 hr = STG_E_INVALIDFLAG;
6000 goto end;
6004 * Interpret the STGM value grfMode
6006 shareMode = GetShareModeFromSTGM(grfMode);
6007 accessMode = GetAccessModeFromSTGM(grfMode);
6010 * Initialize the "out" parameter.
6012 *ppstgOpen = 0;
6014 hFile = CreateFileW( pwcsName,
6015 accessMode,
6016 shareMode,
6017 NULL,
6018 OPEN_EXISTING,
6019 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6022 if (hFile==INVALID_HANDLE_VALUE)
6024 DWORD last_error = GetLastError();
6026 hr = E_FAIL;
6028 switch (last_error)
6030 case ERROR_FILE_NOT_FOUND:
6031 hr = STG_E_FILENOTFOUND;
6032 break;
6034 case ERROR_PATH_NOT_FOUND:
6035 hr = STG_E_PATHNOTFOUND;
6036 break;
6038 case ERROR_ACCESS_DENIED:
6039 case ERROR_WRITE_PROTECT:
6040 hr = STG_E_ACCESSDENIED;
6041 break;
6043 case ERROR_SHARING_VIOLATION:
6044 hr = STG_E_SHAREVIOLATION;
6045 break;
6047 default:
6048 hr = E_FAIL;
6051 goto end;
6055 * Refuse to open the file if it's too small to be a structured storage file
6056 * FIXME: verify the file when reading instead of here
6058 if (GetFileSize(hFile, NULL) < 0x100)
6060 CloseHandle(hFile);
6061 hr = STG_E_FILEALREADYEXISTS;
6062 goto end;
6066 * Allocate and initialize the new IStorage32object.
6068 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6070 if (newStorage == 0)
6072 hr = STG_E_INSUFFICIENTMEMORY;
6073 goto end;
6076 /* Initialize the storage */
6077 hr = StorageImpl_Construct(
6078 newStorage,
6079 hFile,
6080 pwcsName,
6081 NULL,
6082 grfMode,
6083 TRUE,
6084 FALSE );
6086 if (FAILED(hr))
6088 HeapFree(GetProcessHeap(), 0, newStorage);
6090 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6092 if(hr == STG_E_INVALIDHEADER)
6093 hr = STG_E_FILEALREADYEXISTS;
6094 goto end;
6097 /* prepare the file name string given in lieu of the root property name */
6098 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6099 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6100 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6103 * Get an "out" pointer for the caller.
6105 hr = StorageBaseImpl_QueryInterface(
6106 (IStorage*)newStorage,
6107 (REFIID)&IID_IStorage,
6108 (void**)ppstgOpen);
6110 end:
6111 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6112 return hr;
6115 /******************************************************************************
6116 * StgCreateDocfileOnILockBytes [OLE32.@]
6118 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6119 ILockBytes *plkbyt,
6120 DWORD grfMode,
6121 DWORD reserved,
6122 IStorage** ppstgOpen)
6124 StorageImpl* newStorage = 0;
6125 HRESULT hr = S_OK;
6128 * Validate the parameters
6130 if ((ppstgOpen == 0) || (plkbyt == 0))
6131 return STG_E_INVALIDPOINTER;
6134 * Allocate and initialize the new IStorage object.
6136 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6138 if (newStorage == 0)
6139 return STG_E_INSUFFICIENTMEMORY;
6141 hr = StorageImpl_Construct(
6142 newStorage,
6145 plkbyt,
6146 grfMode,
6147 FALSE,
6148 TRUE);
6150 if (FAILED(hr))
6152 HeapFree(GetProcessHeap(), 0, newStorage);
6153 return hr;
6157 * Get an "out" pointer for the caller.
6159 hr = StorageBaseImpl_QueryInterface(
6160 (IStorage*)newStorage,
6161 (REFIID)&IID_IStorage,
6162 (void**)ppstgOpen);
6164 return hr;
6167 /******************************************************************************
6168 * StgOpenStorageOnILockBytes [OLE32.@]
6170 HRESULT WINAPI StgOpenStorageOnILockBytes(
6171 ILockBytes *plkbyt,
6172 IStorage *pstgPriority,
6173 DWORD grfMode,
6174 SNB snbExclude,
6175 DWORD reserved,
6176 IStorage **ppstgOpen)
6178 StorageImpl* newStorage = 0;
6179 HRESULT hr = S_OK;
6182 * Perform a sanity check
6184 if ((plkbyt == 0) || (ppstgOpen == 0))
6185 return STG_E_INVALIDPOINTER;
6188 * Validate the STGM flags
6190 if ( FAILED( validateSTGM(grfMode) ))
6191 return STG_E_INVALIDFLAG;
6194 * Initialize the "out" parameter.
6196 *ppstgOpen = 0;
6199 * Allocate and initialize the new IStorage object.
6201 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6203 if (newStorage == 0)
6204 return STG_E_INSUFFICIENTMEMORY;
6206 hr = StorageImpl_Construct(
6207 newStorage,
6210 plkbyt,
6211 grfMode,
6212 FALSE,
6213 FALSE);
6215 if (FAILED(hr))
6217 HeapFree(GetProcessHeap(), 0, newStorage);
6218 return hr;
6222 * Get an "out" pointer for the caller.
6224 hr = StorageBaseImpl_QueryInterface(
6225 (IStorage*)newStorage,
6226 (REFIID)&IID_IStorage,
6227 (void**)ppstgOpen);
6229 return hr;
6232 /******************************************************************************
6233 * StgSetTimes [ole32.@]
6234 * StgSetTimes [OLE32.@]
6238 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6239 FILETIME const *patime, FILETIME const *pmtime)
6241 IStorage *stg = NULL;
6242 HRESULT r;
6244 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6246 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6247 0, 0, &stg);
6248 if( SUCCEEDED(r) )
6250 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6251 IStorage_Release(stg);
6254 return r;
6257 /******************************************************************************
6258 * StgIsStorageILockBytes [OLE32.@]
6260 * Determines if the ILockBytes contains a storage object.
6262 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6264 BYTE sig[8];
6265 ULARGE_INTEGER offset;
6267 offset.u.HighPart = 0;
6268 offset.u.LowPart = 0;
6270 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6272 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6273 return S_OK;
6275 return S_FALSE;
6278 /******************************************************************************
6279 * WriteClassStg [OLE32.@]
6281 * This method will store the specified CLSID in the specified storage object
6283 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6285 HRESULT hRes;
6287 if(!pStg)
6288 return E_INVALIDARG;
6290 if(!rclsid)
6291 return STG_E_INVALIDPOINTER;
6293 hRes = IStorage_SetClass(pStg, rclsid);
6295 return hRes;
6298 /***********************************************************************
6299 * ReadClassStg (OLE32.@)
6301 * This method reads the CLSID previously written to a storage object with
6302 * the WriteClassStg.
6304 * PARAMS
6305 * pstg [I] IStorage pointer
6306 * pclsid [O] Pointer to where the CLSID is written
6308 * RETURNS
6309 * Success: S_OK.
6310 * Failure: HRESULT code.
6312 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6314 STATSTG pstatstg;
6315 HRESULT hRes;
6317 TRACE("(%p, %p)\n", pstg, pclsid);
6319 if(!pstg || !pclsid)
6320 return E_INVALIDARG;
6323 * read a STATSTG structure (contains the clsid) from the storage
6325 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6327 if(SUCCEEDED(hRes))
6328 *pclsid=pstatstg.clsid;
6330 return hRes;
6333 /***********************************************************************
6334 * OleLoadFromStream (OLE32.@)
6336 * This function loads an object from stream
6338 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6340 CLSID clsid;
6341 HRESULT res;
6342 LPPERSISTSTREAM xstm;
6344 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6346 res=ReadClassStm(pStm,&clsid);
6347 if (FAILED(res))
6348 return res;
6349 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6350 if (FAILED(res))
6351 return res;
6352 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6353 if (FAILED(res)) {
6354 IUnknown_Release((IUnknown*)*ppvObj);
6355 return res;
6357 res=IPersistStream_Load(xstm,pStm);
6358 IPersistStream_Release(xstm);
6359 /* FIXME: all refcounts ok at this point? I think they should be:
6360 * pStm : unchanged
6361 * ppvObj : 1
6362 * xstm : 0 (released)
6364 return res;
6367 /***********************************************************************
6368 * OleSaveToStream (OLE32.@)
6370 * This function saves an object with the IPersistStream interface on it
6371 * to the specified stream.
6373 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6376 CLSID clsid;
6377 HRESULT res;
6379 TRACE("(%p,%p)\n",pPStm,pStm);
6381 res=IPersistStream_GetClassID(pPStm,&clsid);
6383 if (SUCCEEDED(res)){
6385 res=WriteClassStm(pStm,&clsid);
6387 if (SUCCEEDED(res))
6389 res=IPersistStream_Save(pPStm,pStm,TRUE);
6392 TRACE("Finished Save\n");
6393 return res;
6396 /****************************************************************************
6397 * This method validate a STGM parameter that can contain the values below
6399 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6400 * The stgm values contained in 0xffff0000 are bitmasks.
6402 * STGM_DIRECT 0x00000000
6403 * STGM_TRANSACTED 0x00010000
6404 * STGM_SIMPLE 0x08000000
6406 * STGM_READ 0x00000000
6407 * STGM_WRITE 0x00000001
6408 * STGM_READWRITE 0x00000002
6410 * STGM_SHARE_DENY_NONE 0x00000040
6411 * STGM_SHARE_DENY_READ 0x00000030
6412 * STGM_SHARE_DENY_WRITE 0x00000020
6413 * STGM_SHARE_EXCLUSIVE 0x00000010
6415 * STGM_PRIORITY 0x00040000
6416 * STGM_DELETEONRELEASE 0x04000000
6418 * STGM_CREATE 0x00001000
6419 * STGM_CONVERT 0x00020000
6420 * STGM_FAILIFTHERE 0x00000000
6422 * STGM_NOSCRATCH 0x00100000
6423 * STGM_NOSNAPSHOT 0x00200000
6425 static HRESULT validateSTGM(DWORD stgm)
6427 DWORD access = STGM_ACCESS_MODE(stgm);
6428 DWORD share = STGM_SHARE_MODE(stgm);
6429 DWORD create = STGM_CREATE_MODE(stgm);
6431 if (stgm&~STGM_KNOWN_FLAGS)
6433 ERR("unknown flags %08x\n", stgm);
6434 return E_FAIL;
6437 switch (access)
6439 case STGM_READ:
6440 case STGM_WRITE:
6441 case STGM_READWRITE:
6442 break;
6443 default:
6444 return E_FAIL;
6447 switch (share)
6449 case STGM_SHARE_DENY_NONE:
6450 case STGM_SHARE_DENY_READ:
6451 case STGM_SHARE_DENY_WRITE:
6452 case STGM_SHARE_EXCLUSIVE:
6453 break;
6454 default:
6455 return E_FAIL;
6458 switch (create)
6460 case STGM_CREATE:
6461 case STGM_FAILIFTHERE:
6462 break;
6463 default:
6464 return E_FAIL;
6468 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6470 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6471 return E_FAIL;
6474 * STGM_CREATE | STGM_CONVERT
6475 * if both are false, STGM_FAILIFTHERE is set to TRUE
6477 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6478 return E_FAIL;
6481 * STGM_NOSCRATCH requires STGM_TRANSACTED
6483 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6484 return E_FAIL;
6487 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6488 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6490 if ( (stgm & STGM_NOSNAPSHOT) &&
6491 (!(stgm & STGM_TRANSACTED) ||
6492 share == STGM_SHARE_EXCLUSIVE ||
6493 share == STGM_SHARE_DENY_WRITE) )
6494 return E_FAIL;
6496 return S_OK;
6499 /****************************************************************************
6500 * GetShareModeFromSTGM
6502 * This method will return a share mode flag from a STGM value.
6503 * The STGM value is assumed valid.
6505 static DWORD GetShareModeFromSTGM(DWORD stgm)
6507 switch (STGM_SHARE_MODE(stgm))
6509 case STGM_SHARE_DENY_NONE:
6510 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6511 case STGM_SHARE_DENY_READ:
6512 return FILE_SHARE_WRITE;
6513 case STGM_SHARE_DENY_WRITE:
6514 return FILE_SHARE_READ;
6515 case STGM_SHARE_EXCLUSIVE:
6516 return 0;
6518 ERR("Invalid share mode!\n");
6519 assert(0);
6520 return 0;
6523 /****************************************************************************
6524 * GetAccessModeFromSTGM
6526 * This method will return an access mode flag from a STGM value.
6527 * The STGM value is assumed valid.
6529 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6531 switch (STGM_ACCESS_MODE(stgm))
6533 case STGM_READ:
6534 return GENERIC_READ;
6535 case STGM_WRITE:
6536 case STGM_READWRITE:
6537 return GENERIC_READ | GENERIC_WRITE;
6539 ERR("Invalid access mode!\n");
6540 assert(0);
6541 return 0;
6544 /****************************************************************************
6545 * GetCreationModeFromSTGM
6547 * This method will return a creation mode flag from a STGM value.
6548 * The STGM value is assumed valid.
6550 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6552 switch(STGM_CREATE_MODE(stgm))
6554 case STGM_CREATE:
6555 return CREATE_ALWAYS;
6556 case STGM_CONVERT:
6557 FIXME("STGM_CONVERT not implemented!\n");
6558 return CREATE_NEW;
6559 case STGM_FAILIFTHERE:
6560 return CREATE_NEW;
6562 ERR("Invalid create mode!\n");
6563 assert(0);
6564 return 0;
6568 /*************************************************************************
6569 * OLECONVERT_LoadOLE10 [Internal]
6571 * Loads the OLE10 STREAM to memory
6573 * PARAMS
6574 * pOleStream [I] The OLESTREAM
6575 * pData [I] Data Structure for the OLESTREAM Data
6577 * RETURNS
6578 * Success: S_OK
6579 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6580 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6582 * NOTES
6583 * This function is used by OleConvertOLESTREAMToIStorage only.
6585 * Memory allocated for pData must be freed by the caller
6587 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6589 DWORD dwSize;
6590 HRESULT hRes = S_OK;
6591 int nTryCnt=0;
6592 int max_try = 6;
6594 pData->pData = NULL;
6595 pData->pstrOleObjFileName = NULL;
6597 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6599 /* Get the OleID */
6600 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6601 if(dwSize != sizeof(pData->dwOleID))
6603 hRes = CONVERT10_E_OLESTREAM_GET;
6605 else if(pData->dwOleID != OLESTREAM_ID)
6607 hRes = CONVERT10_E_OLESTREAM_FMT;
6609 else
6611 hRes = S_OK;
6612 break;
6616 if(hRes == S_OK)
6618 /* Get the TypeID... more info needed for this field */
6619 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6620 if(dwSize != sizeof(pData->dwTypeID))
6622 hRes = CONVERT10_E_OLESTREAM_GET;
6625 if(hRes == S_OK)
6627 if(pData->dwTypeID != 0)
6629 /* Get the length of the OleTypeName */
6630 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6631 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6633 hRes = CONVERT10_E_OLESTREAM_GET;
6636 if(hRes == S_OK)
6638 if(pData->dwOleTypeNameLength > 0)
6640 /* Get the OleTypeName */
6641 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6642 if(dwSize != pData->dwOleTypeNameLength)
6644 hRes = CONVERT10_E_OLESTREAM_GET;
6648 if(bStrem1)
6650 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6651 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6653 hRes = CONVERT10_E_OLESTREAM_GET;
6655 if(hRes == S_OK)
6657 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6658 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6659 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6660 if(pData->pstrOleObjFileName)
6662 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6663 if(dwSize != pData->dwOleObjFileNameLength)
6665 hRes = CONVERT10_E_OLESTREAM_GET;
6668 else
6669 hRes = CONVERT10_E_OLESTREAM_GET;
6672 else
6674 /* Get the Width of the Metafile */
6675 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6676 if(dwSize != sizeof(pData->dwMetaFileWidth))
6678 hRes = CONVERT10_E_OLESTREAM_GET;
6680 if(hRes == S_OK)
6682 /* Get the Height of the Metafile */
6683 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6684 if(dwSize != sizeof(pData->dwMetaFileHeight))
6686 hRes = CONVERT10_E_OLESTREAM_GET;
6690 if(hRes == S_OK)
6692 /* Get the Length of the Data */
6693 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6694 if(dwSize != sizeof(pData->dwDataLength))
6696 hRes = CONVERT10_E_OLESTREAM_GET;
6700 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6702 if(!bStrem1) /* if it is a second OLE stream data */
6704 pData->dwDataLength -= 8;
6705 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6706 if(dwSize != sizeof(pData->strUnknown))
6708 hRes = CONVERT10_E_OLESTREAM_GET;
6712 if(hRes == S_OK)
6714 if(pData->dwDataLength > 0)
6716 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6718 /* Get Data (ex. IStorage, Metafile, or BMP) */
6719 if(pData->pData)
6721 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6722 if(dwSize != pData->dwDataLength)
6724 hRes = CONVERT10_E_OLESTREAM_GET;
6727 else
6729 hRes = CONVERT10_E_OLESTREAM_GET;
6735 return hRes;
6738 /*************************************************************************
6739 * OLECONVERT_SaveOLE10 [Internal]
6741 * Saves the OLE10 STREAM From memory
6743 * PARAMS
6744 * pData [I] Data Structure for the OLESTREAM Data
6745 * pOleStream [I] The OLESTREAM to save
6747 * RETURNS
6748 * Success: S_OK
6749 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6751 * NOTES
6752 * This function is used by OleConvertIStorageToOLESTREAM only.
6755 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6757 DWORD dwSize;
6758 HRESULT hRes = S_OK;
6761 /* Set the OleID */
6762 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6763 if(dwSize != sizeof(pData->dwOleID))
6765 hRes = CONVERT10_E_OLESTREAM_PUT;
6768 if(hRes == S_OK)
6770 /* Set the TypeID */
6771 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6772 if(dwSize != sizeof(pData->dwTypeID))
6774 hRes = CONVERT10_E_OLESTREAM_PUT;
6778 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6780 /* Set the Length of the OleTypeName */
6781 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6782 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6784 hRes = CONVERT10_E_OLESTREAM_PUT;
6787 if(hRes == S_OK)
6789 if(pData->dwOleTypeNameLength > 0)
6791 /* Set the OleTypeName */
6792 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6793 if(dwSize != pData->dwOleTypeNameLength)
6795 hRes = CONVERT10_E_OLESTREAM_PUT;
6800 if(hRes == S_OK)
6802 /* Set the width of the Metafile */
6803 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6804 if(dwSize != sizeof(pData->dwMetaFileWidth))
6806 hRes = CONVERT10_E_OLESTREAM_PUT;
6810 if(hRes == S_OK)
6812 /* Set the height of the Metafile */
6813 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6814 if(dwSize != sizeof(pData->dwMetaFileHeight))
6816 hRes = CONVERT10_E_OLESTREAM_PUT;
6820 if(hRes == S_OK)
6822 /* Set the length of the Data */
6823 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6824 if(dwSize != sizeof(pData->dwDataLength))
6826 hRes = CONVERT10_E_OLESTREAM_PUT;
6830 if(hRes == S_OK)
6832 if(pData->dwDataLength > 0)
6834 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6835 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6836 if(dwSize != pData->dwDataLength)
6838 hRes = CONVERT10_E_OLESTREAM_PUT;
6843 return hRes;
6846 /*************************************************************************
6847 * OLECONVERT_GetOLE20FromOLE10[Internal]
6849 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6850 * opens it, and copies the content to the dest IStorage for
6851 * OleConvertOLESTREAMToIStorage
6854 * PARAMS
6855 * pDestStorage [I] The IStorage to copy the data to
6856 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6857 * nBufferLength [I] The size of the buffer
6859 * RETURNS
6860 * Nothing
6862 * NOTES
6866 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6868 HRESULT hRes;
6869 HANDLE hFile;
6870 IStorage *pTempStorage;
6871 DWORD dwNumOfBytesWritten;
6872 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6873 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6875 /* Create a temp File */
6876 GetTempPathW(MAX_PATH, wstrTempDir);
6877 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6878 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6880 if(hFile != INVALID_HANDLE_VALUE)
6882 /* Write IStorage Data to File */
6883 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6884 CloseHandle(hFile);
6886 /* Open and copy temp storage to the Dest Storage */
6887 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6888 if(hRes == S_OK)
6890 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6891 StorageBaseImpl_Release(pTempStorage);
6893 DeleteFileW(wstrTempFile);
6898 /*************************************************************************
6899 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6901 * Saves the OLE10 STREAM From memory
6903 * PARAMS
6904 * pStorage [I] The Src IStorage to copy
6905 * pData [I] The Dest Memory to write to.
6907 * RETURNS
6908 * The size in bytes allocated for pData
6910 * NOTES
6911 * Memory allocated for pData must be freed by the caller
6913 * Used by OleConvertIStorageToOLESTREAM only.
6916 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6918 HANDLE hFile;
6919 HRESULT hRes;
6920 DWORD nDataLength = 0;
6921 IStorage *pTempStorage;
6922 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6923 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6925 *pData = NULL;
6927 /* Create temp Storage */
6928 GetTempPathW(MAX_PATH, wstrTempDir);
6929 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6930 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6932 if(hRes == S_OK)
6934 /* Copy Src Storage to the Temp Storage */
6935 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6936 StorageBaseImpl_Release(pTempStorage);
6938 /* Open Temp Storage as a file and copy to memory */
6939 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6940 if(hFile != INVALID_HANDLE_VALUE)
6942 nDataLength = GetFileSize(hFile, NULL);
6943 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6944 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6945 CloseHandle(hFile);
6947 DeleteFileW(wstrTempFile);
6949 return nDataLength;
6952 /*************************************************************************
6953 * OLECONVERT_CreateOleStream [Internal]
6955 * Creates the "\001OLE" stream in the IStorage if necessary.
6957 * PARAMS
6958 * pStorage [I] Dest storage to create the stream in
6960 * RETURNS
6961 * Nothing
6963 * NOTES
6964 * This function is used by OleConvertOLESTREAMToIStorage only.
6966 * This stream is still unknown, MS Word seems to have extra data
6967 * but since the data is stored in the OLESTREAM there should be
6968 * no need to recreate the stream. If the stream is manually
6969 * deleted it will create it with this default data.
6972 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6974 HRESULT hRes;
6975 IStream *pStream;
6976 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6977 BYTE pOleStreamHeader [] =
6979 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6980 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6981 0x00, 0x00, 0x00, 0x00
6984 /* Create stream if not present */
6985 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6986 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6988 if(hRes == S_OK)
6990 /* Write default Data */
6991 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6992 IStream_Release(pStream);
6996 /* write a string to a stream, preceded by its length */
6997 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6999 HRESULT r;
7000 LPSTR str;
7001 DWORD len = 0;
7003 if( string )
7004 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7005 r = IStream_Write( stm, &len, sizeof(len), NULL);
7006 if( FAILED( r ) )
7007 return r;
7008 if(len == 0)
7009 return r;
7010 str = CoTaskMemAlloc( len );
7011 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7012 r = IStream_Write( stm, str, len, NULL);
7013 CoTaskMemFree( str );
7014 return r;
7017 /* read a string preceded by its length from a stream */
7018 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7020 HRESULT r;
7021 DWORD len, count = 0;
7022 LPSTR str;
7023 LPWSTR wstr;
7025 r = IStream_Read( stm, &len, sizeof(len), &count );
7026 if( FAILED( r ) )
7027 return r;
7028 if( count != sizeof(len) )
7029 return E_OUTOFMEMORY;
7031 TRACE("%d bytes\n",len);
7033 str = CoTaskMemAlloc( len );
7034 if( !str )
7035 return E_OUTOFMEMORY;
7036 count = 0;
7037 r = IStream_Read( stm, str, len, &count );
7038 if( FAILED( r ) )
7039 return r;
7040 if( count != len )
7042 CoTaskMemFree( str );
7043 return E_OUTOFMEMORY;
7046 TRACE("Read string %s\n",debugstr_an(str,len));
7048 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7049 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7050 if( wstr )
7051 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7052 CoTaskMemFree( str );
7054 *string = wstr;
7056 return r;
7060 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7061 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7063 IStream *pstm;
7064 HRESULT r = S_OK;
7065 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7067 static const BYTE unknown1[12] =
7068 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7069 0xFF, 0xFF, 0xFF, 0xFF};
7070 static const BYTE unknown2[16] =
7071 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7072 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7074 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7075 debugstr_w(lpszUserType), debugstr_w(szClipName),
7076 debugstr_w(szProgIDName));
7078 /* Create a CompObj stream if it doesn't exist */
7079 r = IStorage_CreateStream(pstg, szwStreamName,
7080 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7081 if( FAILED (r) )
7082 return r;
7084 /* Write CompObj Structure to stream */
7085 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7087 if( SUCCEEDED( r ) )
7088 r = WriteClassStm( pstm, clsid );
7090 if( SUCCEEDED( r ) )
7091 r = STREAM_WriteString( pstm, lpszUserType );
7092 if( SUCCEEDED( r ) )
7093 r = STREAM_WriteString( pstm, szClipName );
7094 if( SUCCEEDED( r ) )
7095 r = STREAM_WriteString( pstm, szProgIDName );
7096 if( SUCCEEDED( r ) )
7097 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7099 IStream_Release( pstm );
7101 return r;
7104 /***********************************************************************
7105 * WriteFmtUserTypeStg (OLE32.@)
7107 HRESULT WINAPI WriteFmtUserTypeStg(
7108 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7110 HRESULT r;
7111 WCHAR szwClipName[0x40];
7112 CLSID clsid = CLSID_NULL;
7113 LPWSTR wstrProgID = NULL;
7114 DWORD n;
7116 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7118 /* get the clipboard format name */
7119 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7120 szwClipName[n]=0;
7122 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7124 /* FIXME: There's room to save a CLSID and its ProgID, but
7125 the CLSID is not looked up in the registry and in all the
7126 tests I wrote it was CLSID_NULL. Where does it come from?
7129 /* get the real program ID. This may fail, but that's fine */
7130 ProgIDFromCLSID(&clsid, &wstrProgID);
7132 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7134 r = STORAGE_WriteCompObj( pstg, &clsid,
7135 lpszUserType, szwClipName, wstrProgID );
7137 CoTaskMemFree(wstrProgID);
7139 return r;
7143 /******************************************************************************
7144 * ReadFmtUserTypeStg [OLE32.@]
7146 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7148 HRESULT r;
7149 IStream *stm = 0;
7150 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7151 unsigned char unknown1[12];
7152 unsigned char unknown2[16];
7153 DWORD count;
7154 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7155 CLSID clsid;
7157 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7159 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7160 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7161 if( FAILED ( r ) )
7163 WARN("Failed to open stream r = %08x\n", r);
7164 return r;
7167 /* read the various parts of the structure */
7168 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7169 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7170 goto end;
7171 r = ReadClassStm( stm, &clsid );
7172 if( FAILED( r ) )
7173 goto end;
7175 r = STREAM_ReadString( stm, &szCLSIDName );
7176 if( FAILED( r ) )
7177 goto end;
7179 r = STREAM_ReadString( stm, &szOleTypeName );
7180 if( FAILED( r ) )
7181 goto end;
7183 r = STREAM_ReadString( stm, &szProgIDName );
7184 if( FAILED( r ) )
7185 goto end;
7187 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7188 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7189 goto end;
7191 /* ok, success... now we just need to store what we found */
7192 if( pcf )
7193 *pcf = RegisterClipboardFormatW( szOleTypeName );
7194 CoTaskMemFree( szOleTypeName );
7196 if( lplpszUserType )
7197 *lplpszUserType = szCLSIDName;
7198 CoTaskMemFree( szProgIDName );
7200 end:
7201 IStream_Release( stm );
7203 return r;
7207 /*************************************************************************
7208 * OLECONVERT_CreateCompObjStream [Internal]
7210 * Creates a "\001CompObj" is the destination IStorage if necessary.
7212 * PARAMS
7213 * pStorage [I] The dest IStorage to create the CompObj Stream
7214 * if necessary.
7215 * strOleTypeName [I] The ProgID
7217 * RETURNS
7218 * Success: S_OK
7219 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7221 * NOTES
7222 * This function is used by OleConvertOLESTREAMToIStorage only.
7224 * The stream data is stored in the OLESTREAM and there should be
7225 * no need to recreate the stream. If the stream is manually
7226 * deleted it will attempt to create it by querying the registry.
7230 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7232 IStream *pStream;
7233 HRESULT hStorageRes, hRes = S_OK;
7234 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7235 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7236 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7238 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7239 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7241 /* Initialize the CompObj structure */
7242 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7243 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7244 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7247 /* Create a CompObj stream if it doesn't exist */
7248 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7249 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7250 if(hStorageRes == S_OK)
7252 /* copy the OleTypeName to the compobj struct */
7253 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7254 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7256 /* copy the OleTypeName to the compobj struct */
7257 /* Note: in the test made, these were Identical */
7258 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7259 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7261 /* Get the CLSID */
7262 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7263 bufferW, OLESTREAM_MAX_STR_LEN );
7264 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7266 if(hRes == S_OK)
7268 HKEY hKey;
7269 LONG hErr;
7270 /* Get the CLSID Default Name from the Registry */
7271 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7272 if(hErr == ERROR_SUCCESS)
7274 char strTemp[OLESTREAM_MAX_STR_LEN];
7275 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7276 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7277 if(hErr == ERROR_SUCCESS)
7279 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7281 RegCloseKey(hKey);
7285 /* Write CompObj Structure to stream */
7286 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7288 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7290 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7291 if(IStorageCompObj.dwCLSIDNameLength > 0)
7293 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7295 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7296 if(IStorageCompObj.dwOleTypeNameLength > 0)
7298 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7300 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7301 if(IStorageCompObj.dwProgIDNameLength > 0)
7303 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7305 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7306 IStream_Release(pStream);
7308 return hRes;
7312 /*************************************************************************
7313 * OLECONVERT_CreateOlePresStream[Internal]
7315 * Creates the "\002OlePres000" Stream with the Metafile data
7317 * PARAMS
7318 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7319 * dwExtentX [I] Width of the Metafile
7320 * dwExtentY [I] Height of the Metafile
7321 * pData [I] Metafile data
7322 * dwDataLength [I] Size of the Metafile data
7324 * RETURNS
7325 * Success: S_OK
7326 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7328 * NOTES
7329 * This function is used by OleConvertOLESTREAMToIStorage only.
7332 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7334 HRESULT hRes;
7335 IStream *pStream;
7336 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7337 BYTE pOlePresStreamHeader [] =
7339 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7340 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7341 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7342 0x00, 0x00, 0x00, 0x00
7345 BYTE pOlePresStreamHeaderEmpty [] =
7347 0x00, 0x00, 0x00, 0x00,
7348 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7349 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7350 0x00, 0x00, 0x00, 0x00
7353 /* Create the OlePres000 Stream */
7354 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7355 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7357 if(hRes == S_OK)
7359 DWORD nHeaderSize;
7360 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7362 memset(&OlePres, 0, sizeof(OlePres));
7363 /* Do we have any metafile data to save */
7364 if(dwDataLength > 0)
7366 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7367 nHeaderSize = sizeof(pOlePresStreamHeader);
7369 else
7371 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7372 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7374 /* Set width and height of the metafile */
7375 OlePres.dwExtentX = dwExtentX;
7376 OlePres.dwExtentY = -dwExtentY;
7378 /* Set Data and Length */
7379 if(dwDataLength > sizeof(METAFILEPICT16))
7381 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7382 OlePres.pData = &(pData[8]);
7384 /* Save OlePres000 Data to Stream */
7385 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7386 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7387 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7388 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7389 if(OlePres.dwSize > 0)
7391 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7393 IStream_Release(pStream);
7397 /*************************************************************************
7398 * OLECONVERT_CreateOle10NativeStream [Internal]
7400 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7402 * PARAMS
7403 * pStorage [I] Dest storage to create the stream in
7404 * pData [I] Ole10 Native Data (ex. bmp)
7405 * dwDataLength [I] Size of the Ole10 Native Data
7407 * RETURNS
7408 * Nothing
7410 * NOTES
7411 * This function is used by OleConvertOLESTREAMToIStorage only.
7413 * Might need to verify the data and return appropriate error message
7416 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7418 HRESULT hRes;
7419 IStream *pStream;
7420 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7422 /* Create the Ole10Native Stream */
7423 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7424 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7426 if(hRes == S_OK)
7428 /* Write info to stream */
7429 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7430 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7431 IStream_Release(pStream);
7436 /*************************************************************************
7437 * OLECONVERT_GetOLE10ProgID [Internal]
7439 * Finds the ProgID (or OleTypeID) from the IStorage
7441 * PARAMS
7442 * pStorage [I] The Src IStorage to get the ProgID
7443 * strProgID [I] the ProgID string to get
7444 * dwSize [I] the size of the string
7446 * RETURNS
7447 * Success: S_OK
7448 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7450 * NOTES
7451 * This function is used by OleConvertIStorageToOLESTREAM only.
7455 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7457 HRESULT hRes;
7458 IStream *pStream;
7459 LARGE_INTEGER iSeekPos;
7460 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7461 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7463 /* Open the CompObj Stream */
7464 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7465 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7466 if(hRes == S_OK)
7469 /*Get the OleType from the CompObj Stream */
7470 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7471 iSeekPos.u.HighPart = 0;
7473 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7474 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7475 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7476 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7477 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7478 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7479 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7481 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7482 if(*dwSize > 0)
7484 IStream_Read(pStream, strProgID, *dwSize, NULL);
7486 IStream_Release(pStream);
7488 else
7490 STATSTG stat;
7491 LPOLESTR wstrProgID;
7493 /* Get the OleType from the registry */
7494 REFCLSID clsid = &(stat.clsid);
7495 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7496 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7497 if(hRes == S_OK)
7499 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7503 return hRes;
7506 /*************************************************************************
7507 * OLECONVERT_GetOle10PresData [Internal]
7509 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7511 * PARAMS
7512 * pStorage [I] Src IStroage
7513 * pOleStream [I] Dest OleStream Mem Struct
7515 * RETURNS
7516 * Nothing
7518 * NOTES
7519 * This function is used by OleConvertIStorageToOLESTREAM only.
7521 * Memory allocated for pData must be freed by the caller
7525 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7528 HRESULT hRes;
7529 IStream *pStream;
7530 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7532 /* Initialize Default data for OLESTREAM */
7533 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7534 pOleStreamData[0].dwTypeID = 2;
7535 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7536 pOleStreamData[1].dwTypeID = 0;
7537 pOleStreamData[0].dwMetaFileWidth = 0;
7538 pOleStreamData[0].dwMetaFileHeight = 0;
7539 pOleStreamData[0].pData = NULL;
7540 pOleStreamData[1].pData = NULL;
7542 /* Open Ole10Native Stream */
7543 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7544 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7545 if(hRes == S_OK)
7548 /* Read Size and Data */
7549 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7550 if(pOleStreamData->dwDataLength > 0)
7552 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7553 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7555 IStream_Release(pStream);
7561 /*************************************************************************
7562 * OLECONVERT_GetOle20PresData[Internal]
7564 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7566 * PARAMS
7567 * pStorage [I] Src IStroage
7568 * pOleStreamData [I] Dest OleStream Mem Struct
7570 * RETURNS
7571 * Nothing
7573 * NOTES
7574 * This function is used by OleConvertIStorageToOLESTREAM only.
7576 * Memory allocated for pData must be freed by the caller
7578 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7580 HRESULT hRes;
7581 IStream *pStream;
7582 OLECONVERT_ISTORAGE_OLEPRES olePress;
7583 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7585 /* Initialize Default data for OLESTREAM */
7586 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7587 pOleStreamData[0].dwTypeID = 2;
7588 pOleStreamData[0].dwMetaFileWidth = 0;
7589 pOleStreamData[0].dwMetaFileHeight = 0;
7590 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7591 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7592 pOleStreamData[1].dwTypeID = 0;
7593 pOleStreamData[1].dwOleTypeNameLength = 0;
7594 pOleStreamData[1].strOleTypeName[0] = 0;
7595 pOleStreamData[1].dwMetaFileWidth = 0;
7596 pOleStreamData[1].dwMetaFileHeight = 0;
7597 pOleStreamData[1].pData = NULL;
7598 pOleStreamData[1].dwDataLength = 0;
7601 /* Open OlePress000 stream */
7602 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7603 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7604 if(hRes == S_OK)
7606 LARGE_INTEGER iSeekPos;
7607 METAFILEPICT16 MetaFilePict;
7608 static const char strMetafilePictName[] = "METAFILEPICT";
7610 /* Set the TypeID for a Metafile */
7611 pOleStreamData[1].dwTypeID = 5;
7613 /* Set the OleTypeName to Metafile */
7614 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7615 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7617 iSeekPos.u.HighPart = 0;
7618 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7620 /* Get Presentation Data */
7621 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7622 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7623 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7624 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7626 /*Set width and Height */
7627 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7628 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7629 if(olePress.dwSize > 0)
7631 /* Set Length */
7632 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7634 /* Set MetaFilePict struct */
7635 MetaFilePict.mm = 8;
7636 MetaFilePict.xExt = olePress.dwExtentX;
7637 MetaFilePict.yExt = olePress.dwExtentY;
7638 MetaFilePict.hMF = 0;
7640 /* Get Metafile Data */
7641 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7642 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7643 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7645 IStream_Release(pStream);
7649 /*************************************************************************
7650 * OleConvertOLESTREAMToIStorage [OLE32.@]
7652 * Read info on MSDN
7654 * TODO
7655 * DVTARGETDEVICE parameter is not handled
7656 * Still unsure of some mem fields for OLE 10 Stream
7657 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7658 * and "\001OLE" streams
7661 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7662 LPOLESTREAM pOleStream,
7663 LPSTORAGE pstg,
7664 const DVTARGETDEVICE* ptd)
7666 int i;
7667 HRESULT hRes=S_OK;
7668 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7670 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7672 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7674 if(ptd != NULL)
7676 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7679 if(pstg == NULL || pOleStream == NULL)
7681 hRes = E_INVALIDARG;
7684 if(hRes == S_OK)
7686 /* Load the OLESTREAM to Memory */
7687 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7690 if(hRes == S_OK)
7692 /* Load the OLESTREAM to Memory (part 2)*/
7693 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7696 if(hRes == S_OK)
7699 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7701 /* Do we have the IStorage Data in the OLESTREAM */
7702 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7704 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7705 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7707 else
7709 /* It must be an original OLE 1.0 source */
7710 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7713 else
7715 /* It must be an original OLE 1.0 source */
7716 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7719 /* Create CompObj Stream if necessary */
7720 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7721 if(hRes == S_OK)
7723 /*Create the Ole Stream if necessary */
7724 OLECONVERT_CreateOleStream(pstg);
7729 /* Free allocated memory */
7730 for(i=0; i < 2; i++)
7732 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7733 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7734 pOleStreamData[i].pstrOleObjFileName = NULL;
7736 return hRes;
7739 /*************************************************************************
7740 * OleConvertIStorageToOLESTREAM [OLE32.@]
7742 * Read info on MSDN
7744 * Read info on MSDN
7746 * TODO
7747 * Still unsure of some mem fields for OLE 10 Stream
7748 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7749 * and "\001OLE" streams.
7752 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7753 LPSTORAGE pstg,
7754 LPOLESTREAM pOleStream)
7756 int i;
7757 HRESULT hRes = S_OK;
7758 IStream *pStream;
7759 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7760 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7762 TRACE("%p %p\n", pstg, pOleStream);
7764 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7766 if(pstg == NULL || pOleStream == NULL)
7768 hRes = E_INVALIDARG;
7770 if(hRes == S_OK)
7772 /* Get the ProgID */
7773 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7774 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7776 if(hRes == S_OK)
7778 /* Was it originally Ole10 */
7779 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7780 if(hRes == S_OK)
7782 IStream_Release(pStream);
7783 /* Get Presentation Data for Ole10Native */
7784 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7786 else
7788 /* Get Presentation Data (OLE20) */
7789 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7792 /* Save OLESTREAM */
7793 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7794 if(hRes == S_OK)
7796 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7801 /* Free allocated memory */
7802 for(i=0; i < 2; i++)
7804 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7807 return hRes;
7810 /***********************************************************************
7811 * GetConvertStg (OLE32.@)
7813 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7814 FIXME("unimplemented stub!\n");
7815 return E_FAIL;
7818 /******************************************************************************
7819 * StgIsStorageFile [OLE32.@]
7820 * Verify if the file contains a storage object
7822 * PARAMS
7823 * fn [ I] Filename
7825 * RETURNS
7826 * S_OK if file has magic bytes as a storage object
7827 * S_FALSE if file is not storage
7829 HRESULT WINAPI
7830 StgIsStorageFile(LPCOLESTR fn)
7832 HANDLE hf;
7833 BYTE magic[8];
7834 DWORD bytes_read;
7836 TRACE("%s\n", debugstr_w(fn));
7837 hf = CreateFileW(fn, GENERIC_READ,
7838 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7839 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7841 if (hf == INVALID_HANDLE_VALUE)
7842 return STG_E_FILENOTFOUND;
7844 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7846 WARN(" unable to read file\n");
7847 CloseHandle(hf);
7848 return S_FALSE;
7851 CloseHandle(hf);
7853 if (bytes_read != 8) {
7854 WARN(" too short\n");
7855 return S_FALSE;
7858 if (!memcmp(magic,STORAGE_magic,8)) {
7859 WARN(" -> YES\n");
7860 return S_OK;
7863 WARN(" -> Invalid header.\n");
7864 return S_FALSE;
7867 /***********************************************************************
7868 * WriteClassStm (OLE32.@)
7870 * Writes a CLSID to a stream.
7872 * PARAMS
7873 * pStm [I] Stream to write to.
7874 * rclsid [I] CLSID to write.
7876 * RETURNS
7877 * Success: S_OK.
7878 * Failure: HRESULT code.
7880 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7882 TRACE("(%p,%p)\n",pStm,rclsid);
7884 if (!pStm || !rclsid)
7885 return E_INVALIDARG;
7887 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7890 /***********************************************************************
7891 * ReadClassStm (OLE32.@)
7893 * Reads a CLSID from a stream.
7895 * PARAMS
7896 * pStm [I] Stream to read from.
7897 * rclsid [O] CLSID to read.
7899 * RETURNS
7900 * Success: S_OK.
7901 * Failure: HRESULT code.
7903 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7905 ULONG nbByte;
7906 HRESULT res;
7908 TRACE("(%p,%p)\n",pStm,pclsid);
7910 if (!pStm || !pclsid)
7911 return E_INVALIDARG;
7913 /* clear the output args */
7914 *pclsid = CLSID_NULL;
7916 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7918 if (FAILED(res))
7919 return res;
7921 if (nbByte != sizeof(CLSID))
7922 return STG_E_READFAULT;
7923 else
7924 return S_OK;