include: Add types to objidl.idl used by COM contexts.
[wine/testsucceed.git] / dlls / ole32 / storage32.c
blob187ba0b116d2aec8e166c00e5a37f3486d7fc4c8
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 DWORD length;
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 hFile = CreateFileW( pwcsName,
6040 accessMode,
6041 shareMode,
6042 NULL,
6043 OPEN_EXISTING,
6044 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6047 if (hFile==INVALID_HANDLE_VALUE)
6049 DWORD last_error = GetLastError();
6051 hr = E_FAIL;
6053 switch (last_error)
6055 case ERROR_FILE_NOT_FOUND:
6056 hr = STG_E_FILENOTFOUND;
6057 break;
6059 case ERROR_PATH_NOT_FOUND:
6060 hr = STG_E_PATHNOTFOUND;
6061 break;
6063 case ERROR_ACCESS_DENIED:
6064 case ERROR_WRITE_PROTECT:
6065 hr = STG_E_ACCESSDENIED;
6066 break;
6068 case ERROR_SHARING_VIOLATION:
6069 hr = STG_E_SHAREVIOLATION;
6070 break;
6072 default:
6073 hr = E_FAIL;
6076 goto end;
6080 * Refuse to open the file if it's too small to be a structured storage file
6081 * FIXME: verify the file when reading instead of here
6083 length = GetFileSize(hFile, NULL);
6084 if (length < 0x100)
6086 CloseHandle(hFile);
6087 hr = STG_E_FILEALREADYEXISTS;
6088 goto end;
6092 * Allocate and initialize the new IStorage32object.
6094 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6096 if (newStorage == 0)
6098 hr = STG_E_INSUFFICIENTMEMORY;
6099 goto end;
6102 /* if the file's length was zero, initialize the storage */
6103 hr = StorageImpl_Construct(
6104 newStorage,
6105 hFile,
6106 pwcsName,
6107 NULL,
6108 grfMode,
6109 TRUE,
6110 FALSE );
6112 if (FAILED(hr))
6114 HeapFree(GetProcessHeap(), 0, newStorage);
6116 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6118 if(hr == STG_E_INVALIDHEADER)
6119 hr = STG_E_FILEALREADYEXISTS;
6120 goto end;
6123 /* prepare the file name string given in lieu of the root property name */
6124 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6125 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6126 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6129 * Get an "out" pointer for the caller.
6131 hr = StorageBaseImpl_QueryInterface(
6132 (IStorage*)newStorage,
6133 (REFIID)&IID_IStorage,
6134 (void**)ppstgOpen);
6136 end:
6137 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6138 return hr;
6141 /******************************************************************************
6142 * StgCreateDocfileOnILockBytes [OLE32.@]
6144 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6145 ILockBytes *plkbyt,
6146 DWORD grfMode,
6147 DWORD reserved,
6148 IStorage** ppstgOpen)
6150 StorageImpl* newStorage = 0;
6151 HRESULT hr = S_OK;
6154 * Validate the parameters
6156 if ((ppstgOpen == 0) || (plkbyt == 0))
6157 return STG_E_INVALIDPOINTER;
6160 * Allocate and initialize the new IStorage object.
6162 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6164 if (newStorage == 0)
6165 return STG_E_INSUFFICIENTMEMORY;
6167 hr = StorageImpl_Construct(
6168 newStorage,
6171 plkbyt,
6172 grfMode,
6173 FALSE,
6174 TRUE);
6176 if (FAILED(hr))
6178 HeapFree(GetProcessHeap(), 0, newStorage);
6179 return hr;
6183 * Get an "out" pointer for the caller.
6185 hr = StorageBaseImpl_QueryInterface(
6186 (IStorage*)newStorage,
6187 (REFIID)&IID_IStorage,
6188 (void**)ppstgOpen);
6190 return hr;
6193 /******************************************************************************
6194 * StgOpenStorageOnILockBytes [OLE32.@]
6196 HRESULT WINAPI StgOpenStorageOnILockBytes(
6197 ILockBytes *plkbyt,
6198 IStorage *pstgPriority,
6199 DWORD grfMode,
6200 SNB snbExclude,
6201 DWORD reserved,
6202 IStorage **ppstgOpen)
6204 StorageImpl* newStorage = 0;
6205 HRESULT hr = S_OK;
6208 * Perform a sanity check
6210 if ((plkbyt == 0) || (ppstgOpen == 0))
6211 return STG_E_INVALIDPOINTER;
6214 * Validate the STGM flags
6216 if ( FAILED( validateSTGM(grfMode) ))
6217 return STG_E_INVALIDFLAG;
6220 * Initialize the "out" parameter.
6222 *ppstgOpen = 0;
6225 * Allocate and initialize the new IStorage object.
6227 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6229 if (newStorage == 0)
6230 return STG_E_INSUFFICIENTMEMORY;
6232 hr = StorageImpl_Construct(
6233 newStorage,
6236 plkbyt,
6237 grfMode,
6238 FALSE,
6239 FALSE);
6241 if (FAILED(hr))
6243 HeapFree(GetProcessHeap(), 0, newStorage);
6244 return hr;
6248 * Get an "out" pointer for the caller.
6250 hr = StorageBaseImpl_QueryInterface(
6251 (IStorage*)newStorage,
6252 (REFIID)&IID_IStorage,
6253 (void**)ppstgOpen);
6255 return hr;
6258 /******************************************************************************
6259 * StgSetTimes [ole32.@]
6260 * StgSetTimes [OLE32.@]
6264 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6265 FILETIME const *patime, FILETIME const *pmtime)
6267 IStorage *stg = NULL;
6268 HRESULT r;
6270 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6272 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6273 0, 0, &stg);
6274 if( SUCCEEDED(r) )
6276 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6277 IStorage_Release(stg);
6280 return r;
6283 /******************************************************************************
6284 * StgIsStorageILockBytes [OLE32.@]
6286 * Determines if the ILockBytes contains a storage object.
6288 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6290 BYTE sig[8];
6291 ULARGE_INTEGER offset;
6293 offset.u.HighPart = 0;
6294 offset.u.LowPart = 0;
6296 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6298 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6299 return S_OK;
6301 return S_FALSE;
6304 /******************************************************************************
6305 * WriteClassStg [OLE32.@]
6307 * This method will store the specified CLSID in the specified storage object
6309 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6311 HRESULT hRes;
6313 if(!pStg)
6314 return E_INVALIDARG;
6316 hRes = IStorage_SetClass(pStg, rclsid);
6318 return hRes;
6321 /***********************************************************************
6322 * ReadClassStg (OLE32.@)
6324 * This method reads the CLSID previously written to a storage object with
6325 * the WriteClassStg.
6327 * PARAMS
6328 * pstg [I] IStorage pointer
6329 * pclsid [O] Pointer to where the CLSID is written
6331 * RETURNS
6332 * Success: S_OK.
6333 * Failure: HRESULT code.
6335 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6337 STATSTG pstatstg;
6338 HRESULT hRes;
6340 TRACE("(%p, %p)\n", pstg, pclsid);
6342 if(!pstg || !pclsid)
6343 return E_INVALIDARG;
6346 * read a STATSTG structure (contains the clsid) from the storage
6348 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6350 if(SUCCEEDED(hRes))
6351 *pclsid=pstatstg.clsid;
6353 return hRes;
6356 /***********************************************************************
6357 * OleLoadFromStream (OLE32.@)
6359 * This function loads an object from stream
6361 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6363 CLSID clsid;
6364 HRESULT res;
6365 LPPERSISTSTREAM xstm;
6367 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6369 res=ReadClassStm(pStm,&clsid);
6370 if (!SUCCEEDED(res))
6371 return res;
6372 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6373 if (!SUCCEEDED(res))
6374 return res;
6375 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6376 if (!SUCCEEDED(res)) {
6377 IUnknown_Release((IUnknown*)*ppvObj);
6378 return res;
6380 res=IPersistStream_Load(xstm,pStm);
6381 IPersistStream_Release(xstm);
6382 /* FIXME: all refcounts ok at this point? I think they should be:
6383 * pStm : unchanged
6384 * ppvObj : 1
6385 * xstm : 0 (released)
6387 return res;
6390 /***********************************************************************
6391 * OleSaveToStream (OLE32.@)
6393 * This function saves an object with the IPersistStream interface on it
6394 * to the specified stream.
6396 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6399 CLSID clsid;
6400 HRESULT res;
6402 TRACE("(%p,%p)\n",pPStm,pStm);
6404 res=IPersistStream_GetClassID(pPStm,&clsid);
6406 if (SUCCEEDED(res)){
6408 res=WriteClassStm(pStm,&clsid);
6410 if (SUCCEEDED(res))
6412 res=IPersistStream_Save(pPStm,pStm,TRUE);
6415 TRACE("Finished Save\n");
6416 return res;
6419 /****************************************************************************
6420 * This method validate a STGM parameter that can contain the values below
6422 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6423 * The stgm values contained in 0xffff0000 are bitmasks.
6425 * STGM_DIRECT 0x00000000
6426 * STGM_TRANSACTED 0x00010000
6427 * STGM_SIMPLE 0x08000000
6429 * STGM_READ 0x00000000
6430 * STGM_WRITE 0x00000001
6431 * STGM_READWRITE 0x00000002
6433 * STGM_SHARE_DENY_NONE 0x00000040
6434 * STGM_SHARE_DENY_READ 0x00000030
6435 * STGM_SHARE_DENY_WRITE 0x00000020
6436 * STGM_SHARE_EXCLUSIVE 0x00000010
6438 * STGM_PRIORITY 0x00040000
6439 * STGM_DELETEONRELEASE 0x04000000
6441 * STGM_CREATE 0x00001000
6442 * STGM_CONVERT 0x00020000
6443 * STGM_FAILIFTHERE 0x00000000
6445 * STGM_NOSCRATCH 0x00100000
6446 * STGM_NOSNAPSHOT 0x00200000
6448 static HRESULT validateSTGM(DWORD stgm)
6450 DWORD access = STGM_ACCESS_MODE(stgm);
6451 DWORD share = STGM_SHARE_MODE(stgm);
6452 DWORD create = STGM_CREATE_MODE(stgm);
6454 if (stgm&~STGM_KNOWN_FLAGS)
6456 ERR("unknown flags %08lx\n", stgm);
6457 return E_FAIL;
6460 switch (access)
6462 case STGM_READ:
6463 case STGM_WRITE:
6464 case STGM_READWRITE:
6465 break;
6466 default:
6467 return E_FAIL;
6470 switch (share)
6472 case STGM_SHARE_DENY_NONE:
6473 case STGM_SHARE_DENY_READ:
6474 case STGM_SHARE_DENY_WRITE:
6475 case STGM_SHARE_EXCLUSIVE:
6476 break;
6477 default:
6478 return E_FAIL;
6481 switch (create)
6483 case STGM_CREATE:
6484 case STGM_FAILIFTHERE:
6485 break;
6486 default:
6487 return E_FAIL;
6491 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6493 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6494 return E_FAIL;
6497 * STGM_CREATE | STGM_CONVERT
6498 * if both are false, STGM_FAILIFTHERE is set to TRUE
6500 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6501 return E_FAIL;
6504 * STGM_NOSCRATCH requires STGM_TRANSACTED
6506 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6507 return E_FAIL;
6510 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6511 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6513 if ( (stgm & STGM_NOSNAPSHOT) &&
6514 (!(stgm & STGM_TRANSACTED) ||
6515 share == STGM_SHARE_EXCLUSIVE ||
6516 share == STGM_SHARE_DENY_WRITE) )
6517 return E_FAIL;
6519 return S_OK;
6522 /****************************************************************************
6523 * GetShareModeFromSTGM
6525 * This method will return a share mode flag from a STGM value.
6526 * The STGM value is assumed valid.
6528 static DWORD GetShareModeFromSTGM(DWORD stgm)
6530 switch (STGM_SHARE_MODE(stgm))
6532 case STGM_SHARE_DENY_NONE:
6533 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6534 case STGM_SHARE_DENY_READ:
6535 return FILE_SHARE_WRITE;
6536 case STGM_SHARE_DENY_WRITE:
6537 return FILE_SHARE_READ;
6538 case STGM_SHARE_EXCLUSIVE:
6539 return 0;
6541 ERR("Invalid share mode!\n");
6542 assert(0);
6543 return 0;
6546 /****************************************************************************
6547 * GetAccessModeFromSTGM
6549 * This method will return an access mode flag from a STGM value.
6550 * The STGM value is assumed valid.
6552 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6554 switch (STGM_ACCESS_MODE(stgm))
6556 case STGM_READ:
6557 return GENERIC_READ;
6558 case STGM_WRITE:
6559 case STGM_READWRITE:
6560 return GENERIC_READ | GENERIC_WRITE;
6562 ERR("Invalid access mode!\n");
6563 assert(0);
6564 return 0;
6567 /****************************************************************************
6568 * GetCreationModeFromSTGM
6570 * This method will return a creation mode flag from a STGM value.
6571 * The STGM value is assumed valid.
6573 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6575 switch(STGM_CREATE_MODE(stgm))
6577 case STGM_CREATE:
6578 return CREATE_ALWAYS;
6579 case STGM_CONVERT:
6580 FIXME("STGM_CONVERT not implemented!\n");
6581 return CREATE_NEW;
6582 case STGM_FAILIFTHERE:
6583 return CREATE_NEW;
6585 ERR("Invalid create mode!\n");
6586 assert(0);
6587 return 0;
6591 /*************************************************************************
6592 * OLECONVERT_LoadOLE10 [Internal]
6594 * Loads the OLE10 STREAM to memory
6596 * PARAMS
6597 * pOleStream [I] The OLESTREAM
6598 * pData [I] Data Structure for the OLESTREAM Data
6600 * RETURNS
6601 * Success: S_OK
6602 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6603 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6605 * NOTES
6606 * This function is used by OleConvertOLESTREAMToIStorage only.
6608 * Memory allocated for pData must be freed by the caller
6610 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6612 DWORD dwSize;
6613 HRESULT hRes = S_OK;
6614 int nTryCnt=0;
6615 int max_try = 6;
6617 pData->pData = NULL;
6618 pData->pstrOleObjFileName = (CHAR *) NULL;
6620 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6622 /* Get the OleID */
6623 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6624 if(dwSize != sizeof(pData->dwOleID))
6626 hRes = CONVERT10_E_OLESTREAM_GET;
6628 else if(pData->dwOleID != OLESTREAM_ID)
6630 hRes = CONVERT10_E_OLESTREAM_FMT;
6632 else
6634 hRes = S_OK;
6635 break;
6639 if(hRes == S_OK)
6641 /* Get the TypeID...more info needed for this field */
6642 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6643 if(dwSize != sizeof(pData->dwTypeID))
6645 hRes = CONVERT10_E_OLESTREAM_GET;
6648 if(hRes == S_OK)
6650 if(pData->dwTypeID != 0)
6652 /* Get the length of the OleTypeName */
6653 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6654 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6656 hRes = CONVERT10_E_OLESTREAM_GET;
6659 if(hRes == S_OK)
6661 if(pData->dwOleTypeNameLength > 0)
6663 /* Get the OleTypeName */
6664 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6665 if(dwSize != pData->dwOleTypeNameLength)
6667 hRes = CONVERT10_E_OLESTREAM_GET;
6671 if(bStrem1)
6673 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6674 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6676 hRes = CONVERT10_E_OLESTREAM_GET;
6678 if(hRes == S_OK)
6680 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6681 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6682 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6683 if(pData->pstrOleObjFileName)
6685 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6686 if(dwSize != pData->dwOleObjFileNameLength)
6688 hRes = CONVERT10_E_OLESTREAM_GET;
6691 else
6692 hRes = CONVERT10_E_OLESTREAM_GET;
6695 else
6697 /* Get the Width of the Metafile */
6698 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6699 if(dwSize != sizeof(pData->dwMetaFileWidth))
6701 hRes = CONVERT10_E_OLESTREAM_GET;
6703 if(hRes == S_OK)
6705 /* Get the Height of the Metafile */
6706 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6707 if(dwSize != sizeof(pData->dwMetaFileHeight))
6709 hRes = CONVERT10_E_OLESTREAM_GET;
6713 if(hRes == S_OK)
6715 /* Get the Length of the Data */
6716 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6717 if(dwSize != sizeof(pData->dwDataLength))
6719 hRes = CONVERT10_E_OLESTREAM_GET;
6723 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6725 if(!bStrem1) /* if it is a second OLE stream data */
6727 pData->dwDataLength -= 8;
6728 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6729 if(dwSize != sizeof(pData->strUnknown))
6731 hRes = CONVERT10_E_OLESTREAM_GET;
6735 if(hRes == S_OK)
6737 if(pData->dwDataLength > 0)
6739 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6741 /* Get Data (ex. IStorage, Metafile, or BMP) */
6742 if(pData->pData)
6744 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6745 if(dwSize != pData->dwDataLength)
6747 hRes = CONVERT10_E_OLESTREAM_GET;
6750 else
6752 hRes = CONVERT10_E_OLESTREAM_GET;
6758 return hRes;
6761 /*************************************************************************
6762 * OLECONVERT_SaveOLE10 [Internal]
6764 * Saves the OLE10 STREAM From memory
6766 * PARAMS
6767 * pData [I] Data Structure for the OLESTREAM Data
6768 * pOleStream [I] The OLESTREAM to save
6770 * RETURNS
6771 * Success: S_OK
6772 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6774 * NOTES
6775 * This function is used by OleConvertIStorageToOLESTREAM only.
6778 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6780 DWORD dwSize;
6781 HRESULT hRes = S_OK;
6784 /* Set the OleID */
6785 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6786 if(dwSize != sizeof(pData->dwOleID))
6788 hRes = CONVERT10_E_OLESTREAM_PUT;
6791 if(hRes == S_OK)
6793 /* Set the TypeID */
6794 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6795 if(dwSize != sizeof(pData->dwTypeID))
6797 hRes = CONVERT10_E_OLESTREAM_PUT;
6801 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6803 /* Set the Length of the OleTypeName */
6804 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6805 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6807 hRes = CONVERT10_E_OLESTREAM_PUT;
6810 if(hRes == S_OK)
6812 if(pData->dwOleTypeNameLength > 0)
6814 /* Set the OleTypeName */
6815 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6816 if(dwSize != pData->dwOleTypeNameLength)
6818 hRes = CONVERT10_E_OLESTREAM_PUT;
6823 if(hRes == S_OK)
6825 /* Set the width of the Metafile */
6826 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6827 if(dwSize != sizeof(pData->dwMetaFileWidth))
6829 hRes = CONVERT10_E_OLESTREAM_PUT;
6833 if(hRes == S_OK)
6835 /* Set the height of the Metafile */
6836 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6837 if(dwSize != sizeof(pData->dwMetaFileHeight))
6839 hRes = CONVERT10_E_OLESTREAM_PUT;
6843 if(hRes == S_OK)
6845 /* Set the length of the Data */
6846 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6847 if(dwSize != sizeof(pData->dwDataLength))
6849 hRes = CONVERT10_E_OLESTREAM_PUT;
6853 if(hRes == S_OK)
6855 if(pData->dwDataLength > 0)
6857 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6858 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6859 if(dwSize != pData->dwDataLength)
6861 hRes = CONVERT10_E_OLESTREAM_PUT;
6866 return hRes;
6869 /*************************************************************************
6870 * OLECONVERT_GetOLE20FromOLE10[Internal]
6872 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6873 * opens it, and copies the content to the dest IStorage for
6874 * OleConvertOLESTREAMToIStorage
6877 * PARAMS
6878 * pDestStorage [I] The IStorage to copy the data to
6879 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6880 * nBufferLength [I] The size of the buffer
6882 * RETURNS
6883 * Nothing
6885 * NOTES
6889 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6891 HRESULT hRes;
6892 HANDLE hFile;
6893 IStorage *pTempStorage;
6894 DWORD dwNumOfBytesWritten;
6895 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6896 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6898 /* Create a temp File */
6899 GetTempPathW(MAX_PATH, wstrTempDir);
6900 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6901 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6903 if(hFile != INVALID_HANDLE_VALUE)
6905 /* Write IStorage Data to File */
6906 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6907 CloseHandle(hFile);
6909 /* Open and copy temp storage to the Dest Storage */
6910 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6911 if(hRes == S_OK)
6913 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6914 StorageBaseImpl_Release(pTempStorage);
6916 DeleteFileW(wstrTempFile);
6921 /*************************************************************************
6922 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6924 * Saves the OLE10 STREAM From memory
6926 * PARAMS
6927 * pStorage [I] The Src IStorage to copy
6928 * pData [I] The Dest Memory to write to.
6930 * RETURNS
6931 * The size in bytes allocated for pData
6933 * NOTES
6934 * Memory allocated for pData must be freed by the caller
6936 * Used by OleConvertIStorageToOLESTREAM only.
6939 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6941 HANDLE hFile;
6942 HRESULT hRes;
6943 DWORD nDataLength = 0;
6944 IStorage *pTempStorage;
6945 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6946 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6948 *pData = NULL;
6950 /* Create temp Storage */
6951 GetTempPathW(MAX_PATH, wstrTempDir);
6952 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6953 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6955 if(hRes == S_OK)
6957 /* Copy Src Storage to the Temp Storage */
6958 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6959 StorageBaseImpl_Release(pTempStorage);
6961 /* Open Temp Storage as a file and copy to memory */
6962 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6963 if(hFile != INVALID_HANDLE_VALUE)
6965 nDataLength = GetFileSize(hFile, NULL);
6966 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6967 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6968 CloseHandle(hFile);
6970 DeleteFileW(wstrTempFile);
6972 return nDataLength;
6975 /*************************************************************************
6976 * OLECONVERT_CreateOleStream [Internal]
6978 * Creates the "\001OLE" stream in the IStorage if necessary.
6980 * PARAMS
6981 * pStorage [I] Dest storage to create the stream in
6983 * RETURNS
6984 * Nothing
6986 * NOTES
6987 * This function is used by OleConvertOLESTREAMToIStorage only.
6989 * This stream is still unknown, MS Word seems to have extra data
6990 * but since the data is stored in the OLESTREAM there should be
6991 * no need to recreate the stream. If the stream is manually
6992 * deleted it will create it with this default data.
6995 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6997 HRESULT hRes;
6998 IStream *pStream;
6999 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
7000 BYTE pOleStreamHeader [] =
7002 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
7003 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7004 0x00, 0x00, 0x00, 0x00
7007 /* Create stream if not present */
7008 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7009 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7011 if(hRes == S_OK)
7013 /* Write default Data */
7014 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7015 IStream_Release(pStream);
7019 /* write a string to a stream, preceded by its length */
7020 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7022 HRESULT r;
7023 LPSTR str;
7024 DWORD len = 0;
7026 if( string )
7027 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7028 r = IStream_Write( stm, &len, sizeof(len), NULL);
7029 if( FAILED( r ) )
7030 return r;
7031 if(len == 0)
7032 return r;
7033 str = CoTaskMemAlloc( len );
7034 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7035 r = IStream_Write( stm, str, len, NULL);
7036 CoTaskMemFree( str );
7037 return r;
7040 /* read a string preceded by its length from a stream */
7041 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7043 HRESULT r;
7044 DWORD len, count = 0;
7045 LPSTR str;
7046 LPWSTR wstr;
7048 r = IStream_Read( stm, &len, sizeof(len), &count );
7049 if( FAILED( r ) )
7050 return r;
7051 if( count != sizeof(len) )
7052 return E_OUTOFMEMORY;
7054 TRACE("%ld bytes\n",len);
7056 str = CoTaskMemAlloc( len );
7057 if( !str )
7058 return E_OUTOFMEMORY;
7059 count = 0;
7060 r = IStream_Read( stm, str, len, &count );
7061 if( FAILED( r ) )
7062 return r;
7063 if( count != len )
7065 CoTaskMemFree( str );
7066 return E_OUTOFMEMORY;
7069 TRACE("Read string %s\n",debugstr_an(str,len));
7071 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7072 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7073 if( wstr )
7074 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7075 CoTaskMemFree( str );
7077 *string = wstr;
7079 return r;
7083 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7084 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7086 IStream *pstm;
7087 HRESULT r = S_OK;
7088 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7090 static const BYTE unknown1[12] =
7091 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7092 0xFF, 0xFF, 0xFF, 0xFF};
7093 static const BYTE unknown2[16] =
7094 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7095 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7097 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7098 debugstr_w(lpszUserType), debugstr_w(szClipName),
7099 debugstr_w(szProgIDName));
7101 /* Create a CompObj stream if it doesn't exist */
7102 r = IStorage_CreateStream(pstg, szwStreamName,
7103 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7104 if( FAILED (r) )
7105 return r;
7107 /* Write CompObj Structure to stream */
7108 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7110 if( SUCCEEDED( r ) )
7111 r = WriteClassStm( pstm, clsid );
7113 if( SUCCEEDED( r ) )
7114 r = STREAM_WriteString( pstm, lpszUserType );
7115 if( SUCCEEDED( r ) )
7116 r = STREAM_WriteString( pstm, szClipName );
7117 if( SUCCEEDED( r ) )
7118 r = STREAM_WriteString( pstm, szProgIDName );
7119 if( SUCCEEDED( r ) )
7120 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7122 IStream_Release( pstm );
7124 return r;
7127 /***********************************************************************
7128 * WriteFmtUserTypeStg (OLE32.@)
7130 HRESULT WINAPI WriteFmtUserTypeStg(
7131 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7133 HRESULT r;
7134 WCHAR szwClipName[0x40];
7135 CLSID clsid = CLSID_NULL;
7136 LPWSTR wstrProgID = NULL;
7137 DWORD n;
7139 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7141 /* get the clipboard format name */
7142 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7143 szwClipName[n]=0;
7145 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7147 /* FIXME: There's room to save a CLSID and its ProgID, but
7148 the CLSID is not looked up in the registry and in all the
7149 tests I wrote it was CLSID_NULL. Where does it come from?
7152 /* get the real program ID. This may fail, but that's fine */
7153 ProgIDFromCLSID(&clsid, &wstrProgID);
7155 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7157 r = STORAGE_WriteCompObj( pstg, &clsid,
7158 lpszUserType, szwClipName, wstrProgID );
7160 CoTaskMemFree(wstrProgID);
7162 return r;
7166 /******************************************************************************
7167 * ReadFmtUserTypeStg [OLE32.@]
7169 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7171 HRESULT r;
7172 IStream *stm = 0;
7173 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7174 unsigned char unknown1[12];
7175 unsigned char unknown2[16];
7176 DWORD count;
7177 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7178 CLSID clsid;
7180 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7182 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7183 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7184 if( FAILED ( r ) )
7186 WARN("Failed to open stream r = %08lx\n", r);
7187 return r;
7190 /* read the various parts of the structure */
7191 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7192 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7193 goto end;
7194 r = ReadClassStm( stm, &clsid );
7195 if( FAILED( r ) )
7196 goto end;
7198 r = STREAM_ReadString( stm, &szCLSIDName );
7199 if( FAILED( r ) )
7200 goto end;
7202 r = STREAM_ReadString( stm, &szOleTypeName );
7203 if( FAILED( r ) )
7204 goto end;
7206 r = STREAM_ReadString( stm, &szProgIDName );
7207 if( FAILED( r ) )
7208 goto end;
7210 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7211 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7212 goto end;
7214 /* ok, success... now we just need to store what we found */
7215 if( pcf )
7216 *pcf = RegisterClipboardFormatW( szOleTypeName );
7217 CoTaskMemFree( szOleTypeName );
7219 if( lplpszUserType )
7220 *lplpszUserType = szCLSIDName;
7221 CoTaskMemFree( szProgIDName );
7223 end:
7224 IStream_Release( stm );
7226 return r;
7230 /*************************************************************************
7231 * OLECONVERT_CreateCompObjStream [Internal]
7233 * Creates a "\001CompObj" is the destination IStorage if necessary.
7235 * PARAMS
7236 * pStorage [I] The dest IStorage to create the CompObj Stream
7237 * if necessary.
7238 * strOleTypeName [I] The ProgID
7240 * RETURNS
7241 * Success: S_OK
7242 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7244 * NOTES
7245 * This function is used by OleConvertOLESTREAMToIStorage only.
7247 * The stream data is stored in the OLESTREAM and there should be
7248 * no need to recreate the stream. If the stream is manually
7249 * deleted it will attempt to create it by querying the registry.
7253 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7255 IStream *pStream;
7256 HRESULT hStorageRes, hRes = S_OK;
7257 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7258 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7259 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7261 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7262 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7264 /* Initialize the CompObj structure */
7265 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7266 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7267 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7270 /* Create a CompObj stream if it doesn't exist */
7271 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7272 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7273 if(hStorageRes == S_OK)
7275 /* copy the OleTypeName to the compobj struct */
7276 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7277 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7279 /* copy the OleTypeName to the compobj struct */
7280 /* Note: in the test made, these were Identical */
7281 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7282 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7284 /* Get the CLSID */
7285 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7286 bufferW, OLESTREAM_MAX_STR_LEN );
7287 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7289 if(hRes == S_OK)
7291 HKEY hKey;
7292 LONG hErr;
7293 /* Get the CLSID Default Name from the Registry */
7294 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7295 if(hErr == ERROR_SUCCESS)
7297 char strTemp[OLESTREAM_MAX_STR_LEN];
7298 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7299 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7300 if(hErr == ERROR_SUCCESS)
7302 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7304 RegCloseKey(hKey);
7308 /* Write CompObj Structure to stream */
7309 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7311 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7313 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7314 if(IStorageCompObj.dwCLSIDNameLength > 0)
7316 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7318 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7319 if(IStorageCompObj.dwOleTypeNameLength > 0)
7321 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7323 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7324 if(IStorageCompObj.dwProgIDNameLength > 0)
7326 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7328 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7329 IStream_Release(pStream);
7331 return hRes;
7335 /*************************************************************************
7336 * OLECONVERT_CreateOlePresStream[Internal]
7338 * Creates the "\002OlePres000" Stream with the Metafile data
7340 * PARAMS
7341 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7342 * dwExtentX [I] Width of the Metafile
7343 * dwExtentY [I] Height of the Metafile
7344 * pData [I] Metafile data
7345 * dwDataLength [I] Size of the Metafile data
7347 * RETURNS
7348 * Success: S_OK
7349 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7351 * NOTES
7352 * This function is used by OleConvertOLESTREAMToIStorage only.
7355 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7357 HRESULT hRes;
7358 IStream *pStream;
7359 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7360 BYTE pOlePresStreamHeader [] =
7362 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7363 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7364 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7365 0x00, 0x00, 0x00, 0x00
7368 BYTE pOlePresStreamHeaderEmpty [] =
7370 0x00, 0x00, 0x00, 0x00,
7371 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7372 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7373 0x00, 0x00, 0x00, 0x00
7376 /* Create the OlePres000 Stream */
7377 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7378 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7380 if(hRes == S_OK)
7382 DWORD nHeaderSize;
7383 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7385 memset(&OlePres, 0, sizeof(OlePres));
7386 /* Do we have any metafile data to save */
7387 if(dwDataLength > 0)
7389 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7390 nHeaderSize = sizeof(pOlePresStreamHeader);
7392 else
7394 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7395 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7397 /* Set width and height of the metafile */
7398 OlePres.dwExtentX = dwExtentX;
7399 OlePres.dwExtentY = -dwExtentY;
7401 /* Set Data and Length */
7402 if(dwDataLength > sizeof(METAFILEPICT16))
7404 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7405 OlePres.pData = &(pData[8]);
7407 /* Save OlePres000 Data to Stream */
7408 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7409 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7410 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7411 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7412 if(OlePres.dwSize > 0)
7414 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7416 IStream_Release(pStream);
7420 /*************************************************************************
7421 * OLECONVERT_CreateOle10NativeStream [Internal]
7423 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7425 * PARAMS
7426 * pStorage [I] Dest storage to create the stream in
7427 * pData [I] Ole10 Native Data (ex. bmp)
7428 * dwDataLength [I] Size of the Ole10 Native Data
7430 * RETURNS
7431 * Nothing
7433 * NOTES
7434 * This function is used by OleConvertOLESTREAMToIStorage only.
7436 * Might need to verify the data and return appropriate error message
7439 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7441 HRESULT hRes;
7442 IStream *pStream;
7443 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7445 /* Create the Ole10Native Stream */
7446 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7447 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7449 if(hRes == S_OK)
7451 /* Write info to stream */
7452 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7453 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7454 IStream_Release(pStream);
7459 /*************************************************************************
7460 * OLECONVERT_GetOLE10ProgID [Internal]
7462 * Finds the ProgID (or OleTypeID) from the IStorage
7464 * PARAMS
7465 * pStorage [I] The Src IStorage to get the ProgID
7466 * strProgID [I] the ProgID string to get
7467 * dwSize [I] the size of the string
7469 * RETURNS
7470 * Success: S_OK
7471 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7473 * NOTES
7474 * This function is used by OleConvertIStorageToOLESTREAM only.
7478 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7480 HRESULT hRes;
7481 IStream *pStream;
7482 LARGE_INTEGER iSeekPos;
7483 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7484 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7486 /* Open the CompObj Stream */
7487 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7488 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7489 if(hRes == S_OK)
7492 /*Get the OleType from the CompObj Stream */
7493 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7494 iSeekPos.u.HighPart = 0;
7496 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7497 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7498 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7499 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7500 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7501 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7502 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7504 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7505 if(*dwSize > 0)
7507 IStream_Read(pStream, strProgID, *dwSize, NULL);
7509 IStream_Release(pStream);
7511 else
7513 STATSTG stat;
7514 LPOLESTR wstrProgID;
7516 /* Get the OleType from the registry */
7517 REFCLSID clsid = &(stat.clsid);
7518 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7519 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7520 if(hRes == S_OK)
7522 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7526 return hRes;
7529 /*************************************************************************
7530 * OLECONVERT_GetOle10PresData [Internal]
7532 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7534 * PARAMS
7535 * pStorage [I] Src IStroage
7536 * pOleStream [I] Dest OleStream Mem Struct
7538 * RETURNS
7539 * Nothing
7541 * NOTES
7542 * This function is used by OleConvertIStorageToOLESTREAM only.
7544 * Memory allocated for pData must be freed by the caller
7548 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7551 HRESULT hRes;
7552 IStream *pStream;
7553 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7555 /* Initialize Default data for OLESTREAM */
7556 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7557 pOleStreamData[0].dwTypeID = 2;
7558 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7559 pOleStreamData[1].dwTypeID = 0;
7560 pOleStreamData[0].dwMetaFileWidth = 0;
7561 pOleStreamData[0].dwMetaFileHeight = 0;
7562 pOleStreamData[0].pData = NULL;
7563 pOleStreamData[1].pData = NULL;
7565 /* Open Ole10Native Stream */
7566 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7567 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7568 if(hRes == S_OK)
7571 /* Read Size and Data */
7572 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7573 if(pOleStreamData->dwDataLength > 0)
7575 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7576 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7578 IStream_Release(pStream);
7584 /*************************************************************************
7585 * OLECONVERT_GetOle20PresData[Internal]
7587 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7589 * PARAMS
7590 * pStorage [I] Src IStroage
7591 * pOleStreamData [I] Dest OleStream Mem Struct
7593 * RETURNS
7594 * Nothing
7596 * NOTES
7597 * This function is used by OleConvertIStorageToOLESTREAM only.
7599 * Memory allocated for pData must be freed by the caller
7601 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7603 HRESULT hRes;
7604 IStream *pStream;
7605 OLECONVERT_ISTORAGE_OLEPRES olePress;
7606 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7608 /* Initialize Default data for OLESTREAM */
7609 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7610 pOleStreamData[0].dwTypeID = 2;
7611 pOleStreamData[0].dwMetaFileWidth = 0;
7612 pOleStreamData[0].dwMetaFileHeight = 0;
7613 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7614 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7615 pOleStreamData[1].dwTypeID = 0;
7616 pOleStreamData[1].dwOleTypeNameLength = 0;
7617 pOleStreamData[1].strOleTypeName[0] = 0;
7618 pOleStreamData[1].dwMetaFileWidth = 0;
7619 pOleStreamData[1].dwMetaFileHeight = 0;
7620 pOleStreamData[1].pData = NULL;
7621 pOleStreamData[1].dwDataLength = 0;
7624 /* Open OlePress000 stream */
7625 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7626 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7627 if(hRes == S_OK)
7629 LARGE_INTEGER iSeekPos;
7630 METAFILEPICT16 MetaFilePict;
7631 static const char strMetafilePictName[] = "METAFILEPICT";
7633 /* Set the TypeID for a Metafile */
7634 pOleStreamData[1].dwTypeID = 5;
7636 /* Set the OleTypeName to Metafile */
7637 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7638 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7640 iSeekPos.u.HighPart = 0;
7641 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7643 /* Get Presentation Data */
7644 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7645 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7646 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7647 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7649 /*Set width and Height */
7650 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7651 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7652 if(olePress.dwSize > 0)
7654 /* Set Length */
7655 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7657 /* Set MetaFilePict struct */
7658 MetaFilePict.mm = 8;
7659 MetaFilePict.xExt = olePress.dwExtentX;
7660 MetaFilePict.yExt = olePress.dwExtentY;
7661 MetaFilePict.hMF = 0;
7663 /* Get Metafile Data */
7664 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7665 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7666 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7668 IStream_Release(pStream);
7672 /*************************************************************************
7673 * OleConvertOLESTREAMToIStorage [OLE32.@]
7675 * Read info on MSDN
7677 * TODO
7678 * DVTARGETDEVICE paramenter is not handled
7679 * Still unsure of some mem fields for OLE 10 Stream
7680 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7681 * and "\001OLE" streams
7684 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7685 LPOLESTREAM pOleStream,
7686 LPSTORAGE pstg,
7687 const DVTARGETDEVICE* ptd)
7689 int i;
7690 HRESULT hRes=S_OK;
7691 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7693 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7695 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7697 if(ptd != NULL)
7699 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7702 if(pstg == NULL || pOleStream == NULL)
7704 hRes = E_INVALIDARG;
7707 if(hRes == S_OK)
7709 /* Load the OLESTREAM to Memory */
7710 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7713 if(hRes == S_OK)
7715 /* Load the OLESTREAM to Memory (part 2)*/
7716 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7719 if(hRes == S_OK)
7722 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7724 /* Do we have the IStorage Data in the OLESTREAM */
7725 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7727 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7728 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7730 else
7732 /* It must be an original OLE 1.0 source */
7733 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7736 else
7738 /* It must be an original OLE 1.0 source */
7739 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7742 /* Create CompObj Stream if necessary */
7743 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7744 if(hRes == S_OK)
7746 /*Create the Ole Stream if necessary */
7747 OLECONVERT_CreateOleStream(pstg);
7752 /* Free allocated memory */
7753 for(i=0; i < 2; i++)
7755 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7756 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7757 pOleStreamData[i].pstrOleObjFileName = NULL;
7759 return hRes;
7762 /*************************************************************************
7763 * OleConvertIStorageToOLESTREAM [OLE32.@]
7765 * Read info on MSDN
7767 * Read info on MSDN
7769 * TODO
7770 * Still unsure of some mem fields for OLE 10 Stream
7771 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7772 * and "\001OLE" streams.
7775 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7776 LPSTORAGE pstg,
7777 LPOLESTREAM pOleStream)
7779 int i;
7780 HRESULT hRes = S_OK;
7781 IStream *pStream;
7782 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7783 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7785 TRACE("%p %p\n", pstg, pOleStream);
7787 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7789 if(pstg == NULL || pOleStream == NULL)
7791 hRes = E_INVALIDARG;
7793 if(hRes == S_OK)
7795 /* Get the ProgID */
7796 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7797 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7799 if(hRes == S_OK)
7801 /* Was it originally Ole10 */
7802 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7803 if(hRes == S_OK)
7805 IStream_Release(pStream);
7806 /* Get Presentation Data for Ole10Native */
7807 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7809 else
7811 /* Get Presentation Data (OLE20) */
7812 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7815 /* Save OLESTREAM */
7816 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7817 if(hRes == S_OK)
7819 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7824 /* Free allocated memory */
7825 for(i=0; i < 2; i++)
7827 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7830 return hRes;
7833 /***********************************************************************
7834 * GetConvertStg (OLE32.@)
7836 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7837 FIXME("unimplemented stub!\n");
7838 return E_FAIL;
7841 /******************************************************************************
7842 * StgIsStorageFile [OLE32.@]
7843 * Verify if the file contains a storage object
7845 * PARAMS
7846 * fn [ I] Filename
7848 * RETURNS
7849 * S_OK if file has magic bytes as a storage object
7850 * S_FALSE if file is not storage
7852 HRESULT WINAPI
7853 StgIsStorageFile(LPCOLESTR fn)
7855 HANDLE hf;
7856 BYTE magic[8];
7857 DWORD bytes_read;
7859 TRACE("%s\n", debugstr_w(fn));
7860 hf = CreateFileW(fn, GENERIC_READ,
7861 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7862 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7864 if (hf == INVALID_HANDLE_VALUE)
7865 return STG_E_FILENOTFOUND;
7867 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7869 WARN(" unable to read file\n");
7870 CloseHandle(hf);
7871 return S_FALSE;
7874 CloseHandle(hf);
7876 if (bytes_read != 8) {
7877 WARN(" too short\n");
7878 return S_FALSE;
7881 if (!memcmp(magic,STORAGE_magic,8)) {
7882 WARN(" -> YES\n");
7883 return S_OK;
7886 WARN(" -> Invalid header.\n");
7887 return S_FALSE;
7890 /***********************************************************************
7891 * WriteClassStm (OLE32.@)
7893 * Writes a CLSID to a stream.
7895 * PARAMS
7896 * pStm [I] Stream to write to.
7897 * rclsid [I] CLSID to write.
7899 * RETURNS
7900 * Success: S_OK.
7901 * Failure: HRESULT code.
7903 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7905 TRACE("(%p,%p)\n",pStm,rclsid);
7907 if (rclsid==NULL)
7908 return E_INVALIDARG;
7910 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7913 /***********************************************************************
7914 * ReadClassStm (OLE32.@)
7916 * Reads a CLSID from a stream.
7918 * PARAMS
7919 * pStm [I] Stream to read from.
7920 * rclsid [O] CLSID to read.
7922 * RETURNS
7923 * Success: S_OK.
7924 * Failure: HRESULT code.
7926 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7928 ULONG nbByte;
7929 HRESULT res;
7931 TRACE("(%p,%p)\n",pStm,pclsid);
7933 if (pclsid==NULL)
7934 return E_INVALIDARG;
7936 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7938 if (FAILED(res))
7939 return res;
7941 if (nbByte != sizeof(CLSID))
7942 return S_FALSE;
7943 else
7944 return S_OK;