ole32: StgOpenStorage on non-existent file should create it (with test).
[wine/wine64.git] / dlls / ole32 / storage32.c
blob0293073c6479bd02f7d06d27aefccaec04f72983
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
32 * MSDN
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
42 #define COBJMACROS
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
46 #include "windef.h"
47 #include "winbase.h"
48 #include "winnls.h"
49 #include "winuser.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
56 #include "winreg.h"
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
65 static const char rootPropertyName[] = "Root Entry";
67 /****************************************************************************
68 * Storage32InternalImpl definitions.
70 * Definition of the implementation structure for the IStorage32 interface.
71 * This one implements the IStorage32 interface for storage that are
72 * inside another storage.
74 struct StorageInternalImpl
76 struct StorageBaseImpl base;
78 * There is no specific data for this class.
81 typedef struct StorageInternalImpl StorageInternalImpl;
83 /* Method definitions for the Storage32InternalImpl class. */
84 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
85 DWORD openFlags, ULONG rootTropertyIndex);
86 static void StorageImpl_Destroy(StorageBaseImpl* iface);
87 static void* StorageImpl_GetBigBlock(StorageImpl* This, ULONG blockIndex);
88 static void* StorageImpl_GetROBigBlock(StorageImpl* This, ULONG blockIndex);
89 static void StorageImpl_ReleaseBigBlock(StorageImpl* This, void* pBigBlock);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
92 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
93 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
94 static void StorageImpl_SaveFileHeader(StorageImpl* This);
96 static void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm);
98 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
100 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
101 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
102 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
104 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
105 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
106 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
108 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
111 /* OLESTREAM memory structure to use for Get and Put Routines */
112 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
113 typedef struct
115 DWORD dwOleID;
116 DWORD dwTypeID;
117 DWORD dwOleTypeNameLength;
118 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
119 CHAR *pstrOleObjFileName;
120 DWORD dwOleObjFileNameLength;
121 DWORD dwMetaFileWidth;
122 DWORD dwMetaFileHeight;
123 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
124 DWORD dwDataLength;
125 BYTE *pData;
126 }OLECONVERT_OLESTREAM_DATA;
128 /* CompObj Stream structure */
129 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
130 typedef struct
132 BYTE byUnknown1[12];
133 CLSID clsid;
134 DWORD dwCLSIDNameLength;
135 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
136 DWORD dwOleTypeNameLength;
137 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
138 DWORD dwProgIDNameLength;
139 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
140 BYTE byUnknown2[16];
141 }OLECONVERT_ISTORAGE_COMPOBJ;
144 /* Ole Presention Stream structure */
145 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
146 typedef struct
148 BYTE byUnknown1[28];
149 DWORD dwExtentX;
150 DWORD dwExtentY;
151 DWORD dwSize;
152 BYTE *pData;
153 }OLECONVERT_ISTORAGE_OLEPRES;
157 /***********************************************************************
158 * Forward declaration of internal functions used by the method DestroyElement
160 static HRESULT deleteStorageProperty(
161 StorageImpl *parentStorage,
162 ULONG foundPropertyIndexToDelete,
163 StgProperty propertyToDelete);
165 static HRESULT deleteStreamProperty(
166 StorageImpl *parentStorage,
167 ULONG foundPropertyIndexToDelete,
168 StgProperty propertyToDelete);
170 static HRESULT findPlaceholder(
171 StorageImpl *storage,
172 ULONG propertyIndexToStore,
173 ULONG storagePropertyIndex,
174 INT typeOfRelation);
176 static HRESULT adjustPropertyChain(
177 StorageImpl *This,
178 StgProperty propertyToDelete,
179 StgProperty parentProperty,
180 ULONG parentPropertyId,
181 INT typeOfRelation);
183 /***********************************************************************
184 * Declaration of the functions used to manipulate StgProperty
187 static ULONG getFreeProperty(
188 StorageImpl *storage);
190 static void updatePropertyChain(
191 StorageImpl *storage,
192 ULONG newPropertyIndex,
193 StgProperty newProperty);
195 static LONG propertyNameCmp(
196 const OLECHAR *newProperty,
197 const OLECHAR *currentProperty);
200 /***********************************************************************
201 * Declaration of miscellaneous functions...
203 static HRESULT validateSTGM(DWORD stgmValue);
205 static DWORD GetShareModeFromSTGM(DWORD stgm);
206 static DWORD GetAccessModeFromSTGM(DWORD stgm);
207 static DWORD GetCreationModeFromSTGM(DWORD stgm);
209 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
212 /****************************************************************************
213 * IEnumSTATSTGImpl definitions.
215 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
216 * This class allows iterating through the content of a storage and to find
217 * specific items inside it.
219 struct IEnumSTATSTGImpl
221 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
222 * since we want to cast this in an IEnumSTATSTG pointer */
224 LONG ref; /* Reference count */
225 StorageImpl* parentStorage; /* Reference to the parent storage */
226 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
229 * The current implementation of the IEnumSTATSTGImpl class uses a stack
230 * to walk the property sets to get the content of a storage. This stack
231 * is implemented by the following 3 data members
233 ULONG stackSize;
234 ULONG stackMaxSize;
235 ULONG* stackToVisit;
237 #define ENUMSTATSGT_SIZE_INCREMENT 10
241 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
242 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
243 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
244 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
245 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
246 StgProperty* buffer);
247 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
248 StgProperty *currentProperty, ULONG *propertyId);
251 /************************************************************************
252 ** Storage32BaseImpl implementatiion
255 /************************************************************************
256 * Storage32BaseImpl_QueryInterface (IUnknown)
258 * This method implements the common QueryInterface for all IStorage32
259 * implementations contained in this file.
261 * See Windows documentation for more details on IUnknown methods.
263 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
264 IStorage* iface,
265 REFIID riid,
266 void** ppvObject)
268 StorageBaseImpl *This = (StorageBaseImpl *)iface;
270 * Perform a sanity check on the parameters.
272 if ( (This==0) || (ppvObject==0) )
273 return E_INVALIDARG;
276 * Initialize the return parameter.
278 *ppvObject = 0;
281 * Compare the riid with the interface IDs implemented by this object.
283 if (IsEqualGUID(&IID_IUnknown, riid) ||
284 IsEqualGUID(&IID_IStorage, riid))
286 *ppvObject = (IStorage*)This;
288 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
290 *ppvObject = (IStorage*)&This->pssVtbl;
294 * Check that we obtained an interface.
296 if ((*ppvObject)==0)
297 return E_NOINTERFACE;
300 * Query Interface always increases the reference count by one when it is
301 * successful
303 IStorage_AddRef(iface);
305 return S_OK;
308 /************************************************************************
309 * Storage32BaseImpl_AddRef (IUnknown)
311 * This method implements the common AddRef for all IStorage32
312 * implementations contained in this file.
314 * See Windows documentation for more details on IUnknown methods.
316 static ULONG WINAPI StorageBaseImpl_AddRef(
317 IStorage* iface)
319 StorageBaseImpl *This = (StorageBaseImpl *)iface;
320 ULONG ref = InterlockedIncrement(&This->ref);
322 TRACE("(%p) AddRef to %ld\n", This, ref);
324 return ref;
327 /************************************************************************
328 * Storage32BaseImpl_Release (IUnknown)
330 * This method implements the common Release for all IStorage32
331 * implementations contained in this file.
333 * See Windows documentation for more details on IUnknown methods.
335 static ULONG WINAPI StorageBaseImpl_Release(
336 IStorage* iface)
338 StorageBaseImpl *This = (StorageBaseImpl *)iface;
340 * Decrease the reference count on this object.
342 ULONG ref = InterlockedDecrement(&This->ref);
344 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
347 * If the reference count goes down to 0, perform suicide.
349 if (ref == 0)
352 * Since we are using a system of base-classes, we want to call the
353 * destructor of the appropriate derived class. To do this, we are
354 * using virtual functions to implement the destructor.
356 This->v_destructor(This);
359 return ref;
362 /************************************************************************
363 * Storage32BaseImpl_OpenStream (IStorage)
365 * This method will open the specified stream object from the current storage.
367 * See Windows documentation for more details on IStorage methods.
369 static HRESULT WINAPI StorageBaseImpl_OpenStream(
370 IStorage* iface,
371 const OLECHAR* pwcsName, /* [string][in] */
372 void* reserved1, /* [unique][in] */
373 DWORD grfMode, /* [in] */
374 DWORD reserved2, /* [in] */
375 IStream** ppstm) /* [out] */
377 StorageBaseImpl *This = (StorageBaseImpl *)iface;
378 IEnumSTATSTGImpl* propertyEnumeration;
379 StgStreamImpl* newStream;
380 StgProperty currentProperty;
381 ULONG foundPropertyIndex;
382 HRESULT res = STG_E_UNKNOWN;
384 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
385 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
388 * Perform a sanity check on the parameters.
390 if ( (pwcsName==NULL) || (ppstm==0) )
392 res = E_INVALIDARG;
393 goto end;
397 * Initialize the out parameter
399 *ppstm = NULL;
402 * Validate the STGM flags
404 if ( FAILED( validateSTGM(grfMode) ) ||
405 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
407 res = STG_E_INVALIDFLAG;
408 goto end;
412 * As documented.
414 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
416 res = STG_E_INVALIDFUNCTION;
417 goto end;
421 * Check that we're compatible with the parent's storage mode, but
422 * only if we are not in transacted mode
424 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
425 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
427 res = STG_E_ACCESSDENIED;
428 goto end;
433 * Create a property enumeration to search the properties
435 propertyEnumeration = IEnumSTATSTGImpl_Construct(
436 This->ancestorStorage,
437 This->rootPropertySetIndex);
440 * Search the enumeration for the property with the given name
442 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
443 propertyEnumeration,
444 pwcsName,
445 &currentProperty);
448 * Delete the property enumeration since we don't need it anymore
450 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
453 * If it was found, construct the stream object and return a pointer to it.
455 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
456 (currentProperty.propertyType==PROPTYPE_STREAM) )
458 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
460 if (newStream!=0)
462 newStream->grfMode = grfMode;
463 *ppstm = (IStream*)newStream;
466 * Since we are returning a pointer to the interface, we have to
467 * nail down the reference.
469 IStream_AddRef(*ppstm);
472 * add us to the storage's list of active streams
475 StorageBaseImpl_AddStream(This,newStream);
477 res = S_OK;
478 goto end;
481 res = E_OUTOFMEMORY;
482 goto end;
485 res = STG_E_FILENOTFOUND;
487 end:
488 if (res == S_OK)
489 TRACE("<-- IStream %p\n", *ppstm);
490 TRACE("<-- %08lx\n", res);
491 return res;
494 /************************************************************************
495 * Storage32BaseImpl_OpenStorage (IStorage)
497 * This method will open a new storage object from the current storage.
499 * See Windows documentation for more details on IStorage methods.
501 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
502 IStorage* iface,
503 const OLECHAR* pwcsName, /* [string][unique][in] */
504 IStorage* pstgPriority, /* [unique][in] */
505 DWORD grfMode, /* [in] */
506 SNB snbExclude, /* [unique][in] */
507 DWORD reserved, /* [in] */
508 IStorage** ppstg) /* [out] */
510 StorageBaseImpl *This = (StorageBaseImpl *)iface;
511 StorageInternalImpl* newStorage;
512 IEnumSTATSTGImpl* propertyEnumeration;
513 StgProperty currentProperty;
514 ULONG foundPropertyIndex;
515 HRESULT res = STG_E_UNKNOWN;
517 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
518 iface, debugstr_w(pwcsName), pstgPriority,
519 grfMode, snbExclude, reserved, ppstg);
522 * Perform a sanity check on the parameters.
524 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
526 res = E_INVALIDARG;
527 goto end;
530 /* as documented */
531 if (snbExclude != NULL)
533 res = STG_E_INVALIDPARAMETER;
534 goto end;
538 * Validate the STGM flags
540 if ( FAILED( validateSTGM(grfMode) ))
542 res = STG_E_INVALIDFLAG;
543 goto end;
547 * As documented.
549 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
550 (grfMode & STGM_DELETEONRELEASE) ||
551 (grfMode & STGM_PRIORITY) )
553 res = STG_E_INVALIDFUNCTION;
554 goto end;
558 * Check that we're compatible with the parent's storage mode,
559 * but only if we are not transacted
561 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
562 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
564 res = STG_E_ACCESSDENIED;
565 goto end;
570 * Initialize the out parameter
572 *ppstg = NULL;
575 * Create a property enumeration to search the properties
577 propertyEnumeration = IEnumSTATSTGImpl_Construct(
578 This->ancestorStorage,
579 This->rootPropertySetIndex);
582 * Search the enumeration for the property with the given name
584 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
585 propertyEnumeration,
586 pwcsName,
587 &currentProperty);
590 * Delete the property enumeration since we don't need it anymore
592 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
595 * If it was found, construct the stream object and return a pointer to it.
597 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
598 (currentProperty.propertyType==PROPTYPE_STORAGE) )
601 * Construct a new Storage object
603 newStorage = StorageInternalImpl_Construct(
604 This->ancestorStorage,
605 grfMode,
606 foundPropertyIndex);
608 if (newStorage != 0)
610 *ppstg = (IStorage*)newStorage;
613 * Since we are returning a pointer to the interface,
614 * we have to nail down the reference.
616 StorageBaseImpl_AddRef(*ppstg);
618 res = S_OK;
619 goto end;
622 res = STG_E_INSUFFICIENTMEMORY;
623 goto end;
626 res = STG_E_FILENOTFOUND;
628 end:
629 TRACE("<-- %08lx\n", res);
630 return res;
633 /************************************************************************
634 * Storage32BaseImpl_EnumElements (IStorage)
636 * This method will create an enumerator object that can be used to
637 * retrieve informatino about all the properties in the storage object.
639 * See Windows documentation for more details on IStorage methods.
641 static HRESULT WINAPI StorageBaseImpl_EnumElements(
642 IStorage* iface,
643 DWORD reserved1, /* [in] */
644 void* reserved2, /* [size_is][unique][in] */
645 DWORD reserved3, /* [in] */
646 IEnumSTATSTG** ppenum) /* [out] */
648 StorageBaseImpl *This = (StorageBaseImpl *)iface;
649 IEnumSTATSTGImpl* newEnum;
651 TRACE("(%p, %ld, %p, %ld, %p)\n",
652 iface, reserved1, reserved2, reserved3, ppenum);
655 * Perform a sanity check on the parameters.
657 if ( (This==0) || (ppenum==0))
658 return E_INVALIDARG;
661 * Construct the enumerator.
663 newEnum = IEnumSTATSTGImpl_Construct(
664 This->ancestorStorage,
665 This->rootPropertySetIndex);
667 if (newEnum!=0)
669 *ppenum = (IEnumSTATSTG*)newEnum;
672 * Don't forget to nail down a reference to the new object before
673 * returning it.
675 IEnumSTATSTG_AddRef(*ppenum);
677 return S_OK;
680 return E_OUTOFMEMORY;
683 /************************************************************************
684 * Storage32BaseImpl_Stat (IStorage)
686 * This method will retrieve information about this storage object.
688 * See Windows documentation for more details on IStorage methods.
690 static HRESULT WINAPI StorageBaseImpl_Stat(
691 IStorage* iface,
692 STATSTG* pstatstg, /* [out] */
693 DWORD grfStatFlag) /* [in] */
695 StorageBaseImpl *This = (StorageBaseImpl *)iface;
696 StgProperty curProperty;
697 BOOL readSuccessful;
698 HRESULT res = STG_E_UNKNOWN;
700 TRACE("(%p, %p, %lx)\n",
701 iface, pstatstg, grfStatFlag);
704 * Perform a sanity check on the parameters.
706 if ( (This==0) || (pstatstg==0))
708 res = E_INVALIDARG;
709 goto end;
713 * Read the information from the property.
715 readSuccessful = StorageImpl_ReadProperty(
716 This->ancestorStorage,
717 This->rootPropertySetIndex,
718 &curProperty);
720 if (readSuccessful)
722 StorageUtl_CopyPropertyToSTATSTG(
723 pstatstg,
724 &curProperty,
725 grfStatFlag);
727 pstatstg->grfMode = This->openFlags;
729 res = S_OK;
730 goto end;
733 res = E_FAIL;
735 end:
736 if (res == S_OK)
738 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
740 TRACE("<-- %08lx\n", res);
741 return res;
744 /************************************************************************
745 * Storage32BaseImpl_RenameElement (IStorage)
747 * This method will rename the specified element.
749 * See Windows documentation for more details on IStorage methods.
751 * Implementation notes: The method used to rename consists of creating a clone
752 * of the deleted StgProperty object setting it with the new name and to
753 * perform a DestroyElement of the old StgProperty.
755 static HRESULT WINAPI StorageBaseImpl_RenameElement(
756 IStorage* iface,
757 const OLECHAR* pwcsOldName, /* [in] */
758 const OLECHAR* pwcsNewName) /* [in] */
760 StorageBaseImpl *This = (StorageBaseImpl *)iface;
761 IEnumSTATSTGImpl* propertyEnumeration;
762 StgProperty currentProperty;
763 ULONG foundPropertyIndex;
765 TRACE("(%p, %s, %s)\n",
766 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
769 * Create a property enumeration to search the properties
771 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
772 This->rootPropertySetIndex);
775 * Search the enumeration for the new property name
777 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
778 pwcsNewName,
779 &currentProperty);
781 if (foundPropertyIndex != PROPERTY_NULL)
784 * There is already a property with the new name
786 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
787 return STG_E_FILEALREADYEXISTS;
790 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
793 * Search the enumeration for the old property name
795 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
796 pwcsOldName,
797 &currentProperty);
800 * Delete the property enumeration since we don't need it anymore
802 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
804 if (foundPropertyIndex != PROPERTY_NULL)
806 StgProperty renamedProperty;
807 ULONG renamedPropertyIndex;
810 * Setup a new property for the renamed property
812 renamedProperty.sizeOfNameString =
813 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
815 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
816 return STG_E_INVALIDNAME;
818 strcpyW(renamedProperty.name, pwcsNewName);
820 renamedProperty.propertyType = currentProperty.propertyType;
821 renamedProperty.startingBlock = currentProperty.startingBlock;
822 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
823 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
825 renamedProperty.previousProperty = PROPERTY_NULL;
826 renamedProperty.nextProperty = PROPERTY_NULL;
829 * Bring the dirProperty link in case it is a storage and in which
830 * case the renamed storage elements don't require to be reorganized.
832 renamedProperty.dirProperty = currentProperty.dirProperty;
834 /* call CoFileTime to get the current time
835 renamedProperty.timeStampS1
836 renamedProperty.timeStampD1
837 renamedProperty.timeStampS2
838 renamedProperty.timeStampD2
839 renamedProperty.propertyUniqueID
843 * Obtain a free property in the property chain
845 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
848 * Save the new property into the new property spot
850 StorageImpl_WriteProperty(
851 This->ancestorStorage,
852 renamedPropertyIndex,
853 &renamedProperty);
856 * Find a spot in the property chain for our newly created property.
858 updatePropertyChain(
859 (StorageImpl*)This,
860 renamedPropertyIndex,
861 renamedProperty);
864 * At this point the renamed property has been inserted in the tree,
865 * now, before Destroying the old property we must zero its dirProperty
866 * otherwise the DestroyProperty below will zap it all and we do not want
867 * this to happen.
868 * Also, we fake that the old property is a storage so the DestroyProperty
869 * will not do a SetSize(0) on the stream data.
871 * This means that we need to tweak the StgProperty if it is a stream or a
872 * non empty storage.
874 StorageImpl_ReadProperty(This->ancestorStorage,
875 foundPropertyIndex,
876 &currentProperty);
878 currentProperty.dirProperty = PROPERTY_NULL;
879 currentProperty.propertyType = PROPTYPE_STORAGE;
880 StorageImpl_WriteProperty(
881 This->ancestorStorage,
882 foundPropertyIndex,
883 &currentProperty);
886 * Invoke Destroy to get rid of the ole property and automatically redo
887 * the linking of its previous and next members...
889 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
892 else
895 * There is no property with the old name
897 return STG_E_FILENOTFOUND;
900 return S_OK;
903 /************************************************************************
904 * Storage32BaseImpl_CreateStream (IStorage)
906 * This method will create a stream object within this storage
908 * See Windows documentation for more details on IStorage methods.
910 static HRESULT WINAPI StorageBaseImpl_CreateStream(
911 IStorage* iface,
912 const OLECHAR* pwcsName, /* [string][in] */
913 DWORD grfMode, /* [in] */
914 DWORD reserved1, /* [in] */
915 DWORD reserved2, /* [in] */
916 IStream** ppstm) /* [out] */
918 StorageBaseImpl *This = (StorageBaseImpl *)iface;
919 IEnumSTATSTGImpl* propertyEnumeration;
920 StgStreamImpl* newStream;
921 StgProperty currentProperty, newStreamProperty;
922 ULONG foundPropertyIndex, newPropertyIndex;
924 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
925 iface, debugstr_w(pwcsName), grfMode,
926 reserved1, reserved2, ppstm);
929 * Validate parameters
931 if (ppstm == 0)
932 return STG_E_INVALIDPOINTER;
934 if (pwcsName == 0)
935 return STG_E_INVALIDNAME;
937 if (reserved1 || reserved2)
938 return STG_E_INVALIDPARAMETER;
941 * Validate the STGM flags
943 if ( FAILED( validateSTGM(grfMode) ))
944 return STG_E_INVALIDFLAG;
946 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
947 return STG_E_INVALIDFLAG;
950 * As documented.
952 if ((grfMode & STGM_DELETEONRELEASE) ||
953 (grfMode & STGM_TRANSACTED))
954 return STG_E_INVALIDFUNCTION;
957 * Check that we're compatible with the parent's storage mode
958 * if not in transacted mode
960 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
961 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
962 return STG_E_ACCESSDENIED;
966 * Initialize the out parameter
968 *ppstm = 0;
971 * Create a property enumeration to search the properties
973 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
974 This->rootPropertySetIndex);
976 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
977 pwcsName,
978 &currentProperty);
980 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
982 if (foundPropertyIndex != PROPERTY_NULL)
985 * An element with this name already exists
987 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
989 IStorage_DestroyElement(iface, pwcsName);
991 else
992 return STG_E_FILEALREADYEXISTS;
994 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
996 WARN("read-only storage\n");
997 return STG_E_ACCESSDENIED;
1001 * memset the empty property
1003 memset(&newStreamProperty, 0, sizeof(StgProperty));
1005 newStreamProperty.sizeOfNameString =
1006 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1008 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1009 return STG_E_INVALIDNAME;
1011 strcpyW(newStreamProperty.name, pwcsName);
1013 newStreamProperty.propertyType = PROPTYPE_STREAM;
1014 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1015 newStreamProperty.size.u.LowPart = 0;
1016 newStreamProperty.size.u.HighPart = 0;
1018 newStreamProperty.previousProperty = PROPERTY_NULL;
1019 newStreamProperty.nextProperty = PROPERTY_NULL;
1020 newStreamProperty.dirProperty = PROPERTY_NULL;
1022 /* call CoFileTime to get the current time
1023 newStreamProperty.timeStampS1
1024 newStreamProperty.timeStampD1
1025 newStreamProperty.timeStampS2
1026 newStreamProperty.timeStampD2
1029 /* newStreamProperty.propertyUniqueID */
1032 * Get a free property or create a new one
1034 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1037 * Save the new property into the new property spot
1039 StorageImpl_WriteProperty(
1040 This->ancestorStorage,
1041 newPropertyIndex,
1042 &newStreamProperty);
1045 * Find a spot in the property chain for our newly created property.
1047 updatePropertyChain(
1048 (StorageImpl*)This,
1049 newPropertyIndex,
1050 newStreamProperty);
1053 * Open the stream to return it.
1055 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1057 if (newStream != 0)
1059 *ppstm = (IStream*)newStream;
1062 * Since we are returning a pointer to the interface, we have to nail down
1063 * the reference.
1065 IStream_AddRef(*ppstm);
1067 /* add us to the storage's list of active streams
1069 StorageBaseImpl_AddStream(This,newStream);
1072 else
1074 return STG_E_INSUFFICIENTMEMORY;
1077 return S_OK;
1080 /************************************************************************
1081 * Storage32BaseImpl_SetClass (IStorage)
1083 * This method will write the specified CLSID in the property of this
1084 * storage.
1086 * See Windows documentation for more details on IStorage methods.
1088 static HRESULT WINAPI StorageBaseImpl_SetClass(
1089 IStorage* iface,
1090 REFCLSID clsid) /* [in] */
1092 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1093 HRESULT hRes = E_FAIL;
1094 StgProperty curProperty;
1095 BOOL success;
1097 TRACE("(%p, %p)\n", iface, clsid);
1099 success = StorageImpl_ReadProperty(This->ancestorStorage,
1100 This->rootPropertySetIndex,
1101 &curProperty);
1102 if (success)
1104 curProperty.propertyUniqueID = *clsid;
1106 success = StorageImpl_WriteProperty(This->ancestorStorage,
1107 This->rootPropertySetIndex,
1108 &curProperty);
1109 if (success)
1110 hRes = S_OK;
1113 return hRes;
1116 /************************************************************************
1117 ** Storage32Impl implementation
1120 /************************************************************************
1121 * Storage32Impl_CreateStorage (IStorage)
1123 * This method will create the storage object within the provided storage.
1125 * See Windows documentation for more details on IStorage methods.
1127 static HRESULT WINAPI StorageImpl_CreateStorage(
1128 IStorage* iface,
1129 const OLECHAR *pwcsName, /* [string][in] */
1130 DWORD grfMode, /* [in] */
1131 DWORD reserved1, /* [in] */
1132 DWORD reserved2, /* [in] */
1133 IStorage **ppstg) /* [out] */
1135 StorageImpl* const This=(StorageImpl*)iface;
1137 IEnumSTATSTGImpl *propertyEnumeration;
1138 StgProperty currentProperty;
1139 StgProperty newProperty;
1140 ULONG foundPropertyIndex;
1141 ULONG newPropertyIndex;
1142 HRESULT hr;
1144 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1145 iface, debugstr_w(pwcsName), grfMode,
1146 reserved1, reserved2, ppstg);
1149 * Validate parameters
1151 if (ppstg == 0)
1152 return STG_E_INVALIDPOINTER;
1154 if (pwcsName == 0)
1155 return STG_E_INVALIDNAME;
1158 * Initialize the out parameter
1160 *ppstg = NULL;
1163 * Validate the STGM flags
1165 if ( FAILED( validateSTGM(grfMode) ) ||
1166 (grfMode & STGM_DELETEONRELEASE) )
1168 WARN("bad grfMode: 0x%lx\n", grfMode);
1169 return STG_E_INVALIDFLAG;
1173 * Check that we're compatible with the parent's storage mode
1175 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1177 WARN("access denied\n");
1178 return STG_E_ACCESSDENIED;
1182 * Create a property enumeration and search the properties
1184 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1185 This->base.rootPropertySetIndex);
1187 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1188 pwcsName,
1189 &currentProperty);
1190 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1192 if (foundPropertyIndex != PROPERTY_NULL)
1195 * An element with this name already exists
1197 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1198 IStorage_DestroyElement(iface, pwcsName);
1199 else
1201 WARN("file already exists\n");
1202 return STG_E_FILEALREADYEXISTS;
1205 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1207 WARN("read-only storage\n");
1208 return STG_E_ACCESSDENIED;
1212 * memset the empty property
1214 memset(&newProperty, 0, sizeof(StgProperty));
1216 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1218 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1220 FIXME("name too long\n");
1221 return STG_E_INVALIDNAME;
1224 strcpyW(newProperty.name, pwcsName);
1226 newProperty.propertyType = PROPTYPE_STORAGE;
1227 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1228 newProperty.size.u.LowPart = 0;
1229 newProperty.size.u.HighPart = 0;
1231 newProperty.previousProperty = PROPERTY_NULL;
1232 newProperty.nextProperty = PROPERTY_NULL;
1233 newProperty.dirProperty = PROPERTY_NULL;
1235 /* call CoFileTime to get the current time
1236 newProperty.timeStampS1
1237 newProperty.timeStampD1
1238 newProperty.timeStampS2
1239 newProperty.timeStampD2
1242 /* newStorageProperty.propertyUniqueID */
1245 * Obtain a free property in the property chain
1247 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1250 * Save the new property into the new property spot
1252 StorageImpl_WriteProperty(
1253 This->base.ancestorStorage,
1254 newPropertyIndex,
1255 &newProperty);
1258 * Find a spot in the property chain for our newly created property.
1260 updatePropertyChain(
1261 This,
1262 newPropertyIndex,
1263 newProperty);
1266 * Open it to get a pointer to return.
1268 hr = IStorage_OpenStorage(
1269 iface,
1270 (const OLECHAR*)pwcsName,
1272 grfMode,
1275 ppstg);
1277 if( (hr != S_OK) || (*ppstg == NULL))
1279 return hr;
1283 return S_OK;
1287 /***************************************************************************
1289 * Internal Method
1291 * Get a free property or create a new one.
1293 static ULONG getFreeProperty(
1294 StorageImpl *storage)
1296 ULONG currentPropertyIndex = 0;
1297 ULONG newPropertyIndex = PROPERTY_NULL;
1298 BOOL readSuccessful = TRUE;
1299 StgProperty currentProperty;
1304 * Start by reading the root property
1306 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1307 currentPropertyIndex,
1308 &currentProperty);
1309 if (readSuccessful)
1311 if (currentProperty.sizeOfNameString == 0)
1314 * The property existis and is available, we found it.
1316 newPropertyIndex = currentPropertyIndex;
1319 else
1322 * We exhausted the property list, we will create more space below
1324 newPropertyIndex = currentPropertyIndex;
1326 currentPropertyIndex++;
1328 } while (newPropertyIndex == PROPERTY_NULL);
1331 * grow the property chain
1333 if (! readSuccessful)
1335 StgProperty emptyProperty;
1336 ULARGE_INTEGER newSize;
1337 ULONG propertyIndex;
1338 ULONG lastProperty = 0;
1339 ULONG blockCount = 0;
1342 * obtain the new count of property blocks
1344 blockCount = BlockChainStream_GetCount(
1345 storage->base.ancestorStorage->rootBlockChain)+1;
1348 * initialize the size used by the property stream
1350 newSize.u.HighPart = 0;
1351 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1354 * add a property block to the property chain
1356 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1359 * memset the empty property in order to initialize the unused newly
1360 * created property
1362 memset(&emptyProperty, 0, sizeof(StgProperty));
1365 * initialize them
1367 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1369 for(
1370 propertyIndex = newPropertyIndex;
1371 propertyIndex < lastProperty;
1372 propertyIndex++)
1374 StorageImpl_WriteProperty(
1375 storage->base.ancestorStorage,
1376 propertyIndex,
1377 &emptyProperty);
1381 return newPropertyIndex;
1384 /****************************************************************************
1386 * Internal Method
1388 * Case insensitive comparaison of StgProperty.name by first considering
1389 * their size.
1391 * Returns <0 when newPrpoerty < currentProperty
1392 * >0 when newPrpoerty > currentProperty
1393 * 0 when newPrpoerty == currentProperty
1395 static LONG propertyNameCmp(
1396 const OLECHAR *newProperty,
1397 const OLECHAR *currentProperty)
1399 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1401 if (diff == 0)
1404 * We compare the string themselves only when they are of the same length
1406 diff = lstrcmpiW( newProperty, currentProperty);
1409 return diff;
1412 /****************************************************************************
1414 * Internal Method
1416 * Properly link this new element in the property chain.
1418 static void updatePropertyChain(
1419 StorageImpl *storage,
1420 ULONG newPropertyIndex,
1421 StgProperty newProperty)
1423 StgProperty currentProperty;
1426 * Read the root property
1428 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1429 storage->base.rootPropertySetIndex,
1430 &currentProperty);
1432 if (currentProperty.dirProperty != PROPERTY_NULL)
1435 * The root storage contains some element, therefore, start the research
1436 * for the appropriate location.
1438 BOOL found = 0;
1439 ULONG current, next, previous, currentPropertyId;
1442 * Keep the StgProperty sequence number of the storage first property
1444 currentPropertyId = currentProperty.dirProperty;
1447 * Read
1449 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1450 currentProperty.dirProperty,
1451 &currentProperty);
1453 previous = currentProperty.previousProperty;
1454 next = currentProperty.nextProperty;
1455 current = currentPropertyId;
1457 while (found == 0)
1459 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1461 if (diff < 0)
1463 if (previous != PROPERTY_NULL)
1465 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1466 previous,
1467 &currentProperty);
1468 current = previous;
1470 else
1472 currentProperty.previousProperty = newPropertyIndex;
1473 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1474 current,
1475 &currentProperty);
1476 found = 1;
1479 else if (diff > 0)
1481 if (next != PROPERTY_NULL)
1483 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1484 next,
1485 &currentProperty);
1486 current = next;
1488 else
1490 currentProperty.nextProperty = newPropertyIndex;
1491 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1492 current,
1493 &currentProperty);
1494 found = 1;
1497 else
1500 * Trying to insert an item with the same name in the
1501 * subtree structure.
1503 assert(FALSE);
1506 previous = currentProperty.previousProperty;
1507 next = currentProperty.nextProperty;
1510 else
1513 * The root storage is empty, link the new property to its dir property
1515 currentProperty.dirProperty = newPropertyIndex;
1516 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1517 storage->base.rootPropertySetIndex,
1518 &currentProperty);
1523 /*************************************************************************
1524 * CopyTo (IStorage)
1526 static HRESULT WINAPI StorageImpl_CopyTo(
1527 IStorage* iface,
1528 DWORD ciidExclude, /* [in] */
1529 const IID* rgiidExclude, /* [size_is][unique][in] */
1530 SNB snbExclude, /* [unique][in] */
1531 IStorage* pstgDest) /* [unique][in] */
1533 IEnumSTATSTG *elements = 0;
1534 STATSTG curElement, strStat;
1535 HRESULT hr;
1536 IStorage *pstgTmp, *pstgChild;
1537 IStream *pstrTmp, *pstrChild;
1539 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1540 FIXME("Exclude option not implemented\n");
1542 TRACE("(%p, %ld, %p, %p, %p)\n",
1543 iface, ciidExclude, rgiidExclude,
1544 snbExclude, pstgDest);
1547 * Perform a sanity check
1549 if ( pstgDest == 0 )
1550 return STG_E_INVALIDPOINTER;
1553 * Enumerate the elements
1555 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1557 if ( hr != S_OK )
1558 return hr;
1561 * set the class ID
1563 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1564 IStorage_SetClass( pstgDest, &curElement.clsid );
1569 * Obtain the next element
1571 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1573 if ( hr == S_FALSE )
1575 hr = S_OK; /* done, every element has been copied */
1576 break;
1579 if (curElement.type == STGTY_STORAGE)
1582 * open child source storage
1584 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1585 STGM_READ|STGM_SHARE_EXCLUSIVE,
1586 NULL, 0, &pstgChild );
1588 if (hr != S_OK)
1589 break;
1592 * Check if destination storage is not a child of the source
1593 * storage, which will cause an infinite loop
1595 if (pstgChild == pstgDest)
1597 IEnumSTATSTG_Release(elements);
1599 return STG_E_ACCESSDENIED;
1603 * create a new storage in destination storage
1605 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1606 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1607 0, 0,
1608 &pstgTmp );
1610 * if it already exist, don't create a new one use this one
1612 if (hr == STG_E_FILEALREADYEXISTS)
1614 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1615 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1616 NULL, 0, &pstgTmp );
1619 if (hr != S_OK)
1620 break;
1624 * do the copy recursively
1626 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1627 snbExclude, pstgTmp );
1629 IStorage_Release( pstgTmp );
1630 IStorage_Release( pstgChild );
1632 else if (curElement.type == STGTY_STREAM)
1635 * create a new stream in destination storage. If the stream already
1636 * exist, it will be deleted and a new one will be created.
1638 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1639 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1640 0, 0, &pstrTmp );
1642 if (hr != S_OK)
1643 break;
1646 * open child stream storage
1648 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1649 STGM_READ|STGM_SHARE_EXCLUSIVE,
1650 0, &pstrChild );
1652 if (hr != S_OK)
1653 break;
1656 * Get the size of the source stream
1658 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1661 * Set the size of the destination stream.
1663 IStream_SetSize(pstrTmp, strStat.cbSize);
1666 * do the copy
1668 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1669 NULL, NULL );
1671 IStream_Release( pstrTmp );
1672 IStream_Release( pstrChild );
1674 else
1676 WARN("unknown element type: %ld\n", curElement.type);
1679 } while (hr == S_OK);
1682 * Clean-up
1684 IEnumSTATSTG_Release(elements);
1686 return hr;
1689 /*************************************************************************
1690 * MoveElementTo (IStorage)
1692 static HRESULT WINAPI StorageImpl_MoveElementTo(
1693 IStorage* iface,
1694 const OLECHAR *pwcsName, /* [string][in] */
1695 IStorage *pstgDest, /* [unique][in] */
1696 const OLECHAR *pwcsNewName,/* [string][in] */
1697 DWORD grfFlags) /* [in] */
1699 FIXME("(%p %s %p %s %lu): stub\n", iface,
1700 debugstr_w(pwcsName), pstgDest,
1701 debugstr_w(pwcsNewName), grfFlags);
1702 return E_NOTIMPL;
1705 /*************************************************************************
1706 * Commit (IStorage)
1708 * Ensures that any changes made to a storage object open in transacted mode
1709 * are reflected in the parent storage
1711 * NOTES
1712 * Wine doesn't implement transacted mode, which seems to be a basic
1713 * optimization, so we can ignore this stub for now.
1715 static HRESULT WINAPI StorageImpl_Commit(
1716 IStorage* iface,
1717 DWORD grfCommitFlags)/* [in] */
1719 FIXME("(%p %ld): stub\n", iface, grfCommitFlags);
1720 return S_OK;
1723 /*************************************************************************
1724 * Revert (IStorage)
1726 * Discard all changes that have been made since the last commit operation
1728 static HRESULT WINAPI StorageImpl_Revert(
1729 IStorage* iface)
1731 FIXME("(%p): stub\n", iface);
1732 return E_NOTIMPL;
1735 /*************************************************************************
1736 * DestroyElement (IStorage)
1738 * Strategy: This implementation is built this way for simplicity not for speed.
1739 * I always delete the topmost element of the enumeration and adjust
1740 * the deleted element pointer all the time. This takes longer to
1741 * do but allow to reinvoke DestroyElement whenever we encounter a
1742 * storage object. The optimisation resides in the usage of another
1743 * enumeration strategy that would give all the leaves of a storage
1744 * first. (postfix order)
1746 static HRESULT WINAPI StorageImpl_DestroyElement(
1747 IStorage* iface,
1748 const OLECHAR *pwcsName)/* [string][in] */
1750 StorageImpl* const This=(StorageImpl*)iface;
1752 IEnumSTATSTGImpl* propertyEnumeration;
1753 HRESULT hr = S_OK;
1754 BOOL res;
1755 StgProperty propertyToDelete;
1756 StgProperty parentProperty;
1757 ULONG foundPropertyIndexToDelete;
1758 ULONG typeOfRelation;
1759 ULONG parentPropertyId = 0;
1761 TRACE("(%p, %s)\n",
1762 iface, debugstr_w(pwcsName));
1765 * Perform a sanity check on the parameters.
1767 if (pwcsName==NULL)
1768 return STG_E_INVALIDPOINTER;
1771 * Create a property enumeration to search the property with the given name
1773 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1774 This->base.ancestorStorage,
1775 This->base.rootPropertySetIndex);
1777 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1778 propertyEnumeration,
1779 pwcsName,
1780 &propertyToDelete);
1782 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1784 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1786 return STG_E_FILENOTFOUND;
1790 * Find the parent property of the property to delete (the one that
1791 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1792 * the parent is This. Otherwise, the parent is one of its sibling...
1796 * First, read This's StgProperty..
1798 res = StorageImpl_ReadProperty(
1799 This->base.ancestorStorage,
1800 This->base.rootPropertySetIndex,
1801 &parentProperty);
1803 assert(res);
1806 * Second, check to see if by any chance the actual storage (This) is not
1807 * the parent of the property to delete... We never know...
1809 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1812 * Set data as it would have been done in the else part...
1814 typeOfRelation = PROPERTY_RELATION_DIR;
1815 parentPropertyId = This->base.rootPropertySetIndex;
1817 else
1820 * Create a property enumeration to search the parent properties, and
1821 * delete it once done.
1823 IEnumSTATSTGImpl* propertyEnumeration2;
1825 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1826 This->base.ancestorStorage,
1827 This->base.rootPropertySetIndex);
1829 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1830 propertyEnumeration2,
1831 foundPropertyIndexToDelete,
1832 &parentProperty,
1833 &parentPropertyId);
1835 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1838 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1840 hr = deleteStorageProperty(
1841 This,
1842 foundPropertyIndexToDelete,
1843 propertyToDelete);
1845 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1847 hr = deleteStreamProperty(
1848 This,
1849 foundPropertyIndexToDelete,
1850 propertyToDelete);
1853 if (hr!=S_OK)
1854 return hr;
1857 * Adjust the property chain
1859 hr = adjustPropertyChain(
1860 This,
1861 propertyToDelete,
1862 parentProperty,
1863 parentPropertyId,
1864 typeOfRelation);
1866 return hr;
1870 /************************************************************************
1871 * StorageImpl_Stat (IStorage)
1873 * This method will retrieve information about this storage object.
1875 * See Windows documentation for more details on IStorage methods.
1877 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1878 STATSTG* pstatstg, /* [out] */
1879 DWORD grfStatFlag) /* [in] */
1881 StorageImpl* const This = (StorageImpl*)iface;
1882 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1884 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1886 CoTaskMemFree(pstatstg->pwcsName);
1887 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1888 strcpyW(pstatstg->pwcsName, This->pwcsName);
1891 return result;
1894 /******************************************************************************
1895 * Internal stream list handlers
1898 static void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1900 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1901 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1904 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1906 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1907 list_remove(&(strm->StrmListEntry));
1910 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1912 struct list *cur, *cur2;
1913 StgStreamImpl *strm=NULL;
1915 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1916 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1917 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1918 strm->parentStorage = NULL;
1919 list_remove(cur);
1924 /*********************************************************************
1926 * Internal Method
1928 * Perform the deletion of a complete storage node
1931 static HRESULT deleteStorageProperty(
1932 StorageImpl *parentStorage,
1933 ULONG indexOfPropertyToDelete,
1934 StgProperty propertyToDelete)
1936 IEnumSTATSTG *elements = 0;
1937 IStorage *childStorage = 0;
1938 STATSTG currentElement;
1939 HRESULT hr;
1940 HRESULT destroyHr = S_OK;
1943 * Open the storage and enumerate it
1945 hr = StorageBaseImpl_OpenStorage(
1946 (IStorage*)parentStorage,
1947 propertyToDelete.name,
1949 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1952 &childStorage);
1954 if (hr != S_OK)
1956 return hr;
1960 * Enumerate the elements
1962 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1967 * Obtain the next element
1969 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1970 if (hr==S_OK)
1972 destroyHr = StorageImpl_DestroyElement(
1973 (IStorage*)childStorage,
1974 (OLECHAR*)currentElement.pwcsName);
1976 CoTaskMemFree(currentElement.pwcsName);
1980 * We need to Reset the enumeration every time because we delete elements
1981 * and the enumeration could be invalid
1983 IEnumSTATSTG_Reset(elements);
1985 } while ((hr == S_OK) && (destroyHr == S_OK));
1988 * Invalidate the property by zeroing its name member.
1990 propertyToDelete.sizeOfNameString = 0;
1992 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1993 indexOfPropertyToDelete,
1994 &propertyToDelete);
1996 IStorage_Release(childStorage);
1997 IEnumSTATSTG_Release(elements);
1999 return destroyHr;
2002 /*********************************************************************
2004 * Internal Method
2006 * Perform the deletion of a stream node
2009 static HRESULT deleteStreamProperty(
2010 StorageImpl *parentStorage,
2011 ULONG indexOfPropertyToDelete,
2012 StgProperty propertyToDelete)
2014 IStream *pis;
2015 HRESULT hr;
2016 ULARGE_INTEGER size;
2018 size.u.HighPart = 0;
2019 size.u.LowPart = 0;
2021 hr = StorageBaseImpl_OpenStream(
2022 (IStorage*)parentStorage,
2023 (OLECHAR*)propertyToDelete.name,
2024 NULL,
2025 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2027 &pis);
2029 if (hr!=S_OK)
2031 return(hr);
2035 * Zap the stream
2037 hr = IStream_SetSize(pis, size);
2039 if(hr != S_OK)
2041 return hr;
2045 * Release the stream object.
2047 IStream_Release(pis);
2050 * Invalidate the property by zeroing its name member.
2052 propertyToDelete.sizeOfNameString = 0;
2055 * Here we should re-read the property so we get the updated pointer
2056 * but since we are here to zap it, I don't do it...
2058 StorageImpl_WriteProperty(
2059 parentStorage->base.ancestorStorage,
2060 indexOfPropertyToDelete,
2061 &propertyToDelete);
2063 return S_OK;
2066 /*********************************************************************
2068 * Internal Method
2070 * Finds a placeholder for the StgProperty within the Storage
2073 static HRESULT findPlaceholder(
2074 StorageImpl *storage,
2075 ULONG propertyIndexToStore,
2076 ULONG storePropertyIndex,
2077 INT typeOfRelation)
2079 StgProperty storeProperty;
2080 HRESULT hr = S_OK;
2081 BOOL res = TRUE;
2084 * Read the storage property
2086 res = StorageImpl_ReadProperty(
2087 storage->base.ancestorStorage,
2088 storePropertyIndex,
2089 &storeProperty);
2091 if(! res)
2093 return E_FAIL;
2096 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2098 if (storeProperty.previousProperty != PROPERTY_NULL)
2100 return findPlaceholder(
2101 storage,
2102 propertyIndexToStore,
2103 storeProperty.previousProperty,
2104 typeOfRelation);
2106 else
2108 storeProperty.previousProperty = propertyIndexToStore;
2111 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2113 if (storeProperty.nextProperty != PROPERTY_NULL)
2115 return findPlaceholder(
2116 storage,
2117 propertyIndexToStore,
2118 storeProperty.nextProperty,
2119 typeOfRelation);
2121 else
2123 storeProperty.nextProperty = propertyIndexToStore;
2126 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2128 if (storeProperty.dirProperty != PROPERTY_NULL)
2130 return findPlaceholder(
2131 storage,
2132 propertyIndexToStore,
2133 storeProperty.dirProperty,
2134 typeOfRelation);
2136 else
2138 storeProperty.dirProperty = propertyIndexToStore;
2142 hr = StorageImpl_WriteProperty(
2143 storage->base.ancestorStorage,
2144 storePropertyIndex,
2145 &storeProperty);
2147 if(! hr)
2149 return E_FAIL;
2152 return S_OK;
2155 /*************************************************************************
2157 * Internal Method
2159 * This method takes the previous and the next property link of a property
2160 * to be deleted and find them a place in the Storage.
2162 static HRESULT adjustPropertyChain(
2163 StorageImpl *This,
2164 StgProperty propertyToDelete,
2165 StgProperty parentProperty,
2166 ULONG parentPropertyId,
2167 INT typeOfRelation)
2169 ULONG newLinkProperty = PROPERTY_NULL;
2170 BOOL needToFindAPlaceholder = FALSE;
2171 ULONG storeNode = PROPERTY_NULL;
2172 ULONG toStoreNode = PROPERTY_NULL;
2173 INT relationType = 0;
2174 HRESULT hr = S_OK;
2175 BOOL res = TRUE;
2177 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2179 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2182 * Set the parent previous to the property to delete previous
2184 newLinkProperty = propertyToDelete.previousProperty;
2186 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2189 * We also need to find a storage for the other link, setup variables
2190 * to do this at the end...
2192 needToFindAPlaceholder = TRUE;
2193 storeNode = propertyToDelete.previousProperty;
2194 toStoreNode = propertyToDelete.nextProperty;
2195 relationType = PROPERTY_RELATION_NEXT;
2198 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2201 * Set the parent previous to the property to delete next
2203 newLinkProperty = propertyToDelete.nextProperty;
2207 * Link it for real...
2209 parentProperty.previousProperty = newLinkProperty;
2212 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2214 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2217 * Set the parent next to the property to delete next previous
2219 newLinkProperty = propertyToDelete.previousProperty;
2221 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2224 * We also need to find a storage for the other link, setup variables
2225 * to do this at the end...
2227 needToFindAPlaceholder = TRUE;
2228 storeNode = propertyToDelete.previousProperty;
2229 toStoreNode = propertyToDelete.nextProperty;
2230 relationType = PROPERTY_RELATION_NEXT;
2233 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2236 * Set the parent next to the property to delete next
2238 newLinkProperty = propertyToDelete.nextProperty;
2242 * Link it for real...
2244 parentProperty.nextProperty = newLinkProperty;
2246 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2248 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2251 * Set the parent dir to the property to delete previous
2253 newLinkProperty = propertyToDelete.previousProperty;
2255 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2258 * We also need to find a storage for the other link, setup variables
2259 * to do this at the end...
2261 needToFindAPlaceholder = TRUE;
2262 storeNode = propertyToDelete.previousProperty;
2263 toStoreNode = propertyToDelete.nextProperty;
2264 relationType = PROPERTY_RELATION_NEXT;
2267 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2270 * Set the parent dir to the property to delete next
2272 newLinkProperty = propertyToDelete.nextProperty;
2276 * Link it for real...
2278 parentProperty.dirProperty = newLinkProperty;
2282 * Write back the parent property
2284 res = StorageImpl_WriteProperty(
2285 This->base.ancestorStorage,
2286 parentPropertyId,
2287 &parentProperty);
2288 if(! res)
2290 return E_FAIL;
2294 * If a placeholder is required for the other link, then, find one and
2295 * get out of here...
2297 if (needToFindAPlaceholder)
2299 hr = findPlaceholder(
2300 This,
2301 toStoreNode,
2302 storeNode,
2303 relationType);
2306 return hr;
2310 /******************************************************************************
2311 * SetElementTimes (IStorage)
2313 static HRESULT WINAPI StorageImpl_SetElementTimes(
2314 IStorage* iface,
2315 const OLECHAR *pwcsName,/* [string][in] */
2316 const FILETIME *pctime, /* [in] */
2317 const FILETIME *patime, /* [in] */
2318 const FILETIME *pmtime) /* [in] */
2320 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2321 return S_OK;
2324 /******************************************************************************
2325 * SetStateBits (IStorage)
2327 static HRESULT WINAPI StorageImpl_SetStateBits(
2328 IStorage* iface,
2329 DWORD grfStateBits,/* [in] */
2330 DWORD grfMask) /* [in] */
2332 FIXME("not implemented!\n");
2333 return E_NOTIMPL;
2337 * Virtual function table for the IStorage32Impl class.
2339 static const IStorageVtbl Storage32Impl_Vtbl =
2341 StorageBaseImpl_QueryInterface,
2342 StorageBaseImpl_AddRef,
2343 StorageBaseImpl_Release,
2344 StorageBaseImpl_CreateStream,
2345 StorageBaseImpl_OpenStream,
2346 StorageImpl_CreateStorage,
2347 StorageBaseImpl_OpenStorage,
2348 StorageImpl_CopyTo,
2349 StorageImpl_MoveElementTo,
2350 StorageImpl_Commit,
2351 StorageImpl_Revert,
2352 StorageBaseImpl_EnumElements,
2353 StorageImpl_DestroyElement,
2354 StorageBaseImpl_RenameElement,
2355 StorageImpl_SetElementTimes,
2356 StorageBaseImpl_SetClass,
2357 StorageImpl_SetStateBits,
2358 StorageImpl_Stat
2361 static HRESULT StorageImpl_Construct(
2362 StorageImpl* This,
2363 HANDLE hFile,
2364 LPCOLESTR pwcsName,
2365 ILockBytes* pLkbyt,
2366 DWORD openFlags,
2367 BOOL fileBased,
2368 BOOL fileCreate)
2370 HRESULT hr = S_OK;
2371 StgProperty currentProperty;
2372 BOOL readSuccessful;
2373 ULONG currentPropertyIndex;
2375 if ( FAILED( validateSTGM(openFlags) ))
2376 return STG_E_INVALIDFLAG;
2378 memset(This, 0, sizeof(StorageImpl));
2381 * Initialize stream list
2384 list_init(&This->base.strmHead);
2387 * Initialize the virtual function table.
2389 This->base.lpVtbl = &Storage32Impl_Vtbl;
2390 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2391 This->base.v_destructor = &StorageImpl_Destroy;
2392 This->base.openFlags = (openFlags & ~STGM_CREATE);
2395 * This is the top-level storage so initialize the ancestor pointer
2396 * to this.
2398 This->base.ancestorStorage = This;
2401 * Initialize the physical support of the storage.
2403 This->hFile = hFile;
2406 * Store copy of file path.
2408 if(pwcsName) {
2409 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2410 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2411 if (!This->pwcsName)
2412 return STG_E_INSUFFICIENTMEMORY;
2413 strcpyW(This->pwcsName, pwcsName);
2417 * Initialize the big block cache.
2419 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2420 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2421 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2422 pLkbyt,
2423 openFlags,
2424 This->bigBlockSize,
2425 fileBased);
2427 if (This->bigBlockFile == 0)
2428 return E_FAIL;
2430 if (fileCreate)
2432 ULARGE_INTEGER size;
2433 BYTE* bigBlockBuffer;
2436 * Initialize all header variables:
2437 * - The big block depot consists of one block and it is at block 0
2438 * - The properties start at block 1
2439 * - There is no small block depot
2441 memset( This->bigBlockDepotStart,
2442 BLOCK_UNUSED,
2443 sizeof(This->bigBlockDepotStart));
2445 This->bigBlockDepotCount = 1;
2446 This->bigBlockDepotStart[0] = 0;
2447 This->rootStartBlock = 1;
2448 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2449 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2450 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2451 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2452 This->extBigBlockDepotCount = 0;
2454 StorageImpl_SaveFileHeader(This);
2457 * Add one block for the big block depot and one block for the properties
2459 size.u.HighPart = 0;
2460 size.u.LowPart = This->bigBlockSize * 3;
2461 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2464 * Initialize the big block depot
2466 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2467 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2468 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2469 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2470 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2472 else
2475 * Load the header for the file.
2477 hr = StorageImpl_LoadFileHeader(This);
2479 if (FAILED(hr))
2481 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2483 return hr;
2488 * There is no block depot cached yet.
2490 This->indexBlockDepotCached = 0xFFFFFFFF;
2493 * Start searching for free blocks with block 0.
2495 This->prevFreeBlock = 0;
2498 * Create the block chain abstractions.
2500 if(!(This->rootBlockChain =
2501 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2502 return STG_E_READFAULT;
2504 if(!(This->smallBlockDepotChain =
2505 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2506 PROPERTY_NULL)))
2507 return STG_E_READFAULT;
2510 * Write the root property (memory only)
2512 if (fileCreate)
2514 StgProperty rootProp;
2516 * Initialize the property chain
2518 memset(&rootProp, 0, sizeof(rootProp));
2519 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2520 sizeof(rootProp.name)/sizeof(WCHAR) );
2521 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2522 rootProp.propertyType = PROPTYPE_ROOT;
2523 rootProp.previousProperty = PROPERTY_NULL;
2524 rootProp.nextProperty = PROPERTY_NULL;
2525 rootProp.dirProperty = PROPERTY_NULL;
2526 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2527 rootProp.size.u.HighPart = 0;
2528 rootProp.size.u.LowPart = 0;
2530 StorageImpl_WriteProperty(This, 0, &rootProp);
2534 * Find the ID of the root in the property sets.
2536 currentPropertyIndex = 0;
2540 readSuccessful = StorageImpl_ReadProperty(
2541 This,
2542 currentPropertyIndex,
2543 &currentProperty);
2545 if (readSuccessful)
2547 if ( (currentProperty.sizeOfNameString != 0 ) &&
2548 (currentProperty.propertyType == PROPTYPE_ROOT) )
2550 This->base.rootPropertySetIndex = currentPropertyIndex;
2554 currentPropertyIndex++;
2556 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2558 if (!readSuccessful)
2560 /* TODO CLEANUP */
2561 return STG_E_READFAULT;
2565 * Create the block chain abstraction for the small block root chain.
2567 if(!(This->smallBlockRootChain =
2568 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2569 return STG_E_READFAULT;
2571 return hr;
2574 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2576 StorageImpl *This = (StorageImpl*) iface;
2577 TRACE("(%p)\n", This);
2579 StorageBaseImpl_DeleteAll(&This->base);
2581 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2583 BlockChainStream_Destroy(This->smallBlockRootChain);
2584 BlockChainStream_Destroy(This->rootBlockChain);
2585 BlockChainStream_Destroy(This->smallBlockDepotChain);
2587 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2588 HeapFree(GetProcessHeap(), 0, This);
2591 /******************************************************************************
2592 * Storage32Impl_GetNextFreeBigBlock
2594 * Returns the index of the next free big block.
2595 * If the big block depot is filled, this method will enlarge it.
2598 static ULONG StorageImpl_GetNextFreeBigBlock(
2599 StorageImpl* This)
2601 ULONG depotBlockIndexPos;
2602 void *depotBuffer;
2603 ULONG depotBlockOffset;
2604 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2605 ULONG nextBlockIndex = BLOCK_SPECIAL;
2606 int depotIndex = 0;
2607 ULONG freeBlock = BLOCK_UNUSED;
2609 depotIndex = This->prevFreeBlock / blocksPerDepot;
2610 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2613 * Scan the entire big block depot until we find a block marked free
2615 while (nextBlockIndex != BLOCK_UNUSED)
2617 if (depotIndex < COUNT_BBDEPOTINHEADER)
2619 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2622 * Grow the primary depot.
2624 if (depotBlockIndexPos == BLOCK_UNUSED)
2626 depotBlockIndexPos = depotIndex*blocksPerDepot;
2629 * Add a block depot.
2631 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2632 This->bigBlockDepotCount++;
2633 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2636 * Flag it as a block depot.
2638 StorageImpl_SetNextBlockInChain(This,
2639 depotBlockIndexPos,
2640 BLOCK_SPECIAL);
2642 /* Save new header information.
2644 StorageImpl_SaveFileHeader(This);
2647 else
2649 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2651 if (depotBlockIndexPos == BLOCK_UNUSED)
2654 * Grow the extended depot.
2656 ULONG extIndex = BLOCK_UNUSED;
2657 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2658 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2660 if (extBlockOffset == 0)
2662 /* We need an extended block.
2664 extIndex = Storage32Impl_AddExtBlockDepot(This);
2665 This->extBigBlockDepotCount++;
2666 depotBlockIndexPos = extIndex + 1;
2668 else
2669 depotBlockIndexPos = depotIndex * blocksPerDepot;
2672 * Add a block depot and mark it in the extended block.
2674 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2675 This->bigBlockDepotCount++;
2676 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2678 /* Flag the block depot.
2680 StorageImpl_SetNextBlockInChain(This,
2681 depotBlockIndexPos,
2682 BLOCK_SPECIAL);
2684 /* If necessary, flag the extended depot block.
2686 if (extIndex != BLOCK_UNUSED)
2687 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2689 /* Save header information.
2691 StorageImpl_SaveFileHeader(This);
2695 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2697 if (depotBuffer != 0)
2699 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2700 ( nextBlockIndex != BLOCK_UNUSED))
2702 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2704 if (nextBlockIndex == BLOCK_UNUSED)
2706 freeBlock = (depotIndex * blocksPerDepot) +
2707 (depotBlockOffset/sizeof(ULONG));
2710 depotBlockOffset += sizeof(ULONG);
2713 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2716 depotIndex++;
2717 depotBlockOffset = 0;
2721 * make sure that the block physically exists before using it
2723 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2725 This->prevFreeBlock = freeBlock;
2727 return freeBlock;
2730 /******************************************************************************
2731 * Storage32Impl_AddBlockDepot
2733 * This will create a depot block, essentially it is a block initialized
2734 * to BLOCK_UNUSEDs.
2736 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2738 BYTE* blockBuffer;
2740 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2743 * Initialize blocks as free
2745 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2747 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2750 /******************************************************************************
2751 * Storage32Impl_GetExtDepotBlock
2753 * Returns the index of the block that corresponds to the specified depot
2754 * index. This method is only for depot indexes equal or greater than
2755 * COUNT_BBDEPOTINHEADER.
2757 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2759 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2760 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2761 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2762 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2763 ULONG blockIndex = BLOCK_UNUSED;
2764 ULONG extBlockIndex = This->extBigBlockDepotStart;
2766 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2768 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2769 return BLOCK_UNUSED;
2771 while (extBlockCount > 0)
2773 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2774 extBlockCount--;
2777 if (extBlockIndex != BLOCK_UNUSED)
2779 BYTE* depotBuffer;
2781 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2783 if (depotBuffer != 0)
2785 StorageUtl_ReadDWord(depotBuffer,
2786 extBlockOffset * sizeof(ULONG),
2787 &blockIndex);
2789 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2793 return blockIndex;
2796 /******************************************************************************
2797 * Storage32Impl_SetExtDepotBlock
2799 * Associates the specified block index to the specified depot index.
2800 * This method is only for depot indexes equal or greater than
2801 * COUNT_BBDEPOTINHEADER.
2803 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2805 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2806 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2807 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2808 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2809 ULONG extBlockIndex = This->extBigBlockDepotStart;
2811 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2813 while (extBlockCount > 0)
2815 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2816 extBlockCount--;
2819 if (extBlockIndex != BLOCK_UNUSED)
2821 BYTE* depotBuffer;
2823 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2825 if (depotBuffer != 0)
2827 StorageUtl_WriteDWord(depotBuffer,
2828 extBlockOffset * sizeof(ULONG),
2829 blockIndex);
2831 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2836 /******************************************************************************
2837 * Storage32Impl_AddExtBlockDepot
2839 * Creates an extended depot block.
2841 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2843 ULONG numExtBlocks = This->extBigBlockDepotCount;
2844 ULONG nextExtBlock = This->extBigBlockDepotStart;
2845 BYTE* depotBuffer = NULL;
2846 ULONG index = BLOCK_UNUSED;
2847 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2848 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2849 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2851 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2852 blocksPerDepotBlock;
2854 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2857 * The first extended block.
2859 This->extBigBlockDepotStart = index;
2861 else
2863 unsigned int i;
2865 * Follow the chain to the last one.
2867 for (i = 0; i < (numExtBlocks - 1); i++)
2869 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2873 * Add the new extended block to the chain.
2875 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2876 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2877 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2881 * Initialize this block.
2883 depotBuffer = StorageImpl_GetBigBlock(This, index);
2884 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2885 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2887 return index;
2890 /******************************************************************************
2891 * Storage32Impl_FreeBigBlock
2893 * This method will flag the specified block as free in the big block depot.
2895 static void StorageImpl_FreeBigBlock(
2896 StorageImpl* This,
2897 ULONG blockIndex)
2899 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2901 if (blockIndex < This->prevFreeBlock)
2902 This->prevFreeBlock = blockIndex;
2905 /************************************************************************
2906 * Storage32Impl_GetNextBlockInChain
2908 * This method will retrieve the block index of the next big block in
2909 * in the chain.
2911 * Params: This - Pointer to the Storage object.
2912 * blockIndex - Index of the block to retrieve the chain
2913 * for.
2914 * nextBlockIndex - receives the return value.
2916 * Returns: This method returns the index of the next block in the chain.
2917 * It will return the constants:
2918 * BLOCK_SPECIAL - If the block given was not part of a
2919 * chain.
2920 * BLOCK_END_OF_CHAIN - If the block given was the last in
2921 * a chain.
2922 * BLOCK_UNUSED - If the block given was not past of a chain
2923 * and is available.
2924 * BLOCK_EXTBBDEPOT - This block is part of the extended
2925 * big block depot.
2927 * See Windows documentation for more details on IStorage methods.
2929 static HRESULT StorageImpl_GetNextBlockInChain(
2930 StorageImpl* This,
2931 ULONG blockIndex,
2932 ULONG* nextBlockIndex)
2934 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2935 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2936 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2937 void* depotBuffer;
2938 ULONG depotBlockIndexPos;
2939 int index;
2941 *nextBlockIndex = BLOCK_SPECIAL;
2943 if(depotBlockCount >= This->bigBlockDepotCount)
2945 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2946 This->bigBlockDepotCount);
2947 return STG_E_READFAULT;
2951 * Cache the currently accessed depot block.
2953 if (depotBlockCount != This->indexBlockDepotCached)
2955 This->indexBlockDepotCached = depotBlockCount;
2957 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2959 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2961 else
2964 * We have to look in the extended depot.
2966 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2969 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2971 if (!depotBuffer)
2972 return STG_E_READFAULT;
2974 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2976 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2977 This->blockDepotCached[index] = *nextBlockIndex;
2979 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2982 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2984 return S_OK;
2987 /******************************************************************************
2988 * Storage32Impl_GetNextExtendedBlock
2990 * Given an extended block this method will return the next extended block.
2992 * NOTES:
2993 * The last ULONG of an extended block is the block index of the next
2994 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2995 * depot.
2997 * Return values:
2998 * - The index of the next extended block
2999 * - BLOCK_UNUSED: there is no next extended block.
3000 * - Any other return values denotes failure.
3002 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3004 ULONG nextBlockIndex = BLOCK_SPECIAL;
3005 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3006 void* depotBuffer;
3008 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3010 if (depotBuffer!=0)
3012 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3014 StorageImpl_ReleaseBigBlock(This, depotBuffer);
3017 return nextBlockIndex;
3020 /******************************************************************************
3021 * Storage32Impl_SetNextBlockInChain
3023 * This method will write the index of the specified block's next block
3024 * in the big block depot.
3026 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3027 * do the following
3029 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3030 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3031 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3034 static void StorageImpl_SetNextBlockInChain(
3035 StorageImpl* This,
3036 ULONG blockIndex,
3037 ULONG nextBlock)
3039 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3040 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3041 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3042 ULONG depotBlockIndexPos;
3043 void* depotBuffer;
3045 assert(depotBlockCount < This->bigBlockDepotCount);
3046 assert(blockIndex != nextBlock);
3048 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3050 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3052 else
3055 * We have to look in the extended depot.
3057 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3060 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
3062 if (depotBuffer!=0)
3064 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
3065 StorageImpl_ReleaseBigBlock(This, depotBuffer);
3069 * Update the cached block depot, if necessary.
3071 if (depotBlockCount == This->indexBlockDepotCached)
3073 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3077 /******************************************************************************
3078 * Storage32Impl_LoadFileHeader
3080 * This method will read in the file header, i.e. big block index -1.
3082 static HRESULT StorageImpl_LoadFileHeader(
3083 StorageImpl* This)
3085 HRESULT hr = STG_E_FILENOTFOUND;
3086 void* headerBigBlock = NULL;
3087 int index;
3089 TRACE("\n");
3091 * Get a pointer to the big block of data containing the header.
3093 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
3096 * Extract the information from the header.
3098 if (headerBigBlock!=0)
3101 * Check for the "magic number" signature and return an error if it is not
3102 * found.
3104 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3106 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3107 return STG_E_OLDFORMAT;
3110 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3112 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3113 return STG_E_INVALIDHEADER;
3116 StorageUtl_ReadWord(
3117 headerBigBlock,
3118 OFFSET_BIGBLOCKSIZEBITS,
3119 &This->bigBlockSizeBits);
3121 StorageUtl_ReadWord(
3122 headerBigBlock,
3123 OFFSET_SMALLBLOCKSIZEBITS,
3124 &This->smallBlockSizeBits);
3126 StorageUtl_ReadDWord(
3127 headerBigBlock,
3128 OFFSET_BBDEPOTCOUNT,
3129 &This->bigBlockDepotCount);
3131 StorageUtl_ReadDWord(
3132 headerBigBlock,
3133 OFFSET_ROOTSTARTBLOCK,
3134 &This->rootStartBlock);
3136 StorageUtl_ReadDWord(
3137 headerBigBlock,
3138 OFFSET_SBDEPOTSTART,
3139 &This->smallBlockDepotStart);
3141 StorageUtl_ReadDWord(
3142 headerBigBlock,
3143 OFFSET_EXTBBDEPOTSTART,
3144 &This->extBigBlockDepotStart);
3146 StorageUtl_ReadDWord(
3147 headerBigBlock,
3148 OFFSET_EXTBBDEPOTCOUNT,
3149 &This->extBigBlockDepotCount);
3151 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3153 StorageUtl_ReadDWord(
3154 headerBigBlock,
3155 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3156 &(This->bigBlockDepotStart[index]));
3160 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3162 if ((1 << 2) == 4)
3164 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3165 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3167 else
3169 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3170 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3174 * Right now, the code is making some assumptions about the size of the
3175 * blocks, just make sure they are what we're expecting.
3177 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3178 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3180 WARN("Broken OLE storage file\n");
3181 hr = STG_E_INVALIDHEADER;
3183 else
3184 hr = S_OK;
3187 * Release the block.
3189 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3192 return hr;
3195 /******************************************************************************
3196 * Storage32Impl_SaveFileHeader
3198 * This method will save to the file the header, i.e. big block -1.
3200 static void StorageImpl_SaveFileHeader(
3201 StorageImpl* This)
3203 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3204 int index;
3205 BOOL success;
3208 * Get a pointer to the big block of data containing the header.
3210 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3213 * If the block read failed, the file is probably new.
3215 if (!success)
3218 * Initialize for all unknown fields.
3220 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3223 * Initialize the magic number.
3225 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3228 * And a bunch of things we don't know what they mean
3230 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3231 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3232 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3233 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3237 * Write the information to the header.
3239 StorageUtl_WriteWord(
3240 headerBigBlock,
3241 OFFSET_BIGBLOCKSIZEBITS,
3242 This->bigBlockSizeBits);
3244 StorageUtl_WriteWord(
3245 headerBigBlock,
3246 OFFSET_SMALLBLOCKSIZEBITS,
3247 This->smallBlockSizeBits);
3249 StorageUtl_WriteDWord(
3250 headerBigBlock,
3251 OFFSET_BBDEPOTCOUNT,
3252 This->bigBlockDepotCount);
3254 StorageUtl_WriteDWord(
3255 headerBigBlock,
3256 OFFSET_ROOTSTARTBLOCK,
3257 This->rootStartBlock);
3259 StorageUtl_WriteDWord(
3260 headerBigBlock,
3261 OFFSET_SBDEPOTSTART,
3262 This->smallBlockDepotStart);
3264 StorageUtl_WriteDWord(
3265 headerBigBlock,
3266 OFFSET_SBDEPOTCOUNT,
3267 This->smallBlockDepotChain ?
3268 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3270 StorageUtl_WriteDWord(
3271 headerBigBlock,
3272 OFFSET_EXTBBDEPOTSTART,
3273 This->extBigBlockDepotStart);
3275 StorageUtl_WriteDWord(
3276 headerBigBlock,
3277 OFFSET_EXTBBDEPOTCOUNT,
3278 This->extBigBlockDepotCount);
3280 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3282 StorageUtl_WriteDWord(
3283 headerBigBlock,
3284 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3285 (This->bigBlockDepotStart[index]));
3289 * Write the big block back to the file.
3291 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3294 /******************************************************************************
3295 * Storage32Impl_ReadProperty
3297 * This method will read the specified property from the property chain.
3299 BOOL StorageImpl_ReadProperty(
3300 StorageImpl* This,
3301 ULONG index,
3302 StgProperty* buffer)
3304 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3305 ULARGE_INTEGER offsetInPropSet;
3306 HRESULT readRes;
3307 ULONG bytesRead;
3309 offsetInPropSet.u.HighPart = 0;
3310 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3312 readRes = BlockChainStream_ReadAt(
3313 This->rootBlockChain,
3314 offsetInPropSet,
3315 PROPSET_BLOCK_SIZE,
3316 currentProperty,
3317 &bytesRead);
3319 if (SUCCEEDED(readRes))
3321 /* replace the name of root entry (often "Root Entry") by the file name */
3322 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3323 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3325 memset(buffer->name, 0, sizeof(buffer->name));
3326 memcpy(
3327 buffer->name,
3328 propName,
3329 PROPERTY_NAME_BUFFER_LEN );
3330 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3332 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3334 StorageUtl_ReadWord(
3335 currentProperty,
3336 OFFSET_PS_NAMELENGTH,
3337 &buffer->sizeOfNameString);
3339 StorageUtl_ReadDWord(
3340 currentProperty,
3341 OFFSET_PS_PREVIOUSPROP,
3342 &buffer->previousProperty);
3344 StorageUtl_ReadDWord(
3345 currentProperty,
3346 OFFSET_PS_NEXTPROP,
3347 &buffer->nextProperty);
3349 StorageUtl_ReadDWord(
3350 currentProperty,
3351 OFFSET_PS_DIRPROP,
3352 &buffer->dirProperty);
3354 StorageUtl_ReadGUID(
3355 currentProperty,
3356 OFFSET_PS_GUID,
3357 &buffer->propertyUniqueID);
3359 StorageUtl_ReadDWord(
3360 currentProperty,
3361 OFFSET_PS_TSS1,
3362 &buffer->timeStampS1);
3364 StorageUtl_ReadDWord(
3365 currentProperty,
3366 OFFSET_PS_TSD1,
3367 &buffer->timeStampD1);
3369 StorageUtl_ReadDWord(
3370 currentProperty,
3371 OFFSET_PS_TSS2,
3372 &buffer->timeStampS2);
3374 StorageUtl_ReadDWord(
3375 currentProperty,
3376 OFFSET_PS_TSD2,
3377 &buffer->timeStampD2);
3379 StorageUtl_ReadDWord(
3380 currentProperty,
3381 OFFSET_PS_STARTBLOCK,
3382 &buffer->startingBlock);
3384 StorageUtl_ReadDWord(
3385 currentProperty,
3386 OFFSET_PS_SIZE,
3387 &buffer->size.u.LowPart);
3389 buffer->size.u.HighPart = 0;
3392 return SUCCEEDED(readRes) ? TRUE : FALSE;
3395 /*********************************************************************
3396 * Write the specified property into the property chain
3398 BOOL StorageImpl_WriteProperty(
3399 StorageImpl* This,
3400 ULONG index,
3401 StgProperty* buffer)
3403 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3404 ULARGE_INTEGER offsetInPropSet;
3405 HRESULT writeRes;
3406 ULONG bytesWritten;
3408 offsetInPropSet.u.HighPart = 0;
3409 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3411 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3413 memcpy(
3414 currentProperty + OFFSET_PS_NAME,
3415 buffer->name,
3416 PROPERTY_NAME_BUFFER_LEN );
3418 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3420 StorageUtl_WriteWord(
3421 currentProperty,
3422 OFFSET_PS_NAMELENGTH,
3423 buffer->sizeOfNameString);
3425 StorageUtl_WriteDWord(
3426 currentProperty,
3427 OFFSET_PS_PREVIOUSPROP,
3428 buffer->previousProperty);
3430 StorageUtl_WriteDWord(
3431 currentProperty,
3432 OFFSET_PS_NEXTPROP,
3433 buffer->nextProperty);
3435 StorageUtl_WriteDWord(
3436 currentProperty,
3437 OFFSET_PS_DIRPROP,
3438 buffer->dirProperty);
3440 StorageUtl_WriteGUID(
3441 currentProperty,
3442 OFFSET_PS_GUID,
3443 &buffer->propertyUniqueID);
3445 StorageUtl_WriteDWord(
3446 currentProperty,
3447 OFFSET_PS_TSS1,
3448 buffer->timeStampS1);
3450 StorageUtl_WriteDWord(
3451 currentProperty,
3452 OFFSET_PS_TSD1,
3453 buffer->timeStampD1);
3455 StorageUtl_WriteDWord(
3456 currentProperty,
3457 OFFSET_PS_TSS2,
3458 buffer->timeStampS2);
3460 StorageUtl_WriteDWord(
3461 currentProperty,
3462 OFFSET_PS_TSD2,
3463 buffer->timeStampD2);
3465 StorageUtl_WriteDWord(
3466 currentProperty,
3467 OFFSET_PS_STARTBLOCK,
3468 buffer->startingBlock);
3470 StorageUtl_WriteDWord(
3471 currentProperty,
3472 OFFSET_PS_SIZE,
3473 buffer->size.u.LowPart);
3475 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3476 offsetInPropSet,
3477 PROPSET_BLOCK_SIZE,
3478 currentProperty,
3479 &bytesWritten);
3480 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3483 static BOOL StorageImpl_ReadBigBlock(
3484 StorageImpl* This,
3485 ULONG blockIndex,
3486 void* buffer)
3488 void* bigBlockBuffer;
3490 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3492 if (bigBlockBuffer!=0)
3494 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3496 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3498 return TRUE;
3501 return FALSE;
3504 static BOOL StorageImpl_WriteBigBlock(
3505 StorageImpl* This,
3506 ULONG blockIndex,
3507 void* buffer)
3509 void* bigBlockBuffer;
3511 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3513 if (bigBlockBuffer!=0)
3515 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3517 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3519 return TRUE;
3522 return FALSE;
3525 static void* StorageImpl_GetROBigBlock(
3526 StorageImpl* This,
3527 ULONG blockIndex)
3529 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3532 static void* StorageImpl_GetBigBlock(
3533 StorageImpl* This,
3534 ULONG blockIndex)
3536 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3539 static void StorageImpl_ReleaseBigBlock(
3540 StorageImpl* This,
3541 void* pBigBlock)
3543 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3546 /******************************************************************************
3547 * Storage32Impl_SmallBlocksToBigBlocks
3549 * This method will convert a small block chain to a big block chain.
3550 * The small block chain will be destroyed.
3552 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3553 StorageImpl* This,
3554 SmallBlockChainStream** ppsbChain)
3556 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3557 ULARGE_INTEGER size, offset;
3558 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3559 ULONG propertyIndex;
3560 HRESULT resWrite = S_OK;
3561 HRESULT resRead;
3562 StgProperty chainProperty;
3563 BYTE *buffer;
3564 BlockChainStream *bbTempChain = NULL;
3565 BlockChainStream *bigBlockChain = NULL;
3568 * Create a temporary big block chain that doesn't have
3569 * an associated property. This temporary chain will be
3570 * used to copy data from small blocks to big blocks.
3572 bbTempChain = BlockChainStream_Construct(This,
3573 &bbHeadOfChain,
3574 PROPERTY_NULL);
3575 if(!bbTempChain) return NULL;
3577 * Grow the big block chain.
3579 size = SmallBlockChainStream_GetSize(*ppsbChain);
3580 BlockChainStream_SetSize(bbTempChain, size);
3583 * Copy the contents of the small block chain to the big block chain
3584 * by small block size increments.
3586 offset.u.LowPart = 0;
3587 offset.u.HighPart = 0;
3588 cbTotalRead = 0;
3589 cbTotalWritten = 0;
3591 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3594 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3595 offset,
3596 This->smallBlockSize,
3597 buffer,
3598 &cbRead);
3599 if (FAILED(resRead))
3600 break;
3602 if (cbRead > 0)
3604 cbTotalRead += cbRead;
3606 resWrite = BlockChainStream_WriteAt(bbTempChain,
3607 offset,
3608 cbRead,
3609 buffer,
3610 &cbWritten);
3612 if (FAILED(resWrite))
3613 break;
3615 cbTotalWritten += cbWritten;
3616 offset.u.LowPart += This->smallBlockSize;
3618 } while (cbRead > 0);
3619 HeapFree(GetProcessHeap(),0,buffer);
3621 if (FAILED(resRead) || FAILED(resWrite))
3623 ERR("conversion failed: resRead = 0x%08lx, resWrite = 0x%08lx\n", resRead, resWrite);
3624 BlockChainStream_Destroy(bbTempChain);
3625 return NULL;
3629 * Destroy the small block chain.
3631 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3632 size.u.HighPart = 0;
3633 size.u.LowPart = 0;
3634 SmallBlockChainStream_SetSize(*ppsbChain, size);
3635 SmallBlockChainStream_Destroy(*ppsbChain);
3636 *ppsbChain = 0;
3639 * Change the property information. This chain is now a big block chain
3640 * and it doesn't reside in the small blocks chain anymore.
3642 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3644 chainProperty.startingBlock = bbHeadOfChain;
3646 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3649 * Destroy the temporary propertyless big block chain.
3650 * Create a new big block chain associated with this property.
3652 BlockChainStream_Destroy(bbTempChain);
3653 bigBlockChain = BlockChainStream_Construct(This,
3654 NULL,
3655 propertyIndex);
3657 return bigBlockChain;
3660 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3662 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3664 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3665 HeapFree(GetProcessHeap(), 0, This);
3668 /******************************************************************************
3670 ** Storage32InternalImpl_Commit
3672 ** The non-root storages cannot be opened in transacted mode thus this function
3673 ** does nothing.
3675 static HRESULT WINAPI StorageInternalImpl_Commit(
3676 IStorage* iface,
3677 DWORD grfCommitFlags) /* [in] */
3679 return S_OK;
3682 /******************************************************************************
3684 ** Storage32InternalImpl_Revert
3686 ** The non-root storages cannot be opened in transacted mode thus this function
3687 ** does nothing.
3689 static HRESULT WINAPI StorageInternalImpl_Revert(
3690 IStorage* iface)
3692 return S_OK;
3695 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3697 IStorage_Release((IStorage*)This->parentStorage);
3698 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3699 HeapFree(GetProcessHeap(), 0, This);
3702 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3703 IEnumSTATSTG* iface,
3704 REFIID riid,
3705 void** ppvObject)
3707 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3710 * Perform a sanity check on the parameters.
3712 if (ppvObject==0)
3713 return E_INVALIDARG;
3716 * Initialize the return parameter.
3718 *ppvObject = 0;
3721 * Compare the riid with the interface IDs implemented by this object.
3723 if (IsEqualGUID(&IID_IUnknown, riid) ||
3724 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3726 *ppvObject = (IEnumSTATSTG*)This;
3727 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3728 return S_OK;
3731 return E_NOINTERFACE;
3734 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3735 IEnumSTATSTG* iface)
3737 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3738 return InterlockedIncrement(&This->ref);
3741 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3742 IEnumSTATSTG* iface)
3744 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3746 ULONG newRef;
3748 newRef = InterlockedDecrement(&This->ref);
3751 * If the reference count goes down to 0, perform suicide.
3753 if (newRef==0)
3755 IEnumSTATSTGImpl_Destroy(This);
3758 return newRef;
3761 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3762 IEnumSTATSTG* iface,
3763 ULONG celt,
3764 STATSTG* rgelt,
3765 ULONG* pceltFetched)
3767 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3769 StgProperty currentProperty;
3770 STATSTG* currentReturnStruct = rgelt;
3771 ULONG objectFetched = 0;
3772 ULONG currentSearchNode;
3775 * Perform a sanity check on the parameters.
3777 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3778 return E_INVALIDARG;
3781 * To avoid the special case, get another pointer to a ULONG value if
3782 * the caller didn't supply one.
3784 if (pceltFetched==0)
3785 pceltFetched = &objectFetched;
3788 * Start the iteration, we will iterate until we hit the end of the
3789 * linked list or until we hit the number of items to iterate through
3791 *pceltFetched = 0;
3794 * Start with the node at the top of the stack.
3796 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3798 while ( ( *pceltFetched < celt) &&
3799 ( currentSearchNode!=PROPERTY_NULL) )
3802 * Remove the top node from the stack
3804 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3807 * Read the property from the storage.
3809 StorageImpl_ReadProperty(This->parentStorage,
3810 currentSearchNode,
3811 &currentProperty);
3814 * Copy the information to the return buffer.
3816 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3817 &currentProperty,
3818 STATFLAG_DEFAULT);
3821 * Step to the next item in the iteration
3823 (*pceltFetched)++;
3824 currentReturnStruct++;
3827 * Push the next search node in the search stack.
3829 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3832 * continue the iteration.
3834 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3837 if (*pceltFetched == celt)
3838 return S_OK;
3840 return S_FALSE;
3844 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3845 IEnumSTATSTG* iface,
3846 ULONG celt)
3848 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3850 StgProperty currentProperty;
3851 ULONG objectFetched = 0;
3852 ULONG currentSearchNode;
3855 * Start with the node at the top of the stack.
3857 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3859 while ( (objectFetched < celt) &&
3860 (currentSearchNode!=PROPERTY_NULL) )
3863 * Remove the top node from the stack
3865 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3868 * Read the property from the storage.
3870 StorageImpl_ReadProperty(This->parentStorage,
3871 currentSearchNode,
3872 &currentProperty);
3875 * Step to the next item in the iteration
3877 objectFetched++;
3880 * Push the next search node in the search stack.
3882 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3885 * continue the iteration.
3887 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3890 if (objectFetched == celt)
3891 return S_OK;
3893 return S_FALSE;
3896 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3897 IEnumSTATSTG* iface)
3899 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3901 StgProperty rootProperty;
3902 BOOL readSuccessful;
3905 * Re-initialize the search stack to an empty stack
3907 This->stackSize = 0;
3910 * Read the root property from the storage.
3912 readSuccessful = StorageImpl_ReadProperty(
3913 This->parentStorage,
3914 This->firstPropertyNode,
3915 &rootProperty);
3917 if (readSuccessful)
3919 assert(rootProperty.sizeOfNameString!=0);
3922 * Push the search node in the search stack.
3924 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3927 return S_OK;
3930 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3931 IEnumSTATSTG* iface,
3932 IEnumSTATSTG** ppenum)
3934 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3936 IEnumSTATSTGImpl* newClone;
3939 * Perform a sanity check on the parameters.
3941 if (ppenum==0)
3942 return E_INVALIDARG;
3944 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3945 This->firstPropertyNode);
3949 * The new clone enumeration must point to the same current node as
3950 * the ole one.
3952 newClone->stackSize = This->stackSize ;
3953 newClone->stackMaxSize = This->stackMaxSize ;
3954 newClone->stackToVisit =
3955 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3957 memcpy(
3958 newClone->stackToVisit,
3959 This->stackToVisit,
3960 sizeof(ULONG) * newClone->stackSize);
3962 *ppenum = (IEnumSTATSTG*)newClone;
3965 * Don't forget to nail down a reference to the clone before
3966 * returning it.
3968 IEnumSTATSTGImpl_AddRef(*ppenum);
3970 return S_OK;
3973 static INT IEnumSTATSTGImpl_FindParentProperty(
3974 IEnumSTATSTGImpl *This,
3975 ULONG childProperty,
3976 StgProperty *currentProperty,
3977 ULONG *thisNodeId)
3979 ULONG currentSearchNode;
3980 ULONG foundNode;
3983 * To avoid the special case, get another pointer to a ULONG value if
3984 * the caller didn't supply one.
3986 if (thisNodeId==0)
3987 thisNodeId = &foundNode;
3990 * Start with the node at the top of the stack.
3992 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3995 while (currentSearchNode!=PROPERTY_NULL)
3998 * Store the current node in the returned parameters
4000 *thisNodeId = currentSearchNode;
4003 * Remove the top node from the stack
4005 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4008 * Read the property from the storage.
4010 StorageImpl_ReadProperty(
4011 This->parentStorage,
4012 currentSearchNode,
4013 currentProperty);
4015 if (currentProperty->previousProperty == childProperty)
4016 return PROPERTY_RELATION_PREVIOUS;
4018 else if (currentProperty->nextProperty == childProperty)
4019 return PROPERTY_RELATION_NEXT;
4021 else if (currentProperty->dirProperty == childProperty)
4022 return PROPERTY_RELATION_DIR;
4025 * Push the next search node in the search stack.
4027 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4030 * continue the iteration.
4032 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4035 return PROPERTY_NULL;
4038 static ULONG IEnumSTATSTGImpl_FindProperty(
4039 IEnumSTATSTGImpl* This,
4040 const OLECHAR* lpszPropName,
4041 StgProperty* currentProperty)
4043 ULONG currentSearchNode;
4046 * Start with the node at the top of the stack.
4048 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4050 while (currentSearchNode!=PROPERTY_NULL)
4053 * Remove the top node from the stack
4055 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4058 * Read the property from the storage.
4060 StorageImpl_ReadProperty(This->parentStorage,
4061 currentSearchNode,
4062 currentProperty);
4064 if ( propertyNameCmp(
4065 (const OLECHAR*)currentProperty->name,
4066 (const OLECHAR*)lpszPropName) == 0)
4067 return currentSearchNode;
4070 * Push the next search node in the search stack.
4072 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4075 * continue the iteration.
4077 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4080 return PROPERTY_NULL;
4083 static void IEnumSTATSTGImpl_PushSearchNode(
4084 IEnumSTATSTGImpl* This,
4085 ULONG nodeToPush)
4087 StgProperty rootProperty;
4088 BOOL readSuccessful;
4091 * First, make sure we're not trying to push an unexisting node.
4093 if (nodeToPush==PROPERTY_NULL)
4094 return;
4097 * First push the node to the stack
4099 if (This->stackSize == This->stackMaxSize)
4101 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4103 This->stackToVisit = HeapReAlloc(
4104 GetProcessHeap(),
4106 This->stackToVisit,
4107 sizeof(ULONG) * This->stackMaxSize);
4110 This->stackToVisit[This->stackSize] = nodeToPush;
4111 This->stackSize++;
4114 * Read the root property from the storage.
4116 readSuccessful = StorageImpl_ReadProperty(
4117 This->parentStorage,
4118 nodeToPush,
4119 &rootProperty);
4121 if (readSuccessful)
4123 assert(rootProperty.sizeOfNameString!=0);
4126 * Push the previous search node in the search stack.
4128 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4132 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4133 IEnumSTATSTGImpl* This,
4134 BOOL remove)
4136 ULONG topNode;
4138 if (This->stackSize == 0)
4139 return PROPERTY_NULL;
4141 topNode = This->stackToVisit[This->stackSize-1];
4143 if (remove)
4144 This->stackSize--;
4146 return topNode;
4150 * Virtual function table for the IEnumSTATSTGImpl class.
4152 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4154 IEnumSTATSTGImpl_QueryInterface,
4155 IEnumSTATSTGImpl_AddRef,
4156 IEnumSTATSTGImpl_Release,
4157 IEnumSTATSTGImpl_Next,
4158 IEnumSTATSTGImpl_Skip,
4159 IEnumSTATSTGImpl_Reset,
4160 IEnumSTATSTGImpl_Clone
4163 /******************************************************************************
4164 ** IEnumSTATSTGImpl implementation
4167 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4168 StorageImpl* parentStorage,
4169 ULONG firstPropertyNode)
4171 IEnumSTATSTGImpl* newEnumeration;
4173 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4175 if (newEnumeration!=0)
4178 * Set-up the virtual function table and reference count.
4180 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4181 newEnumeration->ref = 0;
4184 * We want to nail-down the reference to the storage in case the
4185 * enumeration out-lives the storage in the client application.
4187 newEnumeration->parentStorage = parentStorage;
4188 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4190 newEnumeration->firstPropertyNode = firstPropertyNode;
4193 * Initialize the search stack
4195 newEnumeration->stackSize = 0;
4196 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4197 newEnumeration->stackToVisit =
4198 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4201 * Make sure the current node of the iterator is the first one.
4203 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4206 return newEnumeration;
4210 * Virtual function table for the Storage32InternalImpl class.
4212 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4214 StorageBaseImpl_QueryInterface,
4215 StorageBaseImpl_AddRef,
4216 StorageBaseImpl_Release,
4217 StorageBaseImpl_CreateStream,
4218 StorageBaseImpl_OpenStream,
4219 StorageImpl_CreateStorage,
4220 StorageBaseImpl_OpenStorage,
4221 StorageImpl_CopyTo,
4222 StorageImpl_MoveElementTo,
4223 StorageInternalImpl_Commit,
4224 StorageInternalImpl_Revert,
4225 StorageBaseImpl_EnumElements,
4226 StorageImpl_DestroyElement,
4227 StorageBaseImpl_RenameElement,
4228 StorageImpl_SetElementTimes,
4229 StorageBaseImpl_SetClass,
4230 StorageImpl_SetStateBits,
4231 StorageBaseImpl_Stat
4234 /******************************************************************************
4235 ** Storage32InternalImpl implementation
4238 static StorageInternalImpl* StorageInternalImpl_Construct(
4239 StorageImpl* ancestorStorage,
4240 DWORD openFlags,
4241 ULONG rootPropertyIndex)
4243 StorageInternalImpl* newStorage;
4246 * Allocate space for the new storage object
4248 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4250 if (newStorage!=0)
4252 memset(newStorage, 0, sizeof(StorageInternalImpl));
4255 * Initialize the stream list
4258 list_init(&newStorage->base.strmHead);
4261 * Initialize the virtual function table.
4263 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4264 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4265 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4268 * Keep the ancestor storage pointer and nail a reference to it.
4270 newStorage->base.ancestorStorage = ancestorStorage;
4271 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4274 * Keep the index of the root property set for this storage,
4276 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4278 return newStorage;
4281 return 0;
4284 /******************************************************************************
4285 ** StorageUtl implementation
4288 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4290 WORD tmp;
4292 memcpy(&tmp, buffer+offset, sizeof(WORD));
4293 *value = le16toh(tmp);
4296 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4298 value = htole16(value);
4299 memcpy(buffer+offset, &value, sizeof(WORD));
4302 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4304 DWORD tmp;
4306 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4307 *value = le32toh(tmp);
4310 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4312 value = htole32(value);
4313 memcpy(buffer+offset, &value, sizeof(DWORD));
4316 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4317 ULARGE_INTEGER* value)
4319 #ifdef WORDS_BIGENDIAN
4320 ULARGE_INTEGER tmp;
4322 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4323 value->u.LowPart = htole32(tmp.u.HighPart);
4324 value->u.HighPart = htole32(tmp.u.LowPart);
4325 #else
4326 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4327 #endif
4330 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4331 const ULARGE_INTEGER *value)
4333 #ifdef WORDS_BIGENDIAN
4334 ULARGE_INTEGER tmp;
4336 tmp.u.LowPart = htole32(value->u.HighPart);
4337 tmp.u.HighPart = htole32(value->u.LowPart);
4338 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4339 #else
4340 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4341 #endif
4344 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4346 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4347 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4348 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4350 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4353 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4355 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4356 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4357 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4359 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4362 void StorageUtl_CopyPropertyToSTATSTG(
4363 STATSTG* destination,
4364 StgProperty* source,
4365 int statFlags)
4368 * The copy of the string occurs only when the flag is not set
4370 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4371 (source->name == NULL) ||
4372 (source->name[0] == 0) )
4374 destination->pwcsName = 0;
4376 else
4378 destination->pwcsName =
4379 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4381 strcpyW((LPWSTR)destination->pwcsName, source->name);
4384 switch (source->propertyType)
4386 case PROPTYPE_STORAGE:
4387 case PROPTYPE_ROOT:
4388 destination->type = STGTY_STORAGE;
4389 break;
4390 case PROPTYPE_STREAM:
4391 destination->type = STGTY_STREAM;
4392 break;
4393 default:
4394 destination->type = STGTY_STREAM;
4395 break;
4398 destination->cbSize = source->size;
4400 currentReturnStruct->mtime = {0}; TODO
4401 currentReturnStruct->ctime = {0};
4402 currentReturnStruct->atime = {0};
4404 destination->grfMode = 0;
4405 destination->grfLocksSupported = 0;
4406 destination->clsid = source->propertyUniqueID;
4407 destination->grfStateBits = 0;
4408 destination->reserved = 0;
4411 /******************************************************************************
4412 ** BlockChainStream implementation
4415 BlockChainStream* BlockChainStream_Construct(
4416 StorageImpl* parentStorage,
4417 ULONG* headOfStreamPlaceHolder,
4418 ULONG propertyIndex)
4420 BlockChainStream* newStream;
4421 ULONG blockIndex;
4423 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4425 newStream->parentStorage = parentStorage;
4426 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4427 newStream->ownerPropertyIndex = propertyIndex;
4428 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4429 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4430 newStream->numBlocks = 0;
4432 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4434 while (blockIndex != BLOCK_END_OF_CHAIN)
4436 newStream->numBlocks++;
4437 newStream->tailIndex = blockIndex;
4439 if(FAILED(StorageImpl_GetNextBlockInChain(
4440 parentStorage,
4441 blockIndex,
4442 &blockIndex)))
4444 HeapFree(GetProcessHeap(), 0, newStream);
4445 return NULL;
4449 return newStream;
4452 void BlockChainStream_Destroy(BlockChainStream* This)
4454 HeapFree(GetProcessHeap(), 0, This);
4457 /******************************************************************************
4458 * BlockChainStream_GetHeadOfChain
4460 * Returns the head of this stream chain.
4461 * Some special chains don't have properties, their heads are kept in
4462 * This->headOfStreamPlaceHolder.
4465 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4467 StgProperty chainProperty;
4468 BOOL readSuccessful;
4470 if (This->headOfStreamPlaceHolder != 0)
4471 return *(This->headOfStreamPlaceHolder);
4473 if (This->ownerPropertyIndex != PROPERTY_NULL)
4475 readSuccessful = StorageImpl_ReadProperty(
4476 This->parentStorage,
4477 This->ownerPropertyIndex,
4478 &chainProperty);
4480 if (readSuccessful)
4482 return chainProperty.startingBlock;
4486 return BLOCK_END_OF_CHAIN;
4489 /******************************************************************************
4490 * BlockChainStream_GetCount
4492 * Returns the number of blocks that comprises this chain.
4493 * This is not the size of the stream as the last block may not be full!
4496 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4498 ULONG blockIndex;
4499 ULONG count = 0;
4501 blockIndex = BlockChainStream_GetHeadOfChain(This);
4503 while (blockIndex != BLOCK_END_OF_CHAIN)
4505 count++;
4507 if(FAILED(StorageImpl_GetNextBlockInChain(
4508 This->parentStorage,
4509 blockIndex,
4510 &blockIndex)))
4511 return 0;
4514 return count;
4517 /******************************************************************************
4518 * BlockChainStream_ReadAt
4520 * Reads a specified number of bytes from this chain at the specified offset.
4521 * bytesRead may be NULL.
4522 * Failure will be returned if the specified number of bytes has not been read.
4524 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4525 ULARGE_INTEGER offset,
4526 ULONG size,
4527 void* buffer,
4528 ULONG* bytesRead)
4530 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4531 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4532 ULONG bytesToReadInBuffer;
4533 ULONG blockIndex;
4534 BYTE* bufferWalker;
4535 BYTE* bigBlockBuffer;
4538 * Find the first block in the stream that contains part of the buffer.
4540 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4541 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4542 (blockNoInSequence < This->lastBlockNoInSequence) )
4544 blockIndex = BlockChainStream_GetHeadOfChain(This);
4545 This->lastBlockNoInSequence = blockNoInSequence;
4547 else
4549 ULONG temp = blockNoInSequence;
4551 blockIndex = This->lastBlockNoInSequenceIndex;
4552 blockNoInSequence -= This->lastBlockNoInSequence;
4553 This->lastBlockNoInSequence = temp;
4556 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4558 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4559 return STG_E_DOCFILECORRUPT;
4560 blockNoInSequence--;
4563 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4564 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4566 This->lastBlockNoInSequenceIndex = blockIndex;
4569 * Start reading the buffer.
4571 *bytesRead = 0;
4572 bufferWalker = buffer;
4574 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4577 * Calculate how many bytes we can copy from this big block.
4579 bytesToReadInBuffer =
4580 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4583 * Copy those bytes to the buffer
4585 bigBlockBuffer =
4586 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4587 if (!bigBlockBuffer)
4588 return STG_E_READFAULT;
4590 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4592 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4595 * Step to the next big block.
4597 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4598 return STG_E_DOCFILECORRUPT;
4600 bufferWalker += bytesToReadInBuffer;
4601 size -= bytesToReadInBuffer;
4602 *bytesRead += bytesToReadInBuffer;
4603 offsetInBlock = 0; /* There is no offset on the next block */
4607 return (size == 0) ? S_OK : STG_E_READFAULT;
4610 /******************************************************************************
4611 * BlockChainStream_WriteAt
4613 * Writes the specified number of bytes to this chain at the specified offset.
4614 * bytesWritten may be NULL.
4615 * Will fail if not all specified number of bytes have been written.
4617 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4618 ULARGE_INTEGER offset,
4619 ULONG size,
4620 const void* buffer,
4621 ULONG* bytesWritten)
4623 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4624 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4625 ULONG bytesToWrite;
4626 ULONG blockIndex;
4627 const BYTE* bufferWalker;
4628 BYTE* bigBlockBuffer;
4631 * Find the first block in the stream that contains part of the buffer.
4633 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4634 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4635 (blockNoInSequence < This->lastBlockNoInSequence) )
4637 blockIndex = BlockChainStream_GetHeadOfChain(This);
4638 This->lastBlockNoInSequence = blockNoInSequence;
4640 else
4642 ULONG temp = blockNoInSequence;
4644 blockIndex = This->lastBlockNoInSequenceIndex;
4645 blockNoInSequence -= This->lastBlockNoInSequence;
4646 This->lastBlockNoInSequence = temp;
4649 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4651 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4652 &blockIndex)))
4653 return STG_E_DOCFILECORRUPT;
4654 blockNoInSequence--;
4657 This->lastBlockNoInSequenceIndex = blockIndex;
4659 /* BlockChainStream_SetSize should have already been called to ensure we have
4660 * enough blocks in the chain to write into */
4661 if (blockIndex == BLOCK_END_OF_CHAIN)
4663 ERR("not enough blocks in chain to write data\n");
4664 return STG_E_DOCFILECORRUPT;
4668 * Here, I'm casting away the constness on the buffer variable
4669 * This is OK since we don't intend to modify that buffer.
4671 *bytesWritten = 0;
4672 bufferWalker = (const BYTE*)buffer;
4674 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4677 * Calculate how many bytes we can copy from this big block.
4679 bytesToWrite =
4680 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4683 * Copy those bytes to the buffer
4685 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4687 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4689 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4692 * Step to the next big block.
4694 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4695 &blockIndex)))
4696 return STG_E_DOCFILECORRUPT;
4697 bufferWalker += bytesToWrite;
4698 size -= bytesToWrite;
4699 *bytesWritten += bytesToWrite;
4700 offsetInBlock = 0; /* There is no offset on the next block */
4703 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4706 /******************************************************************************
4707 * BlockChainStream_Shrink
4709 * Shrinks this chain in the big block depot.
4711 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4712 ULARGE_INTEGER newSize)
4714 ULONG blockIndex, extraBlock;
4715 ULONG numBlocks;
4716 ULONG count = 1;
4719 * Reset the last accessed block cache.
4721 This->lastBlockNoInSequence = 0xFFFFFFFF;
4722 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4725 * Figure out how many blocks are needed to contain the new size
4727 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4729 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4730 numBlocks++;
4732 blockIndex = BlockChainStream_GetHeadOfChain(This);
4735 * Go to the new end of chain
4737 while (count < numBlocks)
4739 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4740 &blockIndex)))
4741 return FALSE;
4742 count++;
4745 /* Get the next block before marking the new end */
4746 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4747 &extraBlock)))
4748 return FALSE;
4750 /* Mark the new end of chain */
4751 StorageImpl_SetNextBlockInChain(
4752 This->parentStorage,
4753 blockIndex,
4754 BLOCK_END_OF_CHAIN);
4756 This->tailIndex = blockIndex;
4757 This->numBlocks = numBlocks;
4760 * Mark the extra blocks as free
4762 while (extraBlock != BLOCK_END_OF_CHAIN)
4764 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4765 &blockIndex)))
4766 return FALSE;
4767 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4768 extraBlock = blockIndex;
4771 return TRUE;
4774 /******************************************************************************
4775 * BlockChainStream_Enlarge
4777 * Grows this chain in the big block depot.
4779 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4780 ULARGE_INTEGER newSize)
4782 ULONG blockIndex, currentBlock;
4783 ULONG newNumBlocks;
4784 ULONG oldNumBlocks = 0;
4786 blockIndex = BlockChainStream_GetHeadOfChain(This);
4789 * Empty chain. Create the head.
4791 if (blockIndex == BLOCK_END_OF_CHAIN)
4793 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4794 StorageImpl_SetNextBlockInChain(This->parentStorage,
4795 blockIndex,
4796 BLOCK_END_OF_CHAIN);
4798 if (This->headOfStreamPlaceHolder != 0)
4800 *(This->headOfStreamPlaceHolder) = blockIndex;
4802 else
4804 StgProperty chainProp;
4805 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4807 StorageImpl_ReadProperty(
4808 This->parentStorage,
4809 This->ownerPropertyIndex,
4810 &chainProp);
4812 chainProp.startingBlock = blockIndex;
4814 StorageImpl_WriteProperty(
4815 This->parentStorage,
4816 This->ownerPropertyIndex,
4817 &chainProp);
4820 This->tailIndex = blockIndex;
4821 This->numBlocks = 1;
4825 * Figure out how many blocks are needed to contain this stream
4827 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4829 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4830 newNumBlocks++;
4833 * Go to the current end of chain
4835 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4837 currentBlock = blockIndex;
4839 while (blockIndex != BLOCK_END_OF_CHAIN)
4841 This->numBlocks++;
4842 currentBlock = blockIndex;
4844 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4845 &blockIndex)))
4846 return FALSE;
4849 This->tailIndex = currentBlock;
4852 currentBlock = This->tailIndex;
4853 oldNumBlocks = This->numBlocks;
4856 * Add new blocks to the chain
4858 if (oldNumBlocks < newNumBlocks)
4860 while (oldNumBlocks < newNumBlocks)
4862 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4864 StorageImpl_SetNextBlockInChain(
4865 This->parentStorage,
4866 currentBlock,
4867 blockIndex);
4869 StorageImpl_SetNextBlockInChain(
4870 This->parentStorage,
4871 blockIndex,
4872 BLOCK_END_OF_CHAIN);
4874 currentBlock = blockIndex;
4875 oldNumBlocks++;
4878 This->tailIndex = blockIndex;
4879 This->numBlocks = newNumBlocks;
4882 return TRUE;
4885 /******************************************************************************
4886 * BlockChainStream_SetSize
4888 * Sets the size of this stream. The big block depot will be updated.
4889 * The file will grow if we grow the chain.
4891 * TODO: Free the actual blocks in the file when we shrink the chain.
4892 * Currently, the blocks are still in the file. So the file size
4893 * doesn't shrink even if we shrink streams.
4895 BOOL BlockChainStream_SetSize(
4896 BlockChainStream* This,
4897 ULARGE_INTEGER newSize)
4899 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4901 if (newSize.u.LowPart == size.u.LowPart)
4902 return TRUE;
4904 if (newSize.u.LowPart < size.u.LowPart)
4906 BlockChainStream_Shrink(This, newSize);
4908 else
4910 BlockChainStream_Enlarge(This, newSize);
4913 return TRUE;
4916 /******************************************************************************
4917 * BlockChainStream_GetSize
4919 * Returns the size of this chain.
4920 * Will return the block count if this chain doesn't have a property.
4922 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4924 StgProperty chainProperty;
4926 if(This->headOfStreamPlaceHolder == NULL)
4929 * This chain is a data stream read the property and return
4930 * the appropriate size
4932 StorageImpl_ReadProperty(
4933 This->parentStorage,
4934 This->ownerPropertyIndex,
4935 &chainProperty);
4937 return chainProperty.size;
4939 else
4942 * this chain is a chain that does not have a property, figure out the
4943 * size by making the product number of used blocks times the
4944 * size of them
4946 ULARGE_INTEGER result;
4947 result.u.HighPart = 0;
4949 result.u.LowPart =
4950 BlockChainStream_GetCount(This) *
4951 This->parentStorage->bigBlockSize;
4953 return result;
4957 /******************************************************************************
4958 ** SmallBlockChainStream implementation
4961 SmallBlockChainStream* SmallBlockChainStream_Construct(
4962 StorageImpl* parentStorage,
4963 ULONG propertyIndex)
4965 SmallBlockChainStream* newStream;
4967 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4969 newStream->parentStorage = parentStorage;
4970 newStream->ownerPropertyIndex = propertyIndex;
4972 return newStream;
4975 void SmallBlockChainStream_Destroy(
4976 SmallBlockChainStream* This)
4978 HeapFree(GetProcessHeap(), 0, This);
4981 /******************************************************************************
4982 * SmallBlockChainStream_GetHeadOfChain
4984 * Returns the head of this chain of small blocks.
4986 static ULONG SmallBlockChainStream_GetHeadOfChain(
4987 SmallBlockChainStream* This)
4989 StgProperty chainProperty;
4990 BOOL readSuccessful;
4992 if (This->ownerPropertyIndex)
4994 readSuccessful = StorageImpl_ReadProperty(
4995 This->parentStorage,
4996 This->ownerPropertyIndex,
4997 &chainProperty);
4999 if (readSuccessful)
5001 return chainProperty.startingBlock;
5006 return BLOCK_END_OF_CHAIN;
5009 /******************************************************************************
5010 * SmallBlockChainStream_GetNextBlockInChain
5012 * Returns the index of the next small block in this chain.
5014 * Return Values:
5015 * - BLOCK_END_OF_CHAIN: end of this chain
5016 * - BLOCK_UNUSED: small block 'blockIndex' is free
5018 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5019 SmallBlockChainStream* This,
5020 ULONG blockIndex,
5021 ULONG* nextBlockInChain)
5023 ULARGE_INTEGER offsetOfBlockInDepot;
5024 DWORD buffer;
5025 ULONG bytesRead;
5026 HRESULT res;
5028 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5030 offsetOfBlockInDepot.u.HighPart = 0;
5031 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5034 * Read those bytes in the buffer from the small block file.
5036 res = BlockChainStream_ReadAt(
5037 This->parentStorage->smallBlockDepotChain,
5038 offsetOfBlockInDepot,
5039 sizeof(DWORD),
5040 &buffer,
5041 &bytesRead);
5043 if (SUCCEEDED(res))
5045 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5046 return S_OK;
5049 return res;
5052 /******************************************************************************
5053 * SmallBlockChainStream_SetNextBlockInChain
5055 * Writes the index of the next block of the specified block in the small
5056 * block depot.
5057 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5058 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5060 static void SmallBlockChainStream_SetNextBlockInChain(
5061 SmallBlockChainStream* This,
5062 ULONG blockIndex,
5063 ULONG nextBlock)
5065 ULARGE_INTEGER offsetOfBlockInDepot;
5066 DWORD buffer;
5067 ULONG bytesWritten;
5069 offsetOfBlockInDepot.u.HighPart = 0;
5070 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5072 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5075 * Read those bytes in the buffer from the small block file.
5077 BlockChainStream_WriteAt(
5078 This->parentStorage->smallBlockDepotChain,
5079 offsetOfBlockInDepot,
5080 sizeof(DWORD),
5081 &buffer,
5082 &bytesWritten);
5085 /******************************************************************************
5086 * SmallBlockChainStream_FreeBlock
5088 * Flag small block 'blockIndex' as free in the small block depot.
5090 static void SmallBlockChainStream_FreeBlock(
5091 SmallBlockChainStream* This,
5092 ULONG blockIndex)
5094 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5097 /******************************************************************************
5098 * SmallBlockChainStream_GetNextFreeBlock
5100 * Returns the index of a free small block. The small block depot will be
5101 * enlarged if necessary. The small block chain will also be enlarged if
5102 * necessary.
5104 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5105 SmallBlockChainStream* This)
5107 ULARGE_INTEGER offsetOfBlockInDepot;
5108 DWORD buffer;
5109 ULONG bytesRead;
5110 ULONG blockIndex = 0;
5111 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5112 HRESULT res = S_OK;
5113 ULONG smallBlocksPerBigBlock;
5115 offsetOfBlockInDepot.u.HighPart = 0;
5118 * Scan the small block depot for a free block
5120 while (nextBlockIndex != BLOCK_UNUSED)
5122 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5124 res = BlockChainStream_ReadAt(
5125 This->parentStorage->smallBlockDepotChain,
5126 offsetOfBlockInDepot,
5127 sizeof(DWORD),
5128 &buffer,
5129 &bytesRead);
5132 * If we run out of space for the small block depot, enlarge it
5134 if (SUCCEEDED(res))
5136 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5138 if (nextBlockIndex != BLOCK_UNUSED)
5139 blockIndex++;
5141 else
5143 ULONG count =
5144 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5146 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5147 ULONG nextBlock, newsbdIndex;
5148 BYTE* smallBlockDepot;
5150 nextBlock = sbdIndex;
5151 while (nextBlock != BLOCK_END_OF_CHAIN)
5153 sbdIndex = nextBlock;
5154 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5157 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5158 if (sbdIndex != BLOCK_END_OF_CHAIN)
5159 StorageImpl_SetNextBlockInChain(
5160 This->parentStorage,
5161 sbdIndex,
5162 newsbdIndex);
5164 StorageImpl_SetNextBlockInChain(
5165 This->parentStorage,
5166 newsbdIndex,
5167 BLOCK_END_OF_CHAIN);
5170 * Initialize all the small blocks to free
5172 smallBlockDepot =
5173 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5175 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5176 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5178 if (count == 0)
5181 * We have just created the small block depot.
5183 StgProperty rootProp;
5184 ULONG sbStartIndex;
5187 * Save it in the header
5189 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5190 StorageImpl_SaveFileHeader(This->parentStorage);
5193 * And allocate the first big block that will contain small blocks
5195 sbStartIndex =
5196 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5198 StorageImpl_SetNextBlockInChain(
5199 This->parentStorage,
5200 sbStartIndex,
5201 BLOCK_END_OF_CHAIN);
5203 StorageImpl_ReadProperty(
5204 This->parentStorage,
5205 This->parentStorage->base.rootPropertySetIndex,
5206 &rootProp);
5208 rootProp.startingBlock = sbStartIndex;
5209 rootProp.size.u.HighPart = 0;
5210 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5212 StorageImpl_WriteProperty(
5213 This->parentStorage,
5214 This->parentStorage->base.rootPropertySetIndex,
5215 &rootProp);
5220 smallBlocksPerBigBlock =
5221 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5224 * Verify if we have to allocate big blocks to contain small blocks
5226 if (blockIndex % smallBlocksPerBigBlock == 0)
5228 StgProperty rootProp;
5229 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5231 StorageImpl_ReadProperty(
5232 This->parentStorage,
5233 This->parentStorage->base.rootPropertySetIndex,
5234 &rootProp);
5236 if (rootProp.size.u.LowPart <
5237 (blocksRequired * This->parentStorage->bigBlockSize))
5239 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5241 BlockChainStream_SetSize(
5242 This->parentStorage->smallBlockRootChain,
5243 rootProp.size);
5245 StorageImpl_WriteProperty(
5246 This->parentStorage,
5247 This->parentStorage->base.rootPropertySetIndex,
5248 &rootProp);
5252 return blockIndex;
5255 /******************************************************************************
5256 * SmallBlockChainStream_ReadAt
5258 * Reads a specified number of bytes from this chain at the specified offset.
5259 * bytesRead may be NULL.
5260 * Failure will be returned if the specified number of bytes has not been read.
5262 HRESULT SmallBlockChainStream_ReadAt(
5263 SmallBlockChainStream* This,
5264 ULARGE_INTEGER offset,
5265 ULONG size,
5266 void* buffer,
5267 ULONG* bytesRead)
5269 HRESULT rc = S_OK;
5270 ULARGE_INTEGER offsetInBigBlockFile;
5271 ULONG blockNoInSequence =
5272 offset.u.LowPart / This->parentStorage->smallBlockSize;
5274 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5275 ULONG bytesToReadInBuffer;
5276 ULONG blockIndex;
5277 ULONG bytesReadFromBigBlockFile;
5278 BYTE* bufferWalker;
5281 * This should never happen on a small block file.
5283 assert(offset.u.HighPart==0);
5286 * Find the first block in the stream that contains part of the buffer.
5288 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5290 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5292 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5293 if(FAILED(rc))
5294 return rc;
5295 blockNoInSequence--;
5299 * Start reading the buffer.
5301 *bytesRead = 0;
5302 bufferWalker = buffer;
5304 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5307 * Calculate how many bytes we can copy from this small block.
5309 bytesToReadInBuffer =
5310 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5313 * Calculate the offset of the small block in the small block file.
5315 offsetInBigBlockFile.u.HighPart = 0;
5316 offsetInBigBlockFile.u.LowPart =
5317 blockIndex * This->parentStorage->smallBlockSize;
5319 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5322 * Read those bytes in the buffer from the small block file.
5323 * The small block has already been identified so it shouldn't fail
5324 * unless the file is corrupt.
5326 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5327 offsetInBigBlockFile,
5328 bytesToReadInBuffer,
5329 bufferWalker,
5330 &bytesReadFromBigBlockFile);
5332 if (FAILED(rc))
5333 return rc;
5336 * Step to the next big block.
5338 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5339 if(FAILED(rc))
5340 return STG_E_DOCFILECORRUPT;
5342 bufferWalker += bytesReadFromBigBlockFile;
5343 size -= bytesReadFromBigBlockFile;
5344 *bytesRead += bytesReadFromBigBlockFile;
5345 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5348 return (size == 0) ? S_OK : STG_E_READFAULT;
5351 /******************************************************************************
5352 * SmallBlockChainStream_WriteAt
5354 * Writes the specified number of bytes to this chain at the specified offset.
5355 * bytesWritten may be NULL.
5356 * Will fail if not all specified number of bytes have been written.
5358 HRESULT SmallBlockChainStream_WriteAt(
5359 SmallBlockChainStream* This,
5360 ULARGE_INTEGER offset,
5361 ULONG size,
5362 const void* buffer,
5363 ULONG* bytesWritten)
5365 ULARGE_INTEGER offsetInBigBlockFile;
5366 ULONG blockNoInSequence =
5367 offset.u.LowPart / This->parentStorage->smallBlockSize;
5369 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5370 ULONG bytesToWriteInBuffer;
5371 ULONG blockIndex;
5372 ULONG bytesWrittenToBigBlockFile;
5373 const BYTE* bufferWalker;
5374 HRESULT res;
5377 * This should never happen on a small block file.
5379 assert(offset.u.HighPart==0);
5382 * Find the first block in the stream that contains part of the buffer.
5384 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5386 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5388 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5389 return STG_E_DOCFILECORRUPT;
5390 blockNoInSequence--;
5394 * Start writing the buffer.
5396 * Here, I'm casting away the constness on the buffer variable
5397 * This is OK since we don't intend to modify that buffer.
5399 *bytesWritten = 0;
5400 bufferWalker = (const BYTE*)buffer;
5401 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5404 * Calculate how many bytes we can copy to this small block.
5406 bytesToWriteInBuffer =
5407 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5410 * Calculate the offset of the small block in the small block file.
5412 offsetInBigBlockFile.u.HighPart = 0;
5413 offsetInBigBlockFile.u.LowPart =
5414 blockIndex * This->parentStorage->smallBlockSize;
5416 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5419 * Write those bytes in the buffer to the small block file.
5421 res = BlockChainStream_WriteAt(
5422 This->parentStorage->smallBlockRootChain,
5423 offsetInBigBlockFile,
5424 bytesToWriteInBuffer,
5425 bufferWalker,
5426 &bytesWrittenToBigBlockFile);
5427 if (FAILED(res))
5428 return res;
5431 * Step to the next big block.
5433 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5434 &blockIndex)))
5435 return FALSE;
5436 bufferWalker += bytesWrittenToBigBlockFile;
5437 size -= bytesWrittenToBigBlockFile;
5438 *bytesWritten += bytesWrittenToBigBlockFile;
5439 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5442 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5445 /******************************************************************************
5446 * SmallBlockChainStream_Shrink
5448 * Shrinks this chain in the small block depot.
5450 static BOOL SmallBlockChainStream_Shrink(
5451 SmallBlockChainStream* This,
5452 ULARGE_INTEGER newSize)
5454 ULONG blockIndex, extraBlock;
5455 ULONG numBlocks;
5456 ULONG count = 0;
5458 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5460 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5461 numBlocks++;
5463 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5466 * Go to the new end of chain
5468 while (count < numBlocks)
5470 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5471 &blockIndex)))
5472 return FALSE;
5473 count++;
5477 * If the count is 0, we have a special case, the head of the chain was
5478 * just freed.
5480 if (count == 0)
5482 StgProperty chainProp;
5484 StorageImpl_ReadProperty(This->parentStorage,
5485 This->ownerPropertyIndex,
5486 &chainProp);
5488 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5490 StorageImpl_WriteProperty(This->parentStorage,
5491 This->ownerPropertyIndex,
5492 &chainProp);
5495 * We start freeing the chain at the head block.
5497 extraBlock = blockIndex;
5499 else
5501 /* Get the next block before marking the new end */
5502 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5503 &extraBlock)))
5504 return FALSE;
5506 /* Mark the new end of chain */
5507 SmallBlockChainStream_SetNextBlockInChain(
5508 This,
5509 blockIndex,
5510 BLOCK_END_OF_CHAIN);
5514 * Mark the extra blocks as free
5516 while (extraBlock != BLOCK_END_OF_CHAIN)
5518 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5519 &blockIndex)))
5520 return FALSE;
5521 SmallBlockChainStream_FreeBlock(This, extraBlock);
5522 extraBlock = blockIndex;
5525 return TRUE;
5528 /******************************************************************************
5529 * SmallBlockChainStream_Enlarge
5531 * Grows this chain in the small block depot.
5533 static BOOL SmallBlockChainStream_Enlarge(
5534 SmallBlockChainStream* This,
5535 ULARGE_INTEGER newSize)
5537 ULONG blockIndex, currentBlock;
5538 ULONG newNumBlocks;
5539 ULONG oldNumBlocks = 0;
5541 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5544 * Empty chain
5546 if (blockIndex == BLOCK_END_OF_CHAIN)
5549 StgProperty chainProp;
5551 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5552 &chainProp);
5554 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5556 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5557 &chainProp);
5559 blockIndex = chainProp.startingBlock;
5560 SmallBlockChainStream_SetNextBlockInChain(
5561 This,
5562 blockIndex,
5563 BLOCK_END_OF_CHAIN);
5566 currentBlock = blockIndex;
5569 * Figure out how many blocks are needed to contain this stream
5571 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5573 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5574 newNumBlocks++;
5577 * Go to the current end of chain
5579 while (blockIndex != BLOCK_END_OF_CHAIN)
5581 oldNumBlocks++;
5582 currentBlock = blockIndex;
5583 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5584 return FALSE;
5588 * Add new blocks to the chain
5590 while (oldNumBlocks < newNumBlocks)
5592 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5593 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5595 SmallBlockChainStream_SetNextBlockInChain(
5596 This,
5597 blockIndex,
5598 BLOCK_END_OF_CHAIN);
5600 currentBlock = blockIndex;
5601 oldNumBlocks++;
5604 return TRUE;
5607 /******************************************************************************
5608 * SmallBlockChainStream_SetSize
5610 * Sets the size of this stream.
5611 * The file will grow if we grow the chain.
5613 * TODO: Free the actual blocks in the file when we shrink the chain.
5614 * Currently, the blocks are still in the file. So the file size
5615 * doesn't shrink even if we shrink streams.
5617 BOOL SmallBlockChainStream_SetSize(
5618 SmallBlockChainStream* This,
5619 ULARGE_INTEGER newSize)
5621 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5623 if (newSize.u.LowPart == size.u.LowPart)
5624 return TRUE;
5626 if (newSize.u.LowPart < size.u.LowPart)
5628 SmallBlockChainStream_Shrink(This, newSize);
5630 else
5632 SmallBlockChainStream_Enlarge(This, newSize);
5635 return TRUE;
5638 /******************************************************************************
5639 * SmallBlockChainStream_GetSize
5641 * Returns the size of this chain.
5643 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5645 StgProperty chainProperty;
5647 StorageImpl_ReadProperty(
5648 This->parentStorage,
5649 This->ownerPropertyIndex,
5650 &chainProperty);
5652 return chainProperty.size;
5655 /******************************************************************************
5656 * StgCreateDocfile [OLE32.@]
5657 * Creates a new compound file storage object
5659 * PARAMS
5660 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5661 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5662 * reserved [ ?] unused?, usually 0
5663 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5665 * RETURNS
5666 * S_OK if the file was successfully created
5667 * some STG_E_ value if error
5668 * NOTES
5669 * if pwcsName is NULL, create file with new unique name
5670 * the function can returns
5671 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5672 * (unrealized now)
5674 HRESULT WINAPI StgCreateDocfile(
5675 LPCOLESTR pwcsName,
5676 DWORD grfMode,
5677 DWORD reserved,
5678 IStorage **ppstgOpen)
5680 StorageImpl* newStorage = 0;
5681 HANDLE hFile = INVALID_HANDLE_VALUE;
5682 HRESULT hr = STG_E_INVALIDFLAG;
5683 DWORD shareMode;
5684 DWORD accessMode;
5685 DWORD creationMode;
5686 DWORD fileAttributes;
5687 WCHAR tempFileName[MAX_PATH];
5689 TRACE("(%s, %lx, %ld, %p)\n",
5690 debugstr_w(pwcsName), grfMode,
5691 reserved, ppstgOpen);
5694 * Validate the parameters
5696 if (ppstgOpen == 0)
5697 return STG_E_INVALIDPOINTER;
5698 if (reserved != 0)
5699 return STG_E_INVALIDPARAMETER;
5702 * Validate the STGM flags
5704 if ( FAILED( validateSTGM(grfMode) ))
5705 goto end;
5707 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5708 switch(STGM_ACCESS_MODE(grfMode))
5710 case STGM_WRITE:
5711 case STGM_READWRITE:
5712 break;
5713 default:
5714 goto end;
5717 /* if no share mode given then DENY_NONE is the default */
5718 if (STGM_SHARE_MODE(grfMode) == 0)
5719 grfMode |= STGM_SHARE_DENY_NONE;
5721 /* must have at least one access mode */
5722 if (STGM_ACCESS_MODE(grfMode) == 0)
5723 goto end;
5725 /* in direct mode, can only use SHARE_EXCLUSIVE */
5726 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5727 goto end;
5729 /* but in transacted mode, any share mode is valid */
5732 * Generate a unique name.
5734 if (pwcsName == 0)
5736 WCHAR tempPath[MAX_PATH];
5737 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5739 memset(tempPath, 0, sizeof(tempPath));
5740 memset(tempFileName, 0, sizeof(tempFileName));
5742 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5743 tempPath[0] = '.';
5745 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5746 pwcsName = tempFileName;
5747 else
5749 hr = STG_E_INSUFFICIENTMEMORY;
5750 goto end;
5753 creationMode = TRUNCATE_EXISTING;
5755 else
5757 creationMode = GetCreationModeFromSTGM(grfMode);
5761 * Interpret the STGM value grfMode
5763 shareMode = GetShareModeFromSTGM(grfMode);
5764 accessMode = GetAccessModeFromSTGM(grfMode);
5766 if (grfMode & STGM_DELETEONRELEASE)
5767 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5768 else
5769 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5771 if (grfMode & STGM_TRANSACTED)
5772 FIXME("Transacted mode not implemented.\n");
5775 * Initialize the "out" parameter.
5777 *ppstgOpen = 0;
5779 hFile = CreateFileW(pwcsName,
5780 accessMode,
5781 shareMode,
5782 NULL,
5783 creationMode,
5784 fileAttributes,
5787 if (hFile == INVALID_HANDLE_VALUE)
5789 if(GetLastError() == ERROR_FILE_EXISTS)
5790 hr = STG_E_FILEALREADYEXISTS;
5791 else
5792 hr = E_FAIL;
5793 goto end;
5797 * Allocate and initialize the new IStorage32object.
5799 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5801 if (newStorage == 0)
5803 hr = STG_E_INSUFFICIENTMEMORY;
5804 goto end;
5807 hr = StorageImpl_Construct(
5808 newStorage,
5809 hFile,
5810 pwcsName,
5811 NULL,
5812 grfMode,
5813 TRUE,
5814 TRUE);
5816 if (FAILED(hr))
5818 HeapFree(GetProcessHeap(), 0, newStorage);
5819 goto end;
5823 * Get an "out" pointer for the caller.
5825 hr = StorageBaseImpl_QueryInterface(
5826 (IStorage*)newStorage,
5827 (REFIID)&IID_IStorage,
5828 (void**)ppstgOpen);
5829 end:
5830 TRACE("<-- %p r = %08lx\n", *ppstgOpen, hr);
5832 return hr;
5835 /******************************************************************************
5836 * StgCreateStorageEx [OLE32.@]
5838 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5840 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5841 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5843 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5845 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5846 return STG_E_INVALIDPARAMETER;
5849 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5851 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5852 return STG_E_INVALIDPARAMETER;
5855 if (stgfmt == STGFMT_FILE)
5857 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5858 return STG_E_INVALIDPARAMETER;
5861 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5863 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5864 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5867 ERR("Invalid stgfmt argument\n");
5868 return STG_E_INVALIDPARAMETER;
5871 /******************************************************************************
5872 * StgCreatePropSetStg [OLE32.@]
5874 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5875 IPropertySetStorage **ppPropSetStg)
5877 HRESULT hr;
5879 TRACE("(%p, 0x%lx, %p)\n", pstg, reserved, ppPropSetStg);
5880 if (reserved)
5881 hr = STG_E_INVALIDPARAMETER;
5882 else
5883 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5884 (void**)ppPropSetStg);
5885 return hr;
5888 /******************************************************************************
5889 * StgOpenStorageEx [OLE32.@]
5891 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5893 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5894 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5896 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5898 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5899 return STG_E_INVALIDPARAMETER;
5902 switch (stgfmt)
5904 case STGFMT_FILE:
5905 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5906 return STG_E_INVALIDPARAMETER;
5908 case STGFMT_STORAGE:
5909 break;
5911 case STGFMT_DOCFILE:
5912 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5914 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5915 return STG_E_INVALIDPARAMETER;
5917 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5918 break;
5920 case STGFMT_ANY:
5921 WARN("STGFMT_ANY assuming storage\n");
5922 break;
5924 default:
5925 return STG_E_INVALIDPARAMETER;
5928 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5932 /******************************************************************************
5933 * StgOpenStorage [OLE32.@]
5935 HRESULT WINAPI StgOpenStorage(
5936 const OLECHAR *pwcsName,
5937 IStorage *pstgPriority,
5938 DWORD grfMode,
5939 SNB snbExclude,
5940 DWORD reserved,
5941 IStorage **ppstgOpen)
5943 StorageImpl* newStorage = 0;
5944 HRESULT hr = S_OK;
5945 HANDLE hFile = 0;
5946 DWORD shareMode;
5947 DWORD accessMode;
5948 WCHAR fullname[MAX_PATH];
5949 BOOL newFile;
5951 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5952 debugstr_w(pwcsName), pstgPriority, grfMode,
5953 snbExclude, reserved, ppstgOpen);
5956 * Perform sanity checks
5958 if (pwcsName == 0)
5960 hr = STG_E_INVALIDNAME;
5961 goto end;
5964 if (ppstgOpen == 0)
5966 hr = STG_E_INVALIDPOINTER;
5967 goto end;
5970 if (reserved)
5972 hr = STG_E_INVALIDPARAMETER;
5973 goto end;
5976 if (grfMode & STGM_PRIORITY)
5978 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5979 return STG_E_INVALIDFLAG;
5980 if (grfMode & STGM_DELETEONRELEASE)
5981 return STG_E_INVALIDFUNCTION;
5982 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5983 return STG_E_INVALIDFLAG;
5984 grfMode &= ~0xf0; /* remove the existing sharing mode */
5985 grfMode |= STGM_SHARE_DENY_NONE;
5987 /* STGM_PRIORITY stops other IStorage objects on the same file from
5988 * committing until the STGM_PRIORITY IStorage is closed. it also
5989 * stops non-transacted mode StgOpenStorage calls with write access from
5990 * succeeding. obviously, both of these cannot be achieved through just
5991 * file share flags */
5992 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5996 * Validate the sharing mode
5998 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5999 switch(STGM_SHARE_MODE(grfMode))
6001 case STGM_SHARE_EXCLUSIVE:
6002 case STGM_SHARE_DENY_WRITE:
6003 break;
6004 default:
6005 hr = STG_E_INVALIDFLAG;
6006 goto end;
6010 * Validate the STGM flags
6012 if ( FAILED( validateSTGM(grfMode) ) ||
6013 (grfMode&STGM_CREATE))
6015 hr = STG_E_INVALIDFLAG;
6016 goto end;
6019 /* shared reading requires transacted mode */
6020 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6021 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6022 !(grfMode&STGM_TRANSACTED) )
6024 hr = STG_E_INVALIDFLAG;
6025 goto end;
6029 * Interpret the STGM value grfMode
6031 shareMode = GetShareModeFromSTGM(grfMode);
6032 accessMode = GetAccessModeFromSTGM(grfMode);
6035 * Initialize the "out" parameter.
6037 *ppstgOpen = 0;
6039 if ((accessMode & GENERIC_WRITE) && /* try to create a file if no yet exists */
6040 ((hFile = CreateFileW( pwcsName, accessMode, shareMode, NULL, CREATE_NEW,
6041 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 0))
6042 != INVALID_HANDLE_VALUE))
6044 newFile = TRUE;
6046 else
6048 newFile = FALSE;
6049 hFile = CreateFileW( pwcsName,
6050 accessMode,
6051 shareMode,
6052 NULL,
6053 OPEN_EXISTING,
6054 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6058 if (hFile==INVALID_HANDLE_VALUE)
6060 DWORD last_error = GetLastError();
6062 hr = E_FAIL;
6064 switch (last_error)
6066 case ERROR_FILE_NOT_FOUND:
6067 hr = STG_E_FILENOTFOUND;
6068 break;
6070 case ERROR_PATH_NOT_FOUND:
6071 hr = STG_E_PATHNOTFOUND;
6072 break;
6074 case ERROR_ACCESS_DENIED:
6075 case ERROR_WRITE_PROTECT:
6076 hr = STG_E_ACCESSDENIED;
6077 break;
6079 case ERROR_SHARING_VIOLATION:
6080 hr = STG_E_SHAREVIOLATION;
6081 break;
6083 default:
6084 hr = E_FAIL;
6087 goto end;
6091 * Refuse to open the file if it's too small to be a structured storage file
6092 * FIXME: verify the file when reading instead of here
6094 if (!newFile && GetFileSize(hFile, NULL) < 0x100)
6096 CloseHandle(hFile);
6097 hr = STG_E_FILEALREADYEXISTS;
6098 goto end;
6102 * Allocate and initialize the new IStorage32object.
6104 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6106 if (newStorage == 0)
6108 hr = STG_E_INSUFFICIENTMEMORY;
6109 goto end;
6112 /* if we created new file, initialize the storage */
6113 hr = StorageImpl_Construct(
6114 newStorage,
6115 hFile,
6116 pwcsName,
6117 NULL,
6118 grfMode,
6119 TRUE,
6120 newFile );
6122 if (FAILED(hr))
6124 HeapFree(GetProcessHeap(), 0, newStorage);
6126 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6128 if(hr == STG_E_INVALIDHEADER)
6129 hr = STG_E_FILEALREADYEXISTS;
6130 goto end;
6133 /* prepare the file name string given in lieu of the root property name */
6134 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6135 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6136 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6139 * Get an "out" pointer for the caller.
6141 hr = StorageBaseImpl_QueryInterface(
6142 (IStorage*)newStorage,
6143 (REFIID)&IID_IStorage,
6144 (void**)ppstgOpen);
6146 end:
6147 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6148 return hr;
6151 /******************************************************************************
6152 * StgCreateDocfileOnILockBytes [OLE32.@]
6154 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6155 ILockBytes *plkbyt,
6156 DWORD grfMode,
6157 DWORD reserved,
6158 IStorage** ppstgOpen)
6160 StorageImpl* newStorage = 0;
6161 HRESULT hr = S_OK;
6164 * Validate the parameters
6166 if ((ppstgOpen == 0) || (plkbyt == 0))
6167 return STG_E_INVALIDPOINTER;
6170 * Allocate and initialize the new IStorage object.
6172 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6174 if (newStorage == 0)
6175 return STG_E_INSUFFICIENTMEMORY;
6177 hr = StorageImpl_Construct(
6178 newStorage,
6181 plkbyt,
6182 grfMode,
6183 FALSE,
6184 TRUE);
6186 if (FAILED(hr))
6188 HeapFree(GetProcessHeap(), 0, newStorage);
6189 return hr;
6193 * Get an "out" pointer for the caller.
6195 hr = StorageBaseImpl_QueryInterface(
6196 (IStorage*)newStorage,
6197 (REFIID)&IID_IStorage,
6198 (void**)ppstgOpen);
6200 return hr;
6203 /******************************************************************************
6204 * StgOpenStorageOnILockBytes [OLE32.@]
6206 HRESULT WINAPI StgOpenStorageOnILockBytes(
6207 ILockBytes *plkbyt,
6208 IStorage *pstgPriority,
6209 DWORD grfMode,
6210 SNB snbExclude,
6211 DWORD reserved,
6212 IStorage **ppstgOpen)
6214 StorageImpl* newStorage = 0;
6215 HRESULT hr = S_OK;
6218 * Perform a sanity check
6220 if ((plkbyt == 0) || (ppstgOpen == 0))
6221 return STG_E_INVALIDPOINTER;
6224 * Validate the STGM flags
6226 if ( FAILED( validateSTGM(grfMode) ))
6227 return STG_E_INVALIDFLAG;
6230 * Initialize the "out" parameter.
6232 *ppstgOpen = 0;
6235 * Allocate and initialize the new IStorage object.
6237 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6239 if (newStorage == 0)
6240 return STG_E_INSUFFICIENTMEMORY;
6242 hr = StorageImpl_Construct(
6243 newStorage,
6246 plkbyt,
6247 grfMode,
6248 FALSE,
6249 FALSE);
6251 if (FAILED(hr))
6253 HeapFree(GetProcessHeap(), 0, newStorage);
6254 return hr;
6258 * Get an "out" pointer for the caller.
6260 hr = StorageBaseImpl_QueryInterface(
6261 (IStorage*)newStorage,
6262 (REFIID)&IID_IStorage,
6263 (void**)ppstgOpen);
6265 return hr;
6268 /******************************************************************************
6269 * StgSetTimes [ole32.@]
6270 * StgSetTimes [OLE32.@]
6274 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6275 FILETIME const *patime, FILETIME const *pmtime)
6277 IStorage *stg = NULL;
6278 HRESULT r;
6280 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6282 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6283 0, 0, &stg);
6284 if( SUCCEEDED(r) )
6286 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6287 IStorage_Release(stg);
6290 return r;
6293 /******************************************************************************
6294 * StgIsStorageILockBytes [OLE32.@]
6296 * Determines if the ILockBytes contains a storage object.
6298 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6300 BYTE sig[8];
6301 ULARGE_INTEGER offset;
6303 offset.u.HighPart = 0;
6304 offset.u.LowPart = 0;
6306 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6308 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6309 return S_OK;
6311 return S_FALSE;
6314 /******************************************************************************
6315 * WriteClassStg [OLE32.@]
6317 * This method will store the specified CLSID in the specified storage object
6319 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6321 HRESULT hRes;
6323 if(!pStg)
6324 return E_INVALIDARG;
6326 hRes = IStorage_SetClass(pStg, rclsid);
6328 return hRes;
6331 /***********************************************************************
6332 * ReadClassStg (OLE32.@)
6334 * This method reads the CLSID previously written to a storage object with
6335 * the WriteClassStg.
6337 * PARAMS
6338 * pstg [I] IStorage pointer
6339 * pclsid [O] Pointer to where the CLSID is written
6341 * RETURNS
6342 * Success: S_OK.
6343 * Failure: HRESULT code.
6345 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6347 STATSTG pstatstg;
6348 HRESULT hRes;
6350 TRACE("(%p, %p)\n", pstg, pclsid);
6352 if(!pstg || !pclsid)
6353 return E_INVALIDARG;
6356 * read a STATSTG structure (contains the clsid) from the storage
6358 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6360 if(SUCCEEDED(hRes))
6361 *pclsid=pstatstg.clsid;
6363 return hRes;
6366 /***********************************************************************
6367 * OleLoadFromStream (OLE32.@)
6369 * This function loads an object from stream
6371 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6373 CLSID clsid;
6374 HRESULT res;
6375 LPPERSISTSTREAM xstm;
6377 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6379 res=ReadClassStm(pStm,&clsid);
6380 if (!SUCCEEDED(res))
6381 return res;
6382 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6383 if (!SUCCEEDED(res))
6384 return res;
6385 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6386 if (!SUCCEEDED(res)) {
6387 IUnknown_Release((IUnknown*)*ppvObj);
6388 return res;
6390 res=IPersistStream_Load(xstm,pStm);
6391 IPersistStream_Release(xstm);
6392 /* FIXME: all refcounts ok at this point? I think they should be:
6393 * pStm : unchanged
6394 * ppvObj : 1
6395 * xstm : 0 (released)
6397 return res;
6400 /***********************************************************************
6401 * OleSaveToStream (OLE32.@)
6403 * This function saves an object with the IPersistStream interface on it
6404 * to the specified stream.
6406 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6409 CLSID clsid;
6410 HRESULT res;
6412 TRACE("(%p,%p)\n",pPStm,pStm);
6414 res=IPersistStream_GetClassID(pPStm,&clsid);
6416 if (SUCCEEDED(res)){
6418 res=WriteClassStm(pStm,&clsid);
6420 if (SUCCEEDED(res))
6422 res=IPersistStream_Save(pPStm,pStm,TRUE);
6425 TRACE("Finished Save\n");
6426 return res;
6429 /****************************************************************************
6430 * This method validate a STGM parameter that can contain the values below
6432 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6433 * The stgm values contained in 0xffff0000 are bitmasks.
6435 * STGM_DIRECT 0x00000000
6436 * STGM_TRANSACTED 0x00010000
6437 * STGM_SIMPLE 0x08000000
6439 * STGM_READ 0x00000000
6440 * STGM_WRITE 0x00000001
6441 * STGM_READWRITE 0x00000002
6443 * STGM_SHARE_DENY_NONE 0x00000040
6444 * STGM_SHARE_DENY_READ 0x00000030
6445 * STGM_SHARE_DENY_WRITE 0x00000020
6446 * STGM_SHARE_EXCLUSIVE 0x00000010
6448 * STGM_PRIORITY 0x00040000
6449 * STGM_DELETEONRELEASE 0x04000000
6451 * STGM_CREATE 0x00001000
6452 * STGM_CONVERT 0x00020000
6453 * STGM_FAILIFTHERE 0x00000000
6455 * STGM_NOSCRATCH 0x00100000
6456 * STGM_NOSNAPSHOT 0x00200000
6458 static HRESULT validateSTGM(DWORD stgm)
6460 DWORD access = STGM_ACCESS_MODE(stgm);
6461 DWORD share = STGM_SHARE_MODE(stgm);
6462 DWORD create = STGM_CREATE_MODE(stgm);
6464 if (stgm&~STGM_KNOWN_FLAGS)
6466 ERR("unknown flags %08lx\n", stgm);
6467 return E_FAIL;
6470 switch (access)
6472 case STGM_READ:
6473 case STGM_WRITE:
6474 case STGM_READWRITE:
6475 break;
6476 default:
6477 return E_FAIL;
6480 switch (share)
6482 case STGM_SHARE_DENY_NONE:
6483 case STGM_SHARE_DENY_READ:
6484 case STGM_SHARE_DENY_WRITE:
6485 case STGM_SHARE_EXCLUSIVE:
6486 break;
6487 default:
6488 return E_FAIL;
6491 switch (create)
6493 case STGM_CREATE:
6494 case STGM_FAILIFTHERE:
6495 break;
6496 default:
6497 return E_FAIL;
6501 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6503 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6504 return E_FAIL;
6507 * STGM_CREATE | STGM_CONVERT
6508 * if both are false, STGM_FAILIFTHERE is set to TRUE
6510 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6511 return E_FAIL;
6514 * STGM_NOSCRATCH requires STGM_TRANSACTED
6516 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6517 return E_FAIL;
6520 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6521 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6523 if ( (stgm & STGM_NOSNAPSHOT) &&
6524 (!(stgm & STGM_TRANSACTED) ||
6525 share == STGM_SHARE_EXCLUSIVE ||
6526 share == STGM_SHARE_DENY_WRITE) )
6527 return E_FAIL;
6529 return S_OK;
6532 /****************************************************************************
6533 * GetShareModeFromSTGM
6535 * This method will return a share mode flag from a STGM value.
6536 * The STGM value is assumed valid.
6538 static DWORD GetShareModeFromSTGM(DWORD stgm)
6540 switch (STGM_SHARE_MODE(stgm))
6542 case STGM_SHARE_DENY_NONE:
6543 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6544 case STGM_SHARE_DENY_READ:
6545 return FILE_SHARE_WRITE;
6546 case STGM_SHARE_DENY_WRITE:
6547 return FILE_SHARE_READ;
6548 case STGM_SHARE_EXCLUSIVE:
6549 return 0;
6551 ERR("Invalid share mode!\n");
6552 assert(0);
6553 return 0;
6556 /****************************************************************************
6557 * GetAccessModeFromSTGM
6559 * This method will return an access mode flag from a STGM value.
6560 * The STGM value is assumed valid.
6562 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6564 switch (STGM_ACCESS_MODE(stgm))
6566 case STGM_READ:
6567 return GENERIC_READ;
6568 case STGM_WRITE:
6569 case STGM_READWRITE:
6570 return GENERIC_READ | GENERIC_WRITE;
6572 ERR("Invalid access mode!\n");
6573 assert(0);
6574 return 0;
6577 /****************************************************************************
6578 * GetCreationModeFromSTGM
6580 * This method will return a creation mode flag from a STGM value.
6581 * The STGM value is assumed valid.
6583 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6585 switch(STGM_CREATE_MODE(stgm))
6587 case STGM_CREATE:
6588 return CREATE_ALWAYS;
6589 case STGM_CONVERT:
6590 FIXME("STGM_CONVERT not implemented!\n");
6591 return CREATE_NEW;
6592 case STGM_FAILIFTHERE:
6593 return CREATE_NEW;
6595 ERR("Invalid create mode!\n");
6596 assert(0);
6597 return 0;
6601 /*************************************************************************
6602 * OLECONVERT_LoadOLE10 [Internal]
6604 * Loads the OLE10 STREAM to memory
6606 * PARAMS
6607 * pOleStream [I] The OLESTREAM
6608 * pData [I] Data Structure for the OLESTREAM Data
6610 * RETURNS
6611 * Success: S_OK
6612 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6613 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6615 * NOTES
6616 * This function is used by OleConvertOLESTREAMToIStorage only.
6618 * Memory allocated for pData must be freed by the caller
6620 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6622 DWORD dwSize;
6623 HRESULT hRes = S_OK;
6624 int nTryCnt=0;
6625 int max_try = 6;
6627 pData->pData = NULL;
6628 pData->pstrOleObjFileName = (CHAR *) NULL;
6630 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6632 /* Get the OleID */
6633 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6634 if(dwSize != sizeof(pData->dwOleID))
6636 hRes = CONVERT10_E_OLESTREAM_GET;
6638 else if(pData->dwOleID != OLESTREAM_ID)
6640 hRes = CONVERT10_E_OLESTREAM_FMT;
6642 else
6644 hRes = S_OK;
6645 break;
6649 if(hRes == S_OK)
6651 /* Get the TypeID...more info needed for this field */
6652 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6653 if(dwSize != sizeof(pData->dwTypeID))
6655 hRes = CONVERT10_E_OLESTREAM_GET;
6658 if(hRes == S_OK)
6660 if(pData->dwTypeID != 0)
6662 /* Get the length of the OleTypeName */
6663 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6664 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6666 hRes = CONVERT10_E_OLESTREAM_GET;
6669 if(hRes == S_OK)
6671 if(pData->dwOleTypeNameLength > 0)
6673 /* Get the OleTypeName */
6674 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6675 if(dwSize != pData->dwOleTypeNameLength)
6677 hRes = CONVERT10_E_OLESTREAM_GET;
6681 if(bStrem1)
6683 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6684 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6686 hRes = CONVERT10_E_OLESTREAM_GET;
6688 if(hRes == S_OK)
6690 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6691 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6692 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6693 if(pData->pstrOleObjFileName)
6695 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6696 if(dwSize != pData->dwOleObjFileNameLength)
6698 hRes = CONVERT10_E_OLESTREAM_GET;
6701 else
6702 hRes = CONVERT10_E_OLESTREAM_GET;
6705 else
6707 /* Get the Width of the Metafile */
6708 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6709 if(dwSize != sizeof(pData->dwMetaFileWidth))
6711 hRes = CONVERT10_E_OLESTREAM_GET;
6713 if(hRes == S_OK)
6715 /* Get the Height of the Metafile */
6716 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6717 if(dwSize != sizeof(pData->dwMetaFileHeight))
6719 hRes = CONVERT10_E_OLESTREAM_GET;
6723 if(hRes == S_OK)
6725 /* Get the Length of the Data */
6726 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6727 if(dwSize != sizeof(pData->dwDataLength))
6729 hRes = CONVERT10_E_OLESTREAM_GET;
6733 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6735 if(!bStrem1) /* if it is a second OLE stream data */
6737 pData->dwDataLength -= 8;
6738 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6739 if(dwSize != sizeof(pData->strUnknown))
6741 hRes = CONVERT10_E_OLESTREAM_GET;
6745 if(hRes == S_OK)
6747 if(pData->dwDataLength > 0)
6749 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6751 /* Get Data (ex. IStorage, Metafile, or BMP) */
6752 if(pData->pData)
6754 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6755 if(dwSize != pData->dwDataLength)
6757 hRes = CONVERT10_E_OLESTREAM_GET;
6760 else
6762 hRes = CONVERT10_E_OLESTREAM_GET;
6768 return hRes;
6771 /*************************************************************************
6772 * OLECONVERT_SaveOLE10 [Internal]
6774 * Saves the OLE10 STREAM From memory
6776 * PARAMS
6777 * pData [I] Data Structure for the OLESTREAM Data
6778 * pOleStream [I] The OLESTREAM to save
6780 * RETURNS
6781 * Success: S_OK
6782 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6784 * NOTES
6785 * This function is used by OleConvertIStorageToOLESTREAM only.
6788 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6790 DWORD dwSize;
6791 HRESULT hRes = S_OK;
6794 /* Set the OleID */
6795 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6796 if(dwSize != sizeof(pData->dwOleID))
6798 hRes = CONVERT10_E_OLESTREAM_PUT;
6801 if(hRes == S_OK)
6803 /* Set the TypeID */
6804 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6805 if(dwSize != sizeof(pData->dwTypeID))
6807 hRes = CONVERT10_E_OLESTREAM_PUT;
6811 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6813 /* Set the Length of the OleTypeName */
6814 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6815 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6817 hRes = CONVERT10_E_OLESTREAM_PUT;
6820 if(hRes == S_OK)
6822 if(pData->dwOleTypeNameLength > 0)
6824 /* Set the OleTypeName */
6825 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6826 if(dwSize != pData->dwOleTypeNameLength)
6828 hRes = CONVERT10_E_OLESTREAM_PUT;
6833 if(hRes == S_OK)
6835 /* Set the width of the Metafile */
6836 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6837 if(dwSize != sizeof(pData->dwMetaFileWidth))
6839 hRes = CONVERT10_E_OLESTREAM_PUT;
6843 if(hRes == S_OK)
6845 /* Set the height of the Metafile */
6846 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6847 if(dwSize != sizeof(pData->dwMetaFileHeight))
6849 hRes = CONVERT10_E_OLESTREAM_PUT;
6853 if(hRes == S_OK)
6855 /* Set the length of the Data */
6856 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6857 if(dwSize != sizeof(pData->dwDataLength))
6859 hRes = CONVERT10_E_OLESTREAM_PUT;
6863 if(hRes == S_OK)
6865 if(pData->dwDataLength > 0)
6867 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6868 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6869 if(dwSize != pData->dwDataLength)
6871 hRes = CONVERT10_E_OLESTREAM_PUT;
6876 return hRes;
6879 /*************************************************************************
6880 * OLECONVERT_GetOLE20FromOLE10[Internal]
6882 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6883 * opens it, and copies the content to the dest IStorage for
6884 * OleConvertOLESTREAMToIStorage
6887 * PARAMS
6888 * pDestStorage [I] The IStorage to copy the data to
6889 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6890 * nBufferLength [I] The size of the buffer
6892 * RETURNS
6893 * Nothing
6895 * NOTES
6899 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6901 HRESULT hRes;
6902 HANDLE hFile;
6903 IStorage *pTempStorage;
6904 DWORD dwNumOfBytesWritten;
6905 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6906 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6908 /* Create a temp File */
6909 GetTempPathW(MAX_PATH, wstrTempDir);
6910 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6911 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6913 if(hFile != INVALID_HANDLE_VALUE)
6915 /* Write IStorage Data to File */
6916 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6917 CloseHandle(hFile);
6919 /* Open and copy temp storage to the Dest Storage */
6920 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6921 if(hRes == S_OK)
6923 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6924 StorageBaseImpl_Release(pTempStorage);
6926 DeleteFileW(wstrTempFile);
6931 /*************************************************************************
6932 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6934 * Saves the OLE10 STREAM From memory
6936 * PARAMS
6937 * pStorage [I] The Src IStorage to copy
6938 * pData [I] The Dest Memory to write to.
6940 * RETURNS
6941 * The size in bytes allocated for pData
6943 * NOTES
6944 * Memory allocated for pData must be freed by the caller
6946 * Used by OleConvertIStorageToOLESTREAM only.
6949 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6951 HANDLE hFile;
6952 HRESULT hRes;
6953 DWORD nDataLength = 0;
6954 IStorage *pTempStorage;
6955 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6956 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6958 *pData = NULL;
6960 /* Create temp Storage */
6961 GetTempPathW(MAX_PATH, wstrTempDir);
6962 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6963 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6965 if(hRes == S_OK)
6967 /* Copy Src Storage to the Temp Storage */
6968 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6969 StorageBaseImpl_Release(pTempStorage);
6971 /* Open Temp Storage as a file and copy to memory */
6972 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6973 if(hFile != INVALID_HANDLE_VALUE)
6975 nDataLength = GetFileSize(hFile, NULL);
6976 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6977 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6978 CloseHandle(hFile);
6980 DeleteFileW(wstrTempFile);
6982 return nDataLength;
6985 /*************************************************************************
6986 * OLECONVERT_CreateOleStream [Internal]
6988 * Creates the "\001OLE" stream in the IStorage if necessary.
6990 * PARAMS
6991 * pStorage [I] Dest storage to create the stream in
6993 * RETURNS
6994 * Nothing
6996 * NOTES
6997 * This function is used by OleConvertOLESTREAMToIStorage only.
6999 * This stream is still unknown, MS Word seems to have extra data
7000 * but since the data is stored in the OLESTREAM there should be
7001 * no need to recreate the stream. If the stream is manually
7002 * deleted it will create it with this default data.
7005 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
7007 HRESULT hRes;
7008 IStream *pStream;
7009 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7010 BYTE pOleStreamHeader [] =
7012 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7013 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7014 0x00, 0x00, 0x00, 0x00
7017 /* Create stream if not present */
7018 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7019 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7021 if(hRes == S_OK)
7023 /* Write default Data */
7024 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7025 IStream_Release(pStream);
7029 /* write a string to a stream, preceded by its length */
7030 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7032 HRESULT r;
7033 LPSTR str;
7034 DWORD len = 0;
7036 if( string )
7037 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7038 r = IStream_Write( stm, &len, sizeof(len), NULL);
7039 if( FAILED( r ) )
7040 return r;
7041 if(len == 0)
7042 return r;
7043 str = CoTaskMemAlloc( len );
7044 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7045 r = IStream_Write( stm, str, len, NULL);
7046 CoTaskMemFree( str );
7047 return r;
7050 /* read a string preceded by its length from a stream */
7051 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7053 HRESULT r;
7054 DWORD len, count = 0;
7055 LPSTR str;
7056 LPWSTR wstr;
7058 r = IStream_Read( stm, &len, sizeof(len), &count );
7059 if( FAILED( r ) )
7060 return r;
7061 if( count != sizeof(len) )
7062 return E_OUTOFMEMORY;
7064 TRACE("%ld bytes\n",len);
7066 str = CoTaskMemAlloc( len );
7067 if( !str )
7068 return E_OUTOFMEMORY;
7069 count = 0;
7070 r = IStream_Read( stm, str, len, &count );
7071 if( FAILED( r ) )
7072 return r;
7073 if( count != len )
7075 CoTaskMemFree( str );
7076 return E_OUTOFMEMORY;
7079 TRACE("Read string %s\n",debugstr_an(str,len));
7081 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7082 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7083 if( wstr )
7084 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7085 CoTaskMemFree( str );
7087 *string = wstr;
7089 return r;
7093 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7094 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7096 IStream *pstm;
7097 HRESULT r = S_OK;
7098 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7100 static const BYTE unknown1[12] =
7101 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7102 0xFF, 0xFF, 0xFF, 0xFF};
7103 static const BYTE unknown2[16] =
7104 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7107 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7108 debugstr_w(lpszUserType), debugstr_w(szClipName),
7109 debugstr_w(szProgIDName));
7111 /* Create a CompObj stream if it doesn't exist */
7112 r = IStorage_CreateStream(pstg, szwStreamName,
7113 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7114 if( FAILED (r) )
7115 return r;
7117 /* Write CompObj Structure to stream */
7118 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7120 if( SUCCEEDED( r ) )
7121 r = WriteClassStm( pstm, clsid );
7123 if( SUCCEEDED( r ) )
7124 r = STREAM_WriteString( pstm, lpszUserType );
7125 if( SUCCEEDED( r ) )
7126 r = STREAM_WriteString( pstm, szClipName );
7127 if( SUCCEEDED( r ) )
7128 r = STREAM_WriteString( pstm, szProgIDName );
7129 if( SUCCEEDED( r ) )
7130 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7132 IStream_Release( pstm );
7134 return r;
7137 /***********************************************************************
7138 * WriteFmtUserTypeStg (OLE32.@)
7140 HRESULT WINAPI WriteFmtUserTypeStg(
7141 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7143 HRESULT r;
7144 WCHAR szwClipName[0x40];
7145 CLSID clsid = CLSID_NULL;
7146 LPWSTR wstrProgID = NULL;
7147 DWORD n;
7149 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7151 /* get the clipboard format name */
7152 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7153 szwClipName[n]=0;
7155 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7157 /* FIXME: There's room to save a CLSID and its ProgID, but
7158 the CLSID is not looked up in the registry and in all the
7159 tests I wrote it was CLSID_NULL. Where does it come from?
7162 /* get the real program ID. This may fail, but that's fine */
7163 ProgIDFromCLSID(&clsid, &wstrProgID);
7165 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7167 r = STORAGE_WriteCompObj( pstg, &clsid,
7168 lpszUserType, szwClipName, wstrProgID );
7170 CoTaskMemFree(wstrProgID);
7172 return r;
7176 /******************************************************************************
7177 * ReadFmtUserTypeStg [OLE32.@]
7179 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7181 HRESULT r;
7182 IStream *stm = 0;
7183 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7184 unsigned char unknown1[12];
7185 unsigned char unknown2[16];
7186 DWORD count;
7187 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7188 CLSID clsid;
7190 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7192 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7193 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7194 if( FAILED ( r ) )
7196 WARN("Failed to open stream r = %08lx\n", r);
7197 return r;
7200 /* read the various parts of the structure */
7201 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7202 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7203 goto end;
7204 r = ReadClassStm( stm, &clsid );
7205 if( FAILED( r ) )
7206 goto end;
7208 r = STREAM_ReadString( stm, &szCLSIDName );
7209 if( FAILED( r ) )
7210 goto end;
7212 r = STREAM_ReadString( stm, &szOleTypeName );
7213 if( FAILED( r ) )
7214 goto end;
7216 r = STREAM_ReadString( stm, &szProgIDName );
7217 if( FAILED( r ) )
7218 goto end;
7220 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7221 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7222 goto end;
7224 /* ok, success... now we just need to store what we found */
7225 if( pcf )
7226 *pcf = RegisterClipboardFormatW( szOleTypeName );
7227 CoTaskMemFree( szOleTypeName );
7229 if( lplpszUserType )
7230 *lplpszUserType = szCLSIDName;
7231 CoTaskMemFree( szProgIDName );
7233 end:
7234 IStream_Release( stm );
7236 return r;
7240 /*************************************************************************
7241 * OLECONVERT_CreateCompObjStream [Internal]
7243 * Creates a "\001CompObj" is the destination IStorage if necessary.
7245 * PARAMS
7246 * pStorage [I] The dest IStorage to create the CompObj Stream
7247 * if necessary.
7248 * strOleTypeName [I] The ProgID
7250 * RETURNS
7251 * Success: S_OK
7252 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7254 * NOTES
7255 * This function is used by OleConvertOLESTREAMToIStorage only.
7257 * The stream data is stored in the OLESTREAM and there should be
7258 * no need to recreate the stream. If the stream is manually
7259 * deleted it will attempt to create it by querying the registry.
7263 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7265 IStream *pStream;
7266 HRESULT hStorageRes, hRes = S_OK;
7267 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7268 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7269 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7271 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7272 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7274 /* Initialize the CompObj structure */
7275 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7276 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7277 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7280 /* Create a CompObj stream if it doesn't exist */
7281 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7282 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7283 if(hStorageRes == S_OK)
7285 /* copy the OleTypeName to the compobj struct */
7286 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7287 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7289 /* copy the OleTypeName to the compobj struct */
7290 /* Note: in the test made, these were Identical */
7291 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7292 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7294 /* Get the CLSID */
7295 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7296 bufferW, OLESTREAM_MAX_STR_LEN );
7297 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7299 if(hRes == S_OK)
7301 HKEY hKey;
7302 LONG hErr;
7303 /* Get the CLSID Default Name from the Registry */
7304 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7305 if(hErr == ERROR_SUCCESS)
7307 char strTemp[OLESTREAM_MAX_STR_LEN];
7308 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7309 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7310 if(hErr == ERROR_SUCCESS)
7312 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7314 RegCloseKey(hKey);
7318 /* Write CompObj Structure to stream */
7319 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7321 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7323 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7324 if(IStorageCompObj.dwCLSIDNameLength > 0)
7326 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7328 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7329 if(IStorageCompObj.dwOleTypeNameLength > 0)
7331 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7333 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7334 if(IStorageCompObj.dwProgIDNameLength > 0)
7336 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7338 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7339 IStream_Release(pStream);
7341 return hRes;
7345 /*************************************************************************
7346 * OLECONVERT_CreateOlePresStream[Internal]
7348 * Creates the "\002OlePres000" Stream with the Metafile data
7350 * PARAMS
7351 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7352 * dwExtentX [I] Width of the Metafile
7353 * dwExtentY [I] Height of the Metafile
7354 * pData [I] Metafile data
7355 * dwDataLength [I] Size of the Metafile data
7357 * RETURNS
7358 * Success: S_OK
7359 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7361 * NOTES
7362 * This function is used by OleConvertOLESTREAMToIStorage only.
7365 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7367 HRESULT hRes;
7368 IStream *pStream;
7369 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7370 BYTE pOlePresStreamHeader [] =
7372 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7373 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7374 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7375 0x00, 0x00, 0x00, 0x00
7378 BYTE pOlePresStreamHeaderEmpty [] =
7380 0x00, 0x00, 0x00, 0x00,
7381 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7382 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7383 0x00, 0x00, 0x00, 0x00
7386 /* Create the OlePres000 Stream */
7387 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7388 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7390 if(hRes == S_OK)
7392 DWORD nHeaderSize;
7393 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7395 memset(&OlePres, 0, sizeof(OlePres));
7396 /* Do we have any metafile data to save */
7397 if(dwDataLength > 0)
7399 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7400 nHeaderSize = sizeof(pOlePresStreamHeader);
7402 else
7404 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7405 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7407 /* Set width and height of the metafile */
7408 OlePres.dwExtentX = dwExtentX;
7409 OlePres.dwExtentY = -dwExtentY;
7411 /* Set Data and Length */
7412 if(dwDataLength > sizeof(METAFILEPICT16))
7414 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7415 OlePres.pData = &(pData[8]);
7417 /* Save OlePres000 Data to Stream */
7418 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7419 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7420 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7421 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7422 if(OlePres.dwSize > 0)
7424 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7426 IStream_Release(pStream);
7430 /*************************************************************************
7431 * OLECONVERT_CreateOle10NativeStream [Internal]
7433 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7435 * PARAMS
7436 * pStorage [I] Dest storage to create the stream in
7437 * pData [I] Ole10 Native Data (ex. bmp)
7438 * dwDataLength [I] Size of the Ole10 Native Data
7440 * RETURNS
7441 * Nothing
7443 * NOTES
7444 * This function is used by OleConvertOLESTREAMToIStorage only.
7446 * Might need to verify the data and return appropriate error message
7449 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7451 HRESULT hRes;
7452 IStream *pStream;
7453 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7455 /* Create the Ole10Native Stream */
7456 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7457 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7459 if(hRes == S_OK)
7461 /* Write info to stream */
7462 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7463 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7464 IStream_Release(pStream);
7469 /*************************************************************************
7470 * OLECONVERT_GetOLE10ProgID [Internal]
7472 * Finds the ProgID (or OleTypeID) from the IStorage
7474 * PARAMS
7475 * pStorage [I] The Src IStorage to get the ProgID
7476 * strProgID [I] the ProgID string to get
7477 * dwSize [I] the size of the string
7479 * RETURNS
7480 * Success: S_OK
7481 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7483 * NOTES
7484 * This function is used by OleConvertIStorageToOLESTREAM only.
7488 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7490 HRESULT hRes;
7491 IStream *pStream;
7492 LARGE_INTEGER iSeekPos;
7493 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7494 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7496 /* Open the CompObj Stream */
7497 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7498 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7499 if(hRes == S_OK)
7502 /*Get the OleType from the CompObj Stream */
7503 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7504 iSeekPos.u.HighPart = 0;
7506 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7507 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7508 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7509 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7510 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7511 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7512 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7514 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7515 if(*dwSize > 0)
7517 IStream_Read(pStream, strProgID, *dwSize, NULL);
7519 IStream_Release(pStream);
7521 else
7523 STATSTG stat;
7524 LPOLESTR wstrProgID;
7526 /* Get the OleType from the registry */
7527 REFCLSID clsid = &(stat.clsid);
7528 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7529 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7530 if(hRes == S_OK)
7532 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7536 return hRes;
7539 /*************************************************************************
7540 * OLECONVERT_GetOle10PresData [Internal]
7542 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7544 * PARAMS
7545 * pStorage [I] Src IStroage
7546 * pOleStream [I] Dest OleStream Mem Struct
7548 * RETURNS
7549 * Nothing
7551 * NOTES
7552 * This function is used by OleConvertIStorageToOLESTREAM only.
7554 * Memory allocated for pData must be freed by the caller
7558 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7561 HRESULT hRes;
7562 IStream *pStream;
7563 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7565 /* Initialize Default data for OLESTREAM */
7566 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7567 pOleStreamData[0].dwTypeID = 2;
7568 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7569 pOleStreamData[1].dwTypeID = 0;
7570 pOleStreamData[0].dwMetaFileWidth = 0;
7571 pOleStreamData[0].dwMetaFileHeight = 0;
7572 pOleStreamData[0].pData = NULL;
7573 pOleStreamData[1].pData = NULL;
7575 /* Open Ole10Native Stream */
7576 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7577 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7578 if(hRes == S_OK)
7581 /* Read Size and Data */
7582 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7583 if(pOleStreamData->dwDataLength > 0)
7585 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7586 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7588 IStream_Release(pStream);
7594 /*************************************************************************
7595 * OLECONVERT_GetOle20PresData[Internal]
7597 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7599 * PARAMS
7600 * pStorage [I] Src IStroage
7601 * pOleStreamData [I] Dest OleStream Mem Struct
7603 * RETURNS
7604 * Nothing
7606 * NOTES
7607 * This function is used by OleConvertIStorageToOLESTREAM only.
7609 * Memory allocated for pData must be freed by the caller
7611 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7613 HRESULT hRes;
7614 IStream *pStream;
7615 OLECONVERT_ISTORAGE_OLEPRES olePress;
7616 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7618 /* Initialize Default data for OLESTREAM */
7619 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7620 pOleStreamData[0].dwTypeID = 2;
7621 pOleStreamData[0].dwMetaFileWidth = 0;
7622 pOleStreamData[0].dwMetaFileHeight = 0;
7623 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7624 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7625 pOleStreamData[1].dwTypeID = 0;
7626 pOleStreamData[1].dwOleTypeNameLength = 0;
7627 pOleStreamData[1].strOleTypeName[0] = 0;
7628 pOleStreamData[1].dwMetaFileWidth = 0;
7629 pOleStreamData[1].dwMetaFileHeight = 0;
7630 pOleStreamData[1].pData = NULL;
7631 pOleStreamData[1].dwDataLength = 0;
7634 /* Open OlePress000 stream */
7635 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7636 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7637 if(hRes == S_OK)
7639 LARGE_INTEGER iSeekPos;
7640 METAFILEPICT16 MetaFilePict;
7641 static const char strMetafilePictName[] = "METAFILEPICT";
7643 /* Set the TypeID for a Metafile */
7644 pOleStreamData[1].dwTypeID = 5;
7646 /* Set the OleTypeName to Metafile */
7647 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7648 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7650 iSeekPos.u.HighPart = 0;
7651 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7653 /* Get Presentation Data */
7654 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7655 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7656 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7657 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7659 /*Set width and Height */
7660 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7661 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7662 if(olePress.dwSize > 0)
7664 /* Set Length */
7665 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7667 /* Set MetaFilePict struct */
7668 MetaFilePict.mm = 8;
7669 MetaFilePict.xExt = olePress.dwExtentX;
7670 MetaFilePict.yExt = olePress.dwExtentY;
7671 MetaFilePict.hMF = 0;
7673 /* Get Metafile Data */
7674 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7675 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7676 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7678 IStream_Release(pStream);
7682 /*************************************************************************
7683 * OleConvertOLESTREAMToIStorage [OLE32.@]
7685 * Read info on MSDN
7687 * TODO
7688 * DVTARGETDEVICE paramenter is not handled
7689 * Still unsure of some mem fields for OLE 10 Stream
7690 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7691 * and "\001OLE" streams
7694 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7695 LPOLESTREAM pOleStream,
7696 LPSTORAGE pstg,
7697 const DVTARGETDEVICE* ptd)
7699 int i;
7700 HRESULT hRes=S_OK;
7701 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7703 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7705 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7707 if(ptd != NULL)
7709 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7712 if(pstg == NULL || pOleStream == NULL)
7714 hRes = E_INVALIDARG;
7717 if(hRes == S_OK)
7719 /* Load the OLESTREAM to Memory */
7720 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7723 if(hRes == S_OK)
7725 /* Load the OLESTREAM to Memory (part 2)*/
7726 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7729 if(hRes == S_OK)
7732 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7734 /* Do we have the IStorage Data in the OLESTREAM */
7735 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7737 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7738 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7740 else
7742 /* It must be an original OLE 1.0 source */
7743 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7746 else
7748 /* It must be an original OLE 1.0 source */
7749 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7752 /* Create CompObj Stream if necessary */
7753 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7754 if(hRes == S_OK)
7756 /*Create the Ole Stream if necessary */
7757 OLECONVERT_CreateOleStream(pstg);
7762 /* Free allocated memory */
7763 for(i=0; i < 2; i++)
7765 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7766 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7767 pOleStreamData[i].pstrOleObjFileName = NULL;
7769 return hRes;
7772 /*************************************************************************
7773 * OleConvertIStorageToOLESTREAM [OLE32.@]
7775 * Read info on MSDN
7777 * Read info on MSDN
7779 * TODO
7780 * Still unsure of some mem fields for OLE 10 Stream
7781 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7782 * and "\001OLE" streams.
7785 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7786 LPSTORAGE pstg,
7787 LPOLESTREAM pOleStream)
7789 int i;
7790 HRESULT hRes = S_OK;
7791 IStream *pStream;
7792 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7793 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7795 TRACE("%p %p\n", pstg, pOleStream);
7797 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7799 if(pstg == NULL || pOleStream == NULL)
7801 hRes = E_INVALIDARG;
7803 if(hRes == S_OK)
7805 /* Get the ProgID */
7806 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7807 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7809 if(hRes == S_OK)
7811 /* Was it originally Ole10 */
7812 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7813 if(hRes == S_OK)
7815 IStream_Release(pStream);
7816 /* Get Presentation Data for Ole10Native */
7817 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7819 else
7821 /* Get Presentation Data (OLE20) */
7822 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7825 /* Save OLESTREAM */
7826 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7827 if(hRes == S_OK)
7829 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7834 /* Free allocated memory */
7835 for(i=0; i < 2; i++)
7837 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7840 return hRes;
7843 /***********************************************************************
7844 * GetConvertStg (OLE32.@)
7846 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7847 FIXME("unimplemented stub!\n");
7848 return E_FAIL;
7851 /******************************************************************************
7852 * StgIsStorageFile [OLE32.@]
7853 * Verify if the file contains a storage object
7855 * PARAMS
7856 * fn [ I] Filename
7858 * RETURNS
7859 * S_OK if file has magic bytes as a storage object
7860 * S_FALSE if file is not storage
7862 HRESULT WINAPI
7863 StgIsStorageFile(LPCOLESTR fn)
7865 HANDLE hf;
7866 BYTE magic[8];
7867 DWORD bytes_read;
7869 TRACE("%s\n", debugstr_w(fn));
7870 hf = CreateFileW(fn, GENERIC_READ,
7871 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7872 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7874 if (hf == INVALID_HANDLE_VALUE)
7875 return STG_E_FILENOTFOUND;
7877 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7879 WARN(" unable to read file\n");
7880 CloseHandle(hf);
7881 return S_FALSE;
7884 CloseHandle(hf);
7886 if (bytes_read != 8) {
7887 WARN(" too short\n");
7888 return S_FALSE;
7891 if (!memcmp(magic,STORAGE_magic,8)) {
7892 WARN(" -> YES\n");
7893 return S_OK;
7896 WARN(" -> Invalid header.\n");
7897 return S_FALSE;
7900 /***********************************************************************
7901 * WriteClassStm (OLE32.@)
7903 * Writes a CLSID to a stream.
7905 * PARAMS
7906 * pStm [I] Stream to write to.
7907 * rclsid [I] CLSID to write.
7909 * RETURNS
7910 * Success: S_OK.
7911 * Failure: HRESULT code.
7913 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7915 TRACE("(%p,%p)\n",pStm,rclsid);
7917 if (rclsid==NULL)
7918 return E_INVALIDARG;
7920 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7923 /***********************************************************************
7924 * ReadClassStm (OLE32.@)
7926 * Reads a CLSID from a stream.
7928 * PARAMS
7929 * pStm [I] Stream to read from.
7930 * rclsid [O] CLSID to read.
7932 * RETURNS
7933 * Success: S_OK.
7934 * Failure: HRESULT code.
7936 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7938 ULONG nbByte;
7939 HRESULT res;
7941 TRACE("(%p,%p)\n",pStm,pclsid);
7943 if (pclsid==NULL)
7944 return E_INVALIDARG;
7946 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7948 if (FAILED(res))
7949 return res;
7951 if (nbByte != sizeof(CLSID))
7952 return S_FALSE;
7953 else
7954 return S_OK;