comdlg32: Return defaults for hDevMode and hDevNames.
[wine/gsoc_dplay.git] / dlls / ole32 / storage32.c
blob584b132e22973fc9eba4319ec5d0922a51750f80
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
66 * These are signatures to detect the type of Document file.
68 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
69 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
71 static const char rootPropertyName[] = "Root Entry";
73 /****************************************************************************
74 * Storage32InternalImpl definitions.
76 * Definition of the implementation structure for the IStorage32 interface.
77 * This one implements the IStorage32 interface for storage that are
78 * inside another storage.
80 struct StorageInternalImpl
82 struct StorageBaseImpl base;
84 * There is no specific data for this class.
87 typedef struct StorageInternalImpl StorageInternalImpl;
89 /* Method definitions for the Storage32InternalImpl class. */
90 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
91 DWORD openFlags, ULONG rootTropertyIndex);
92 static void StorageImpl_Destroy(StorageBaseImpl* iface);
93 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
94 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
95 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
96 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
97 static void StorageImpl_SaveFileHeader(StorageImpl* This);
99 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
100 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
101 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
102 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
103 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
105 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
106 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
107 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
109 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
110 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
111 ULONG blockIndex, ULONG offset, DWORD value);
112 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
113 ULONG blockIndex, ULONG offset, DWORD* value);
115 /* OLESTREAM memory structure to use for Get and Put Routines */
116 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
117 typedef struct
119 DWORD dwOleID;
120 DWORD dwTypeID;
121 DWORD dwOleTypeNameLength;
122 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
123 CHAR *pstrOleObjFileName;
124 DWORD dwOleObjFileNameLength;
125 DWORD dwMetaFileWidth;
126 DWORD dwMetaFileHeight;
127 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
128 DWORD dwDataLength;
129 BYTE *pData;
130 }OLECONVERT_OLESTREAM_DATA;
132 /* CompObj Stream structure */
133 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
134 typedef struct
136 BYTE byUnknown1[12];
137 CLSID clsid;
138 DWORD dwCLSIDNameLength;
139 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
140 DWORD dwOleTypeNameLength;
141 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
142 DWORD dwProgIDNameLength;
143 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
144 BYTE byUnknown2[16];
145 }OLECONVERT_ISTORAGE_COMPOBJ;
148 /* Ole Presention Stream structure */
149 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
150 typedef struct
152 BYTE byUnknown1[28];
153 DWORD dwExtentX;
154 DWORD dwExtentY;
155 DWORD dwSize;
156 BYTE *pData;
157 }OLECONVERT_ISTORAGE_OLEPRES;
161 /***********************************************************************
162 * Forward declaration of internal functions used by the method DestroyElement
164 static HRESULT deleteStorageProperty(
165 StorageImpl *parentStorage,
166 ULONG foundPropertyIndexToDelete,
167 StgProperty propertyToDelete);
169 static HRESULT deleteStreamProperty(
170 StorageImpl *parentStorage,
171 ULONG foundPropertyIndexToDelete,
172 StgProperty propertyToDelete);
174 static HRESULT findPlaceholder(
175 StorageImpl *storage,
176 ULONG propertyIndexToStore,
177 ULONG storagePropertyIndex,
178 INT typeOfRelation);
180 static HRESULT adjustPropertyChain(
181 StorageImpl *This,
182 StgProperty propertyToDelete,
183 StgProperty parentProperty,
184 ULONG parentPropertyId,
185 INT typeOfRelation);
187 /***********************************************************************
188 * Declaration of the functions used to manipulate StgProperty
191 static ULONG getFreeProperty(
192 StorageImpl *storage);
194 static void updatePropertyChain(
195 StorageImpl *storage,
196 ULONG newPropertyIndex,
197 StgProperty newProperty);
199 static LONG propertyNameCmp(
200 const OLECHAR *newProperty,
201 const OLECHAR *currentProperty);
204 /***********************************************************************
205 * Declaration of miscellaneous functions...
207 static HRESULT validateSTGM(DWORD stgmValue);
209 static DWORD GetShareModeFromSTGM(DWORD stgm);
210 static DWORD GetAccessModeFromSTGM(DWORD stgm);
211 static DWORD GetCreationModeFromSTGM(DWORD stgm);
213 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
216 /****************************************************************************
217 * IEnumSTATSTGImpl definitions.
219 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
220 * This class allows iterating through the content of a storage and to find
221 * specific items inside it.
223 struct IEnumSTATSTGImpl
225 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
226 * since we want to cast this in an IEnumSTATSTG pointer */
228 LONG ref; /* Reference count */
229 StorageImpl* parentStorage; /* Reference to the parent storage */
230 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
233 * The current implementation of the IEnumSTATSTGImpl class uses a stack
234 * to walk the property sets to get the content of a storage. This stack
235 * is implemented by the following 3 data members
237 ULONG stackSize;
238 ULONG stackMaxSize;
239 ULONG* stackToVisit;
241 #define ENUMSTATSGT_SIZE_INCREMENT 10
245 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
246 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
247 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
248 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
249 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
250 StgProperty* buffer);
251 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
252 StgProperty *currentProperty, ULONG *propertyId);
254 /************************************************************************
255 ** Block Functions
258 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
260 if (index == 0xffffffff)
261 index = 0;
262 else
263 index ++;
265 return index * BIG_BLOCK_SIZE;
268 /************************************************************************
269 ** Storage32BaseImpl implementatiion
271 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
272 ULARGE_INTEGER offset,
273 void* buffer,
274 ULONG size,
275 ULONG* bytesRead)
277 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
280 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
281 ULARGE_INTEGER offset,
282 const void* buffer,
283 const ULONG size,
284 ULONG* bytesWritten)
286 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
289 /************************************************************************
290 * Storage32BaseImpl_QueryInterface (IUnknown)
292 * This method implements the common QueryInterface for all IStorage32
293 * implementations contained in this file.
295 * See Windows documentation for more details on IUnknown methods.
297 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
298 IStorage* iface,
299 REFIID riid,
300 void** ppvObject)
302 StorageBaseImpl *This = (StorageBaseImpl *)iface;
304 * Perform a sanity check on the parameters.
306 if ( (This==0) || (ppvObject==0) )
307 return E_INVALIDARG;
310 * Initialize the return parameter.
312 *ppvObject = 0;
315 * Compare the riid with the interface IDs implemented by this object.
317 if (IsEqualGUID(&IID_IUnknown, riid) ||
318 IsEqualGUID(&IID_IStorage, riid))
320 *ppvObject = (IStorage*)This;
322 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
324 *ppvObject = (IStorage*)&This->pssVtbl;
328 * Check that we obtained an interface.
330 if ((*ppvObject)==0)
331 return E_NOINTERFACE;
334 * Query Interface always increases the reference count by one when it is
335 * successful
337 IStorage_AddRef(iface);
339 return S_OK;
342 /************************************************************************
343 * Storage32BaseImpl_AddRef (IUnknown)
345 * This method implements the common AddRef for all IStorage32
346 * implementations contained in this file.
348 * See Windows documentation for more details on IUnknown methods.
350 static ULONG WINAPI StorageBaseImpl_AddRef(
351 IStorage* iface)
353 StorageBaseImpl *This = (StorageBaseImpl *)iface;
354 ULONG ref = InterlockedIncrement(&This->ref);
356 TRACE("(%p) AddRef to %d\n", This, ref);
358 return ref;
361 /************************************************************************
362 * Storage32BaseImpl_Release (IUnknown)
364 * This method implements the common Release for all IStorage32
365 * implementations contained in this file.
367 * See Windows documentation for more details on IUnknown methods.
369 static ULONG WINAPI StorageBaseImpl_Release(
370 IStorage* iface)
372 StorageBaseImpl *This = (StorageBaseImpl *)iface;
374 * Decrease the reference count on this object.
376 ULONG ref = InterlockedDecrement(&This->ref);
378 TRACE("(%p) ReleaseRef to %d\n", This, ref);
381 * If the reference count goes down to 0, perform suicide.
383 if (ref == 0)
386 * Since we are using a system of base-classes, we want to call the
387 * destructor of the appropriate derived class. To do this, we are
388 * using virtual functions to implement the destructor.
390 This->v_destructor(This);
393 return ref;
396 /************************************************************************
397 * Storage32BaseImpl_OpenStream (IStorage)
399 * This method will open the specified stream object from the current storage.
401 * See Windows documentation for more details on IStorage methods.
403 static HRESULT WINAPI StorageBaseImpl_OpenStream(
404 IStorage* iface,
405 const OLECHAR* pwcsName, /* [string][in] */
406 void* reserved1, /* [unique][in] */
407 DWORD grfMode, /* [in] */
408 DWORD reserved2, /* [in] */
409 IStream** ppstm) /* [out] */
411 StorageBaseImpl *This = (StorageBaseImpl *)iface;
412 IEnumSTATSTGImpl* propertyEnumeration;
413 StgStreamImpl* newStream;
414 StgProperty currentProperty;
415 ULONG foundPropertyIndex;
416 HRESULT res = STG_E_UNKNOWN;
418 TRACE("(%p, %s, %p, %x, %d, %p)\n",
419 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
422 * Perform a sanity check on the parameters.
424 if ( (pwcsName==NULL) || (ppstm==0) )
426 res = E_INVALIDARG;
427 goto end;
431 * Initialize the out parameter
433 *ppstm = NULL;
436 * Validate the STGM flags
438 if ( FAILED( validateSTGM(grfMode) ) ||
439 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
441 res = STG_E_INVALIDFLAG;
442 goto end;
446 * As documented.
448 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
450 res = STG_E_INVALIDFUNCTION;
451 goto end;
455 * Check that we're compatible with the parent's storage mode, but
456 * only if we are not in transacted mode
458 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
459 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
461 res = STG_E_ACCESSDENIED;
462 goto end;
467 * Create a property enumeration to search the properties
469 propertyEnumeration = IEnumSTATSTGImpl_Construct(
470 This->ancestorStorage,
471 This->rootPropertySetIndex);
474 * Search the enumeration for the property with the given name
476 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
477 propertyEnumeration,
478 pwcsName,
479 &currentProperty);
482 * Delete the property enumeration since we don't need it anymore
484 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
487 * If it was found, construct the stream object and return a pointer to it.
489 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
490 (currentProperty.propertyType==PROPTYPE_STREAM) )
492 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
494 if (newStream!=0)
496 newStream->grfMode = grfMode;
497 *ppstm = (IStream*)newStream;
500 * Since we are returning a pointer to the interface, we have to
501 * nail down the reference.
503 IStream_AddRef(*ppstm);
505 res = S_OK;
506 goto end;
509 res = E_OUTOFMEMORY;
510 goto end;
513 res = STG_E_FILENOTFOUND;
515 end:
516 if (res == S_OK)
517 TRACE("<-- IStream %p\n", *ppstm);
518 TRACE("<-- %08x\n", res);
519 return res;
522 /************************************************************************
523 * Storage32BaseImpl_OpenStorage (IStorage)
525 * This method will open a new storage object from the current storage.
527 * See Windows documentation for more details on IStorage methods.
529 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
530 IStorage* iface,
531 const OLECHAR* pwcsName, /* [string][unique][in] */
532 IStorage* pstgPriority, /* [unique][in] */
533 DWORD grfMode, /* [in] */
534 SNB snbExclude, /* [unique][in] */
535 DWORD reserved, /* [in] */
536 IStorage** ppstg) /* [out] */
538 StorageBaseImpl *This = (StorageBaseImpl *)iface;
539 StorageInternalImpl* newStorage;
540 IEnumSTATSTGImpl* propertyEnumeration;
541 StgProperty currentProperty;
542 ULONG foundPropertyIndex;
543 HRESULT res = STG_E_UNKNOWN;
545 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
546 iface, debugstr_w(pwcsName), pstgPriority,
547 grfMode, snbExclude, reserved, ppstg);
550 * Perform a sanity check on the parameters.
552 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
554 res = E_INVALIDARG;
555 goto end;
558 /* as documented */
559 if (snbExclude != NULL)
561 res = STG_E_INVALIDPARAMETER;
562 goto end;
566 * Validate the STGM flags
568 if ( FAILED( validateSTGM(grfMode) ))
570 res = STG_E_INVALIDFLAG;
571 goto end;
575 * As documented.
577 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
578 (grfMode & STGM_DELETEONRELEASE) ||
579 (grfMode & STGM_PRIORITY) )
581 res = STG_E_INVALIDFUNCTION;
582 goto end;
586 * Check that we're compatible with the parent's storage mode,
587 * but only if we are not transacted
589 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
590 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
592 res = STG_E_ACCESSDENIED;
593 goto end;
598 * Initialize the out parameter
600 *ppstg = NULL;
603 * Create a property enumeration to search the properties
605 propertyEnumeration = IEnumSTATSTGImpl_Construct(
606 This->ancestorStorage,
607 This->rootPropertySetIndex);
610 * Search the enumeration for the property with the given name
612 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
613 propertyEnumeration,
614 pwcsName,
615 &currentProperty);
618 * Delete the property enumeration since we don't need it anymore
620 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
623 * If it was found, construct the stream object and return a pointer to it.
625 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
626 (currentProperty.propertyType==PROPTYPE_STORAGE) )
629 * Construct a new Storage object
631 newStorage = StorageInternalImpl_Construct(
632 This->ancestorStorage,
633 grfMode,
634 foundPropertyIndex);
636 if (newStorage != 0)
638 *ppstg = (IStorage*)newStorage;
641 * Since we are returning a pointer to the interface,
642 * we have to nail down the reference.
644 StorageBaseImpl_AddRef(*ppstg);
646 res = S_OK;
647 goto end;
650 res = STG_E_INSUFFICIENTMEMORY;
651 goto end;
654 res = STG_E_FILENOTFOUND;
656 end:
657 TRACE("<-- %08x\n", res);
658 return res;
661 /************************************************************************
662 * Storage32BaseImpl_EnumElements (IStorage)
664 * This method will create an enumerator object that can be used to
665 * retrieve informatino about all the properties in the storage object.
667 * See Windows documentation for more details on IStorage methods.
669 static HRESULT WINAPI StorageBaseImpl_EnumElements(
670 IStorage* iface,
671 DWORD reserved1, /* [in] */
672 void* reserved2, /* [size_is][unique][in] */
673 DWORD reserved3, /* [in] */
674 IEnumSTATSTG** ppenum) /* [out] */
676 StorageBaseImpl *This = (StorageBaseImpl *)iface;
677 IEnumSTATSTGImpl* newEnum;
679 TRACE("(%p, %d, %p, %d, %p)\n",
680 iface, reserved1, reserved2, reserved3, ppenum);
683 * Perform a sanity check on the parameters.
685 if ( (This==0) || (ppenum==0))
686 return E_INVALIDARG;
689 * Construct the enumerator.
691 newEnum = IEnumSTATSTGImpl_Construct(
692 This->ancestorStorage,
693 This->rootPropertySetIndex);
695 if (newEnum!=0)
697 *ppenum = (IEnumSTATSTG*)newEnum;
700 * Don't forget to nail down a reference to the new object before
701 * returning it.
703 IEnumSTATSTG_AddRef(*ppenum);
705 return S_OK;
708 return E_OUTOFMEMORY;
711 /************************************************************************
712 * Storage32BaseImpl_Stat (IStorage)
714 * This method will retrieve information about this storage object.
716 * See Windows documentation for more details on IStorage methods.
718 static HRESULT WINAPI StorageBaseImpl_Stat(
719 IStorage* iface,
720 STATSTG* pstatstg, /* [out] */
721 DWORD grfStatFlag) /* [in] */
723 StorageBaseImpl *This = (StorageBaseImpl *)iface;
724 StgProperty curProperty;
725 BOOL readSuccessful;
726 HRESULT res = STG_E_UNKNOWN;
728 TRACE("(%p, %p, %x)\n",
729 iface, pstatstg, grfStatFlag);
732 * Perform a sanity check on the parameters.
734 if ( (This==0) || (pstatstg==0))
736 res = E_INVALIDARG;
737 goto end;
741 * Read the information from the property.
743 readSuccessful = StorageImpl_ReadProperty(
744 This->ancestorStorage,
745 This->rootPropertySetIndex,
746 &curProperty);
748 if (readSuccessful)
750 StorageUtl_CopyPropertyToSTATSTG(
751 pstatstg,
752 &curProperty,
753 grfStatFlag);
755 pstatstg->grfMode = This->openFlags;
756 pstatstg->grfStateBits = This->stateBits;
758 res = S_OK;
759 goto end;
762 res = E_FAIL;
764 end:
765 if (res == S_OK)
767 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
769 TRACE("<-- %08x\n", res);
770 return res;
773 /************************************************************************
774 * Storage32BaseImpl_RenameElement (IStorage)
776 * This method will rename the specified element.
778 * See Windows documentation for more details on IStorage methods.
780 * Implementation notes: The method used to rename consists of creating a clone
781 * of the deleted StgProperty object setting it with the new name and to
782 * perform a DestroyElement of the old StgProperty.
784 static HRESULT WINAPI StorageBaseImpl_RenameElement(
785 IStorage* iface,
786 const OLECHAR* pwcsOldName, /* [in] */
787 const OLECHAR* pwcsNewName) /* [in] */
789 StorageBaseImpl *This = (StorageBaseImpl *)iface;
790 IEnumSTATSTGImpl* propertyEnumeration;
791 StgProperty currentProperty;
792 ULONG foundPropertyIndex;
794 TRACE("(%p, %s, %s)\n",
795 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
798 * Create a property enumeration to search the properties
800 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
801 This->rootPropertySetIndex);
804 * Search the enumeration for the new property name
806 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
807 pwcsNewName,
808 &currentProperty);
810 if (foundPropertyIndex != PROPERTY_NULL)
813 * There is already a property with the new name
815 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
816 return STG_E_FILEALREADYEXISTS;
819 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
822 * Search the enumeration for the old property name
824 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
825 pwcsOldName,
826 &currentProperty);
829 * Delete the property enumeration since we don't need it anymore
831 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
833 if (foundPropertyIndex != PROPERTY_NULL)
835 StgProperty renamedProperty;
836 ULONG renamedPropertyIndex;
839 * Setup a new property for the renamed property
841 renamedProperty.sizeOfNameString =
842 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
844 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
845 return STG_E_INVALIDNAME;
847 strcpyW(renamedProperty.name, pwcsNewName);
849 renamedProperty.propertyType = currentProperty.propertyType;
850 renamedProperty.startingBlock = currentProperty.startingBlock;
851 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
852 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
854 renamedProperty.previousProperty = PROPERTY_NULL;
855 renamedProperty.nextProperty = PROPERTY_NULL;
858 * Bring the dirProperty link in case it is a storage and in which
859 * case the renamed storage elements don't require to be reorganized.
861 renamedProperty.dirProperty = currentProperty.dirProperty;
863 /* call CoFileTime to get the current time
864 renamedProperty.timeStampS1
865 renamedProperty.timeStampD1
866 renamedProperty.timeStampS2
867 renamedProperty.timeStampD2
868 renamedProperty.propertyUniqueID
872 * Obtain a free property in the property chain
874 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
877 * Save the new property into the new property spot
879 StorageImpl_WriteProperty(
880 This->ancestorStorage,
881 renamedPropertyIndex,
882 &renamedProperty);
885 * Find a spot in the property chain for our newly created property.
887 updatePropertyChain(
888 (StorageImpl*)This,
889 renamedPropertyIndex,
890 renamedProperty);
893 * At this point the renamed property has been inserted in the tree,
894 * now, before Destroying the old property we must zero its dirProperty
895 * otherwise the DestroyProperty below will zap it all and we do not want
896 * this to happen.
897 * Also, we fake that the old property is a storage so the DestroyProperty
898 * will not do a SetSize(0) on the stream data.
900 * This means that we need to tweak the StgProperty if it is a stream or a
901 * non empty storage.
903 StorageImpl_ReadProperty(This->ancestorStorage,
904 foundPropertyIndex,
905 &currentProperty);
907 currentProperty.dirProperty = PROPERTY_NULL;
908 currentProperty.propertyType = PROPTYPE_STORAGE;
909 StorageImpl_WriteProperty(
910 This->ancestorStorage,
911 foundPropertyIndex,
912 &currentProperty);
915 * Invoke Destroy to get rid of the ole property and automatically redo
916 * the linking of its previous and next members...
918 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
921 else
924 * There is no property with the old name
926 return STG_E_FILENOTFOUND;
929 return S_OK;
932 /************************************************************************
933 * Storage32BaseImpl_CreateStream (IStorage)
935 * This method will create a stream object within this storage
937 * See Windows documentation for more details on IStorage methods.
939 static HRESULT WINAPI StorageBaseImpl_CreateStream(
940 IStorage* iface,
941 const OLECHAR* pwcsName, /* [string][in] */
942 DWORD grfMode, /* [in] */
943 DWORD reserved1, /* [in] */
944 DWORD reserved2, /* [in] */
945 IStream** ppstm) /* [out] */
947 StorageBaseImpl *This = (StorageBaseImpl *)iface;
948 IEnumSTATSTGImpl* propertyEnumeration;
949 StgStreamImpl* newStream;
950 StgProperty currentProperty, newStreamProperty;
951 ULONG foundPropertyIndex, newPropertyIndex;
953 TRACE("(%p, %s, %x, %d, %d, %p)\n",
954 iface, debugstr_w(pwcsName), grfMode,
955 reserved1, reserved2, ppstm);
958 * Validate parameters
960 if (ppstm == 0)
961 return STG_E_INVALIDPOINTER;
963 if (pwcsName == 0)
964 return STG_E_INVALIDNAME;
966 if (reserved1 || reserved2)
967 return STG_E_INVALIDPARAMETER;
970 * Validate the STGM flags
972 if ( FAILED( validateSTGM(grfMode) ))
973 return STG_E_INVALIDFLAG;
975 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
976 return STG_E_INVALIDFLAG;
979 * As documented.
981 if ((grfMode & STGM_DELETEONRELEASE) ||
982 (grfMode & STGM_TRANSACTED))
983 return STG_E_INVALIDFUNCTION;
986 * Check that we're compatible with the parent's storage mode
987 * if not in transacted mode
989 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
990 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
991 return STG_E_ACCESSDENIED;
995 * Initialize the out parameter
997 *ppstm = 0;
1000 * Create a property enumeration to search the properties
1002 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1003 This->rootPropertySetIndex);
1005 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1006 pwcsName,
1007 &currentProperty);
1009 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1011 if (foundPropertyIndex != PROPERTY_NULL)
1014 * An element with this name already exists
1016 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1018 IStorage_DestroyElement(iface, pwcsName);
1020 else
1021 return STG_E_FILEALREADYEXISTS;
1023 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1025 WARN("read-only storage\n");
1026 return STG_E_ACCESSDENIED;
1030 * memset the empty property
1032 memset(&newStreamProperty, 0, sizeof(StgProperty));
1034 newStreamProperty.sizeOfNameString =
1035 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1037 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1038 return STG_E_INVALIDNAME;
1040 strcpyW(newStreamProperty.name, pwcsName);
1042 newStreamProperty.propertyType = PROPTYPE_STREAM;
1043 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1044 newStreamProperty.size.u.LowPart = 0;
1045 newStreamProperty.size.u.HighPart = 0;
1047 newStreamProperty.previousProperty = PROPERTY_NULL;
1048 newStreamProperty.nextProperty = PROPERTY_NULL;
1049 newStreamProperty.dirProperty = PROPERTY_NULL;
1051 /* call CoFileTime to get the current time
1052 newStreamProperty.timeStampS1
1053 newStreamProperty.timeStampD1
1054 newStreamProperty.timeStampS2
1055 newStreamProperty.timeStampD2
1058 /* newStreamProperty.propertyUniqueID */
1061 * Get a free property or create a new one
1063 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1066 * Save the new property into the new property spot
1068 StorageImpl_WriteProperty(
1069 This->ancestorStorage,
1070 newPropertyIndex,
1071 &newStreamProperty);
1074 * Find a spot in the property chain for our newly created property.
1076 updatePropertyChain(
1077 (StorageImpl*)This,
1078 newPropertyIndex,
1079 newStreamProperty);
1082 * Open the stream to return it.
1084 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1086 if (newStream != 0)
1088 *ppstm = (IStream*)newStream;
1091 * Since we are returning a pointer to the interface, we have to nail down
1092 * the reference.
1094 IStream_AddRef(*ppstm);
1096 else
1098 return STG_E_INSUFFICIENTMEMORY;
1101 return S_OK;
1104 /************************************************************************
1105 * Storage32BaseImpl_SetClass (IStorage)
1107 * This method will write the specified CLSID in the property of this
1108 * storage.
1110 * See Windows documentation for more details on IStorage methods.
1112 static HRESULT WINAPI StorageBaseImpl_SetClass(
1113 IStorage* iface,
1114 REFCLSID clsid) /* [in] */
1116 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1117 HRESULT hRes = E_FAIL;
1118 StgProperty curProperty;
1119 BOOL success;
1121 TRACE("(%p, %p)\n", iface, clsid);
1123 success = StorageImpl_ReadProperty(This->ancestorStorage,
1124 This->rootPropertySetIndex,
1125 &curProperty);
1126 if (success)
1128 curProperty.propertyUniqueID = *clsid;
1130 success = StorageImpl_WriteProperty(This->ancestorStorage,
1131 This->rootPropertySetIndex,
1132 &curProperty);
1133 if (success)
1134 hRes = S_OK;
1137 return hRes;
1140 /************************************************************************
1141 ** Storage32Impl implementation
1144 /************************************************************************
1145 * Storage32Impl_CreateStorage (IStorage)
1147 * This method will create the storage object within the provided storage.
1149 * See Windows documentation for more details on IStorage methods.
1151 static HRESULT WINAPI StorageImpl_CreateStorage(
1152 IStorage* iface,
1153 const OLECHAR *pwcsName, /* [string][in] */
1154 DWORD grfMode, /* [in] */
1155 DWORD reserved1, /* [in] */
1156 DWORD reserved2, /* [in] */
1157 IStorage **ppstg) /* [out] */
1159 StorageImpl* const This=(StorageImpl*)iface;
1161 IEnumSTATSTGImpl *propertyEnumeration;
1162 StgProperty currentProperty;
1163 StgProperty newProperty;
1164 ULONG foundPropertyIndex;
1165 ULONG newPropertyIndex;
1166 HRESULT hr;
1168 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1169 iface, debugstr_w(pwcsName), grfMode,
1170 reserved1, reserved2, ppstg);
1173 * Validate parameters
1175 if (ppstg == 0)
1176 return STG_E_INVALIDPOINTER;
1178 if (pwcsName == 0)
1179 return STG_E_INVALIDNAME;
1182 * Initialize the out parameter
1184 *ppstg = NULL;
1187 * Validate the STGM flags
1189 if ( FAILED( validateSTGM(grfMode) ) ||
1190 (grfMode & STGM_DELETEONRELEASE) )
1192 WARN("bad grfMode: 0x%x\n", grfMode);
1193 return STG_E_INVALIDFLAG;
1197 * Check that we're compatible with the parent's storage mode
1199 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1201 WARN("access denied\n");
1202 return STG_E_ACCESSDENIED;
1206 * Create a property enumeration and search the properties
1208 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1209 This->base.rootPropertySetIndex);
1211 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1212 pwcsName,
1213 &currentProperty);
1214 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1216 if (foundPropertyIndex != PROPERTY_NULL)
1219 * An element with this name already exists
1221 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1222 IStorage_DestroyElement(iface, pwcsName);
1223 else
1225 WARN("file already exists\n");
1226 return STG_E_FILEALREADYEXISTS;
1229 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1231 WARN("read-only storage\n");
1232 return STG_E_ACCESSDENIED;
1236 * memset the empty property
1238 memset(&newProperty, 0, sizeof(StgProperty));
1240 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1242 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1244 FIXME("name too long\n");
1245 return STG_E_INVALIDNAME;
1248 strcpyW(newProperty.name, pwcsName);
1250 newProperty.propertyType = PROPTYPE_STORAGE;
1251 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1252 newProperty.size.u.LowPart = 0;
1253 newProperty.size.u.HighPart = 0;
1255 newProperty.previousProperty = PROPERTY_NULL;
1256 newProperty.nextProperty = PROPERTY_NULL;
1257 newProperty.dirProperty = PROPERTY_NULL;
1259 /* call CoFileTime to get the current time
1260 newProperty.timeStampS1
1261 newProperty.timeStampD1
1262 newProperty.timeStampS2
1263 newProperty.timeStampD2
1266 /* newStorageProperty.propertyUniqueID */
1269 * Obtain a free property in the property chain
1271 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1274 * Save the new property into the new property spot
1276 StorageImpl_WriteProperty(
1277 This->base.ancestorStorage,
1278 newPropertyIndex,
1279 &newProperty);
1282 * Find a spot in the property chain for our newly created property.
1284 updatePropertyChain(
1285 This,
1286 newPropertyIndex,
1287 newProperty);
1290 * Open it to get a pointer to return.
1292 hr = IStorage_OpenStorage(
1293 iface,
1294 (const OLECHAR*)pwcsName,
1296 grfMode,
1299 ppstg);
1301 if( (hr != S_OK) || (*ppstg == NULL))
1303 return hr;
1307 return S_OK;
1311 /***************************************************************************
1313 * Internal Method
1315 * Get a free property or create a new one.
1317 static ULONG getFreeProperty(
1318 StorageImpl *storage)
1320 ULONG currentPropertyIndex = 0;
1321 ULONG newPropertyIndex = PROPERTY_NULL;
1322 BOOL readSuccessful = TRUE;
1323 StgProperty currentProperty;
1328 * Start by reading the root property
1330 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1331 currentPropertyIndex,
1332 &currentProperty);
1333 if (readSuccessful)
1335 if (currentProperty.sizeOfNameString == 0)
1338 * The property existis and is available, we found it.
1340 newPropertyIndex = currentPropertyIndex;
1343 else
1346 * We exhausted the property list, we will create more space below
1348 newPropertyIndex = currentPropertyIndex;
1350 currentPropertyIndex++;
1352 } while (newPropertyIndex == PROPERTY_NULL);
1355 * grow the property chain
1357 if (! readSuccessful)
1359 StgProperty emptyProperty;
1360 ULARGE_INTEGER newSize;
1361 ULONG propertyIndex;
1362 ULONG lastProperty = 0;
1363 ULONG blockCount = 0;
1366 * obtain the new count of property blocks
1368 blockCount = BlockChainStream_GetCount(
1369 storage->base.ancestorStorage->rootBlockChain)+1;
1372 * initialize the size used by the property stream
1374 newSize.u.HighPart = 0;
1375 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1378 * add a property block to the property chain
1380 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1383 * memset the empty property in order to initialize the unused newly
1384 * created property
1386 memset(&emptyProperty, 0, sizeof(StgProperty));
1389 * initialize them
1391 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1393 for(
1394 propertyIndex = newPropertyIndex;
1395 propertyIndex < lastProperty;
1396 propertyIndex++)
1398 StorageImpl_WriteProperty(
1399 storage->base.ancestorStorage,
1400 propertyIndex,
1401 &emptyProperty);
1405 return newPropertyIndex;
1408 /****************************************************************************
1410 * Internal Method
1412 * Case insensitive comparaison of StgProperty.name by first considering
1413 * their size.
1415 * Returns <0 when newPrpoerty < currentProperty
1416 * >0 when newPrpoerty > currentProperty
1417 * 0 when newPrpoerty == currentProperty
1419 static LONG propertyNameCmp(
1420 const OLECHAR *newProperty,
1421 const OLECHAR *currentProperty)
1423 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1425 if (diff == 0)
1428 * We compare the string themselves only when they are of the same length
1430 diff = lstrcmpiW( newProperty, currentProperty);
1433 return diff;
1436 /****************************************************************************
1438 * Internal Method
1440 * Properly link this new element in the property chain.
1442 static void updatePropertyChain(
1443 StorageImpl *storage,
1444 ULONG newPropertyIndex,
1445 StgProperty newProperty)
1447 StgProperty currentProperty;
1450 * Read the root property
1452 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1453 storage->base.rootPropertySetIndex,
1454 &currentProperty);
1456 if (currentProperty.dirProperty != PROPERTY_NULL)
1459 * The root storage contains some element, therefore, start the research
1460 * for the appropriate location.
1462 BOOL found = 0;
1463 ULONG current, next, previous, currentPropertyId;
1466 * Keep the StgProperty sequence number of the storage first property
1468 currentPropertyId = currentProperty.dirProperty;
1471 * Read
1473 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1474 currentProperty.dirProperty,
1475 &currentProperty);
1477 previous = currentProperty.previousProperty;
1478 next = currentProperty.nextProperty;
1479 current = currentPropertyId;
1481 while (found == 0)
1483 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1485 if (diff < 0)
1487 if (previous != PROPERTY_NULL)
1489 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1490 previous,
1491 &currentProperty);
1492 current = previous;
1494 else
1496 currentProperty.previousProperty = newPropertyIndex;
1497 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1498 current,
1499 &currentProperty);
1500 found = 1;
1503 else if (diff > 0)
1505 if (next != PROPERTY_NULL)
1507 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1508 next,
1509 &currentProperty);
1510 current = next;
1512 else
1514 currentProperty.nextProperty = newPropertyIndex;
1515 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1516 current,
1517 &currentProperty);
1518 found = 1;
1521 else
1524 * Trying to insert an item with the same name in the
1525 * subtree structure.
1527 assert(FALSE);
1530 previous = currentProperty.previousProperty;
1531 next = currentProperty.nextProperty;
1534 else
1537 * The root storage is empty, link the new property to its dir property
1539 currentProperty.dirProperty = newPropertyIndex;
1540 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1541 storage->base.rootPropertySetIndex,
1542 &currentProperty);
1547 /*************************************************************************
1548 * CopyTo (IStorage)
1550 static HRESULT WINAPI StorageImpl_CopyTo(
1551 IStorage* iface,
1552 DWORD ciidExclude, /* [in] */
1553 const IID* rgiidExclude, /* [size_is][unique][in] */
1554 SNB snbExclude, /* [unique][in] */
1555 IStorage* pstgDest) /* [unique][in] */
1557 IEnumSTATSTG *elements = 0;
1558 STATSTG curElement, strStat;
1559 HRESULT hr;
1560 IStorage *pstgTmp, *pstgChild;
1561 IStream *pstrTmp, *pstrChild;
1563 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1564 FIXME("Exclude option not implemented\n");
1566 TRACE("(%p, %d, %p, %p, %p)\n",
1567 iface, ciidExclude, rgiidExclude,
1568 snbExclude, pstgDest);
1571 * Perform a sanity check
1573 if ( pstgDest == 0 )
1574 return STG_E_INVALIDPOINTER;
1577 * Enumerate the elements
1579 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1581 if ( hr != S_OK )
1582 return hr;
1585 * set the class ID
1587 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1588 IStorage_SetClass( pstgDest, &curElement.clsid );
1593 * Obtain the next element
1595 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1597 if ( hr == S_FALSE )
1599 hr = S_OK; /* done, every element has been copied */
1600 break;
1603 if (curElement.type == STGTY_STORAGE)
1606 * open child source storage
1608 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1609 STGM_READ|STGM_SHARE_EXCLUSIVE,
1610 NULL, 0, &pstgChild );
1612 if (hr != S_OK)
1613 break;
1616 * Check if destination storage is not a child of the source
1617 * storage, which will cause an infinite loop
1619 if (pstgChild == pstgDest)
1621 IEnumSTATSTG_Release(elements);
1623 return STG_E_ACCESSDENIED;
1627 * create a new storage in destination storage
1629 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1630 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1631 0, 0,
1632 &pstgTmp );
1634 * if it already exist, don't create a new one use this one
1636 if (hr == STG_E_FILEALREADYEXISTS)
1638 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1639 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1640 NULL, 0, &pstgTmp );
1643 if (hr != S_OK)
1644 break;
1648 * do the copy recursively
1650 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1651 snbExclude, pstgTmp );
1653 IStorage_Release( pstgTmp );
1654 IStorage_Release( pstgChild );
1656 else if (curElement.type == STGTY_STREAM)
1659 * create a new stream in destination storage. If the stream already
1660 * exist, it will be deleted and a new one will be created.
1662 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1663 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1664 0, 0, &pstrTmp );
1666 if (hr != S_OK)
1667 break;
1670 * open child stream storage
1672 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1673 STGM_READ|STGM_SHARE_EXCLUSIVE,
1674 0, &pstrChild );
1676 if (hr != S_OK)
1677 break;
1680 * Get the size of the source stream
1682 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1685 * Set the size of the destination stream.
1687 IStream_SetSize(pstrTmp, strStat.cbSize);
1690 * do the copy
1692 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1693 NULL, NULL );
1695 IStream_Release( pstrTmp );
1696 IStream_Release( pstrChild );
1698 else
1700 WARN("unknown element type: %d\n", curElement.type);
1703 } while (hr == S_OK);
1706 * Clean-up
1708 IEnumSTATSTG_Release(elements);
1710 return hr;
1713 /*************************************************************************
1714 * MoveElementTo (IStorage)
1716 static HRESULT WINAPI StorageImpl_MoveElementTo(
1717 IStorage* iface,
1718 const OLECHAR *pwcsName, /* [string][in] */
1719 IStorage *pstgDest, /* [unique][in] */
1720 const OLECHAR *pwcsNewName,/* [string][in] */
1721 DWORD grfFlags) /* [in] */
1723 FIXME("(%p %s %p %s %u): stub\n", iface,
1724 debugstr_w(pwcsName), pstgDest,
1725 debugstr_w(pwcsNewName), grfFlags);
1726 return E_NOTIMPL;
1729 /*************************************************************************
1730 * Commit (IStorage)
1732 * Ensures that any changes made to a storage object open in transacted mode
1733 * are reflected in the parent storage
1735 * NOTES
1736 * Wine doesn't implement transacted mode, which seems to be a basic
1737 * optimization, so we can ignore this stub for now.
1739 static HRESULT WINAPI StorageImpl_Commit(
1740 IStorage* iface,
1741 DWORD grfCommitFlags)/* [in] */
1743 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1744 return S_OK;
1747 /*************************************************************************
1748 * Revert (IStorage)
1750 * Discard all changes that have been made since the last commit operation
1752 static HRESULT WINAPI StorageImpl_Revert(
1753 IStorage* iface)
1755 FIXME("(%p): stub\n", iface);
1756 return E_NOTIMPL;
1759 /*************************************************************************
1760 * DestroyElement (IStorage)
1762 * Strategy: This implementation is built this way for simplicity not for speed.
1763 * I always delete the topmost element of the enumeration and adjust
1764 * the deleted element pointer all the time. This takes longer to
1765 * do but allow to reinvoke DestroyElement whenever we encounter a
1766 * storage object. The optimisation resides in the usage of another
1767 * enumeration strategy that would give all the leaves of a storage
1768 * first. (postfix order)
1770 static HRESULT WINAPI StorageImpl_DestroyElement(
1771 IStorage* iface,
1772 const OLECHAR *pwcsName)/* [string][in] */
1774 StorageImpl* const This=(StorageImpl*)iface;
1776 IEnumSTATSTGImpl* propertyEnumeration;
1777 HRESULT hr = S_OK;
1778 BOOL res;
1779 StgProperty propertyToDelete;
1780 StgProperty parentProperty;
1781 ULONG foundPropertyIndexToDelete;
1782 ULONG typeOfRelation;
1783 ULONG parentPropertyId = 0;
1785 TRACE("(%p, %s)\n",
1786 iface, debugstr_w(pwcsName));
1789 * Perform a sanity check on the parameters.
1791 if (pwcsName==NULL)
1792 return STG_E_INVALIDPOINTER;
1795 * Create a property enumeration to search the property with the given name
1797 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1798 This->base.ancestorStorage,
1799 This->base.rootPropertySetIndex);
1801 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1802 propertyEnumeration,
1803 pwcsName,
1804 &propertyToDelete);
1806 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1808 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1810 return STG_E_FILENOTFOUND;
1814 * Find the parent property of the property to delete (the one that
1815 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1816 * the parent is This. Otherwise, the parent is one of its sibling...
1820 * First, read This's StgProperty..
1822 res = StorageImpl_ReadProperty(
1823 This->base.ancestorStorage,
1824 This->base.rootPropertySetIndex,
1825 &parentProperty);
1827 assert(res);
1830 * Second, check to see if by any chance the actual storage (This) is not
1831 * the parent of the property to delete... We never know...
1833 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1836 * Set data as it would have been done in the else part...
1838 typeOfRelation = PROPERTY_RELATION_DIR;
1839 parentPropertyId = This->base.rootPropertySetIndex;
1841 else
1844 * Create a property enumeration to search the parent properties, and
1845 * delete it once done.
1847 IEnumSTATSTGImpl* propertyEnumeration2;
1849 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1850 This->base.ancestorStorage,
1851 This->base.rootPropertySetIndex);
1853 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1854 propertyEnumeration2,
1855 foundPropertyIndexToDelete,
1856 &parentProperty,
1857 &parentPropertyId);
1859 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1862 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1864 hr = deleteStorageProperty(
1865 This,
1866 foundPropertyIndexToDelete,
1867 propertyToDelete);
1869 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1871 hr = deleteStreamProperty(
1872 This,
1873 foundPropertyIndexToDelete,
1874 propertyToDelete);
1877 if (hr!=S_OK)
1878 return hr;
1881 * Adjust the property chain
1883 hr = adjustPropertyChain(
1884 This,
1885 propertyToDelete,
1886 parentProperty,
1887 parentPropertyId,
1888 typeOfRelation);
1890 return hr;
1894 /************************************************************************
1895 * StorageImpl_Stat (IStorage)
1897 * This method will retrieve information about this storage object.
1899 * See Windows documentation for more details on IStorage methods.
1901 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1902 STATSTG* pstatstg, /* [out] */
1903 DWORD grfStatFlag) /* [in] */
1905 StorageImpl* const This = (StorageImpl*)iface;
1906 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1908 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1910 CoTaskMemFree(pstatstg->pwcsName);
1911 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1912 strcpyW(pstatstg->pwcsName, This->pwcsName);
1915 return result;
1918 /******************************************************************************
1919 * Internal stream list handlers
1922 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1924 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1925 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1928 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1930 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1931 list_remove(&(strm->StrmListEntry));
1934 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1936 struct list *cur, *cur2;
1937 StgStreamImpl *strm=NULL;
1939 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1940 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1941 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1942 strm->parentStorage = NULL;
1943 list_remove(cur);
1948 /*********************************************************************
1950 * Internal Method
1952 * Perform the deletion of a complete storage node
1955 static HRESULT deleteStorageProperty(
1956 StorageImpl *parentStorage,
1957 ULONG indexOfPropertyToDelete,
1958 StgProperty propertyToDelete)
1960 IEnumSTATSTG *elements = 0;
1961 IStorage *childStorage = 0;
1962 STATSTG currentElement;
1963 HRESULT hr;
1964 HRESULT destroyHr = S_OK;
1967 * Open the storage and enumerate it
1969 hr = StorageBaseImpl_OpenStorage(
1970 (IStorage*)parentStorage,
1971 propertyToDelete.name,
1973 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1976 &childStorage);
1978 if (hr != S_OK)
1980 return hr;
1984 * Enumerate the elements
1986 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1991 * Obtain the next element
1993 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1994 if (hr==S_OK)
1996 destroyHr = StorageImpl_DestroyElement(
1997 (IStorage*)childStorage,
1998 (OLECHAR*)currentElement.pwcsName);
2000 CoTaskMemFree(currentElement.pwcsName);
2004 * We need to Reset the enumeration every time because we delete elements
2005 * and the enumeration could be invalid
2007 IEnumSTATSTG_Reset(elements);
2009 } while ((hr == S_OK) && (destroyHr == S_OK));
2012 * Invalidate the property by zeroing its name member.
2014 propertyToDelete.sizeOfNameString = 0;
2016 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2017 indexOfPropertyToDelete,
2018 &propertyToDelete);
2020 IStorage_Release(childStorage);
2021 IEnumSTATSTG_Release(elements);
2023 return destroyHr;
2026 /*********************************************************************
2028 * Internal Method
2030 * Perform the deletion of a stream node
2033 static HRESULT deleteStreamProperty(
2034 StorageImpl *parentStorage,
2035 ULONG indexOfPropertyToDelete,
2036 StgProperty propertyToDelete)
2038 IStream *pis;
2039 HRESULT hr;
2040 ULARGE_INTEGER size;
2042 size.u.HighPart = 0;
2043 size.u.LowPart = 0;
2045 hr = StorageBaseImpl_OpenStream(
2046 (IStorage*)parentStorage,
2047 (OLECHAR*)propertyToDelete.name,
2048 NULL,
2049 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2051 &pis);
2053 if (hr!=S_OK)
2055 return(hr);
2059 * Zap the stream
2061 hr = IStream_SetSize(pis, size);
2063 if(hr != S_OK)
2065 return hr;
2069 * Release the stream object.
2071 IStream_Release(pis);
2074 * Invalidate the property by zeroing its name member.
2076 propertyToDelete.sizeOfNameString = 0;
2079 * Here we should re-read the property so we get the updated pointer
2080 * but since we are here to zap it, I don't do it...
2082 StorageImpl_WriteProperty(
2083 parentStorage->base.ancestorStorage,
2084 indexOfPropertyToDelete,
2085 &propertyToDelete);
2087 return S_OK;
2090 /*********************************************************************
2092 * Internal Method
2094 * Finds a placeholder for the StgProperty within the Storage
2097 static HRESULT findPlaceholder(
2098 StorageImpl *storage,
2099 ULONG propertyIndexToStore,
2100 ULONG storePropertyIndex,
2101 INT typeOfRelation)
2103 StgProperty storeProperty;
2104 HRESULT hr = S_OK;
2105 BOOL res = TRUE;
2108 * Read the storage property
2110 res = StorageImpl_ReadProperty(
2111 storage->base.ancestorStorage,
2112 storePropertyIndex,
2113 &storeProperty);
2115 if(! res)
2117 return E_FAIL;
2120 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2122 if (storeProperty.previousProperty != PROPERTY_NULL)
2124 return findPlaceholder(
2125 storage,
2126 propertyIndexToStore,
2127 storeProperty.previousProperty,
2128 typeOfRelation);
2130 else
2132 storeProperty.previousProperty = propertyIndexToStore;
2135 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2137 if (storeProperty.nextProperty != PROPERTY_NULL)
2139 return findPlaceholder(
2140 storage,
2141 propertyIndexToStore,
2142 storeProperty.nextProperty,
2143 typeOfRelation);
2145 else
2147 storeProperty.nextProperty = propertyIndexToStore;
2150 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2152 if (storeProperty.dirProperty != PROPERTY_NULL)
2154 return findPlaceholder(
2155 storage,
2156 propertyIndexToStore,
2157 storeProperty.dirProperty,
2158 typeOfRelation);
2160 else
2162 storeProperty.dirProperty = propertyIndexToStore;
2166 hr = StorageImpl_WriteProperty(
2167 storage->base.ancestorStorage,
2168 storePropertyIndex,
2169 &storeProperty);
2171 if(! hr)
2173 return E_FAIL;
2176 return S_OK;
2179 /*************************************************************************
2181 * Internal Method
2183 * This method takes the previous and the next property link of a property
2184 * to be deleted and find them a place in the Storage.
2186 static HRESULT adjustPropertyChain(
2187 StorageImpl *This,
2188 StgProperty propertyToDelete,
2189 StgProperty parentProperty,
2190 ULONG parentPropertyId,
2191 INT typeOfRelation)
2193 ULONG newLinkProperty = PROPERTY_NULL;
2194 BOOL needToFindAPlaceholder = FALSE;
2195 ULONG storeNode = PROPERTY_NULL;
2196 ULONG toStoreNode = PROPERTY_NULL;
2197 INT relationType = 0;
2198 HRESULT hr = S_OK;
2199 BOOL res = TRUE;
2201 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2203 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2206 * Set the parent previous to the property to delete previous
2208 newLinkProperty = propertyToDelete.previousProperty;
2210 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2213 * We also need to find a storage for the other link, setup variables
2214 * to do this at the end...
2216 needToFindAPlaceholder = TRUE;
2217 storeNode = propertyToDelete.previousProperty;
2218 toStoreNode = propertyToDelete.nextProperty;
2219 relationType = PROPERTY_RELATION_NEXT;
2222 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2225 * Set the parent previous to the property to delete next
2227 newLinkProperty = propertyToDelete.nextProperty;
2231 * Link it for real...
2233 parentProperty.previousProperty = newLinkProperty;
2236 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2238 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2241 * Set the parent next to the property to delete next previous
2243 newLinkProperty = propertyToDelete.previousProperty;
2245 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2248 * We also need to find a storage for the other link, setup variables
2249 * to do this at the end...
2251 needToFindAPlaceholder = TRUE;
2252 storeNode = propertyToDelete.previousProperty;
2253 toStoreNode = propertyToDelete.nextProperty;
2254 relationType = PROPERTY_RELATION_NEXT;
2257 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2260 * Set the parent next to the property to delete next
2262 newLinkProperty = propertyToDelete.nextProperty;
2266 * Link it for real...
2268 parentProperty.nextProperty = newLinkProperty;
2270 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2272 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2275 * Set the parent dir to the property to delete previous
2277 newLinkProperty = propertyToDelete.previousProperty;
2279 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2282 * We also need to find a storage for the other link, setup variables
2283 * to do this at the end...
2285 needToFindAPlaceholder = TRUE;
2286 storeNode = propertyToDelete.previousProperty;
2287 toStoreNode = propertyToDelete.nextProperty;
2288 relationType = PROPERTY_RELATION_NEXT;
2291 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2294 * Set the parent dir to the property to delete next
2296 newLinkProperty = propertyToDelete.nextProperty;
2300 * Link it for real...
2302 parentProperty.dirProperty = newLinkProperty;
2306 * Write back the parent property
2308 res = StorageImpl_WriteProperty(
2309 This->base.ancestorStorage,
2310 parentPropertyId,
2311 &parentProperty);
2312 if(! res)
2314 return E_FAIL;
2318 * If a placeholder is required for the other link, then, find one and
2319 * get out of here...
2321 if (needToFindAPlaceholder)
2323 hr = findPlaceholder(
2324 This,
2325 toStoreNode,
2326 storeNode,
2327 relationType);
2330 return hr;
2334 /******************************************************************************
2335 * SetElementTimes (IStorage)
2337 static HRESULT WINAPI StorageImpl_SetElementTimes(
2338 IStorage* iface,
2339 const OLECHAR *pwcsName,/* [string][in] */
2340 const FILETIME *pctime, /* [in] */
2341 const FILETIME *patime, /* [in] */
2342 const FILETIME *pmtime) /* [in] */
2344 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2345 return S_OK;
2348 /******************************************************************************
2349 * SetStateBits (IStorage)
2351 static HRESULT WINAPI StorageImpl_SetStateBits(
2352 IStorage* iface,
2353 DWORD grfStateBits,/* [in] */
2354 DWORD grfMask) /* [in] */
2356 StorageImpl* const This = (StorageImpl*)iface;
2357 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2358 return S_OK;
2362 * Virtual function table for the IStorage32Impl class.
2364 static const IStorageVtbl Storage32Impl_Vtbl =
2366 StorageBaseImpl_QueryInterface,
2367 StorageBaseImpl_AddRef,
2368 StorageBaseImpl_Release,
2369 StorageBaseImpl_CreateStream,
2370 StorageBaseImpl_OpenStream,
2371 StorageImpl_CreateStorage,
2372 StorageBaseImpl_OpenStorage,
2373 StorageImpl_CopyTo,
2374 StorageImpl_MoveElementTo,
2375 StorageImpl_Commit,
2376 StorageImpl_Revert,
2377 StorageBaseImpl_EnumElements,
2378 StorageImpl_DestroyElement,
2379 StorageBaseImpl_RenameElement,
2380 StorageImpl_SetElementTimes,
2381 StorageBaseImpl_SetClass,
2382 StorageImpl_SetStateBits,
2383 StorageImpl_Stat
2386 static HRESULT StorageImpl_Construct(
2387 StorageImpl* This,
2388 HANDLE hFile,
2389 LPCOLESTR pwcsName,
2390 ILockBytes* pLkbyt,
2391 DWORD openFlags,
2392 BOOL fileBased,
2393 BOOL fileCreate)
2395 HRESULT hr = S_OK;
2396 StgProperty currentProperty;
2397 BOOL readSuccessful;
2398 ULONG currentPropertyIndex;
2400 if ( FAILED( validateSTGM(openFlags) ))
2401 return STG_E_INVALIDFLAG;
2403 memset(This, 0, sizeof(StorageImpl));
2406 * Initialize stream list
2409 list_init(&This->base.strmHead);
2412 * Initialize the virtual function table.
2414 This->base.lpVtbl = &Storage32Impl_Vtbl;
2415 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2416 This->base.v_destructor = &StorageImpl_Destroy;
2417 This->base.openFlags = (openFlags & ~STGM_CREATE);
2420 * This is the top-level storage so initialize the ancestor pointer
2421 * to this.
2423 This->base.ancestorStorage = This;
2426 * Initialize the physical support of the storage.
2428 This->hFile = hFile;
2431 * Store copy of file path.
2433 if(pwcsName) {
2434 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2435 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2436 if (!This->pwcsName)
2437 return STG_E_INSUFFICIENTMEMORY;
2438 strcpyW(This->pwcsName, pwcsName);
2442 * Initialize the big block cache.
2444 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2445 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2446 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2447 pLkbyt,
2448 openFlags,
2449 This->bigBlockSize,
2450 fileBased);
2452 if (This->bigBlockFile == 0)
2453 return E_FAIL;
2455 if (fileCreate)
2457 ULARGE_INTEGER size;
2458 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2461 * Initialize all header variables:
2462 * - The big block depot consists of one block and it is at block 0
2463 * - The properties start at block 1
2464 * - There is no small block depot
2466 memset( This->bigBlockDepotStart,
2467 BLOCK_UNUSED,
2468 sizeof(This->bigBlockDepotStart));
2470 This->bigBlockDepotCount = 1;
2471 This->bigBlockDepotStart[0] = 0;
2472 This->rootStartBlock = 1;
2473 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2474 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2475 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2476 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2477 This->extBigBlockDepotCount = 0;
2479 StorageImpl_SaveFileHeader(This);
2482 * Add one block for the big block depot and one block for the properties
2484 size.u.HighPart = 0;
2485 size.u.LowPart = This->bigBlockSize * 3;
2486 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2489 * Initialize the big block depot
2491 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2492 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2493 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2494 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2496 else
2499 * Load the header for the file.
2501 hr = StorageImpl_LoadFileHeader(This);
2503 if (FAILED(hr))
2505 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2507 return hr;
2512 * There is no block depot cached yet.
2514 This->indexBlockDepotCached = 0xFFFFFFFF;
2517 * Start searching for free blocks with block 0.
2519 This->prevFreeBlock = 0;
2522 * Create the block chain abstractions.
2524 if(!(This->rootBlockChain =
2525 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2526 return STG_E_READFAULT;
2528 if(!(This->smallBlockDepotChain =
2529 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2530 PROPERTY_NULL)))
2531 return STG_E_READFAULT;
2534 * Write the root property (memory only)
2536 if (fileCreate)
2538 StgProperty rootProp;
2540 * Initialize the property chain
2542 memset(&rootProp, 0, sizeof(rootProp));
2543 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2544 sizeof(rootProp.name)/sizeof(WCHAR) );
2545 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2546 rootProp.propertyType = PROPTYPE_ROOT;
2547 rootProp.previousProperty = PROPERTY_NULL;
2548 rootProp.nextProperty = PROPERTY_NULL;
2549 rootProp.dirProperty = PROPERTY_NULL;
2550 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2551 rootProp.size.u.HighPart = 0;
2552 rootProp.size.u.LowPart = 0;
2554 StorageImpl_WriteProperty(This, 0, &rootProp);
2558 * Find the ID of the root in the property sets.
2560 currentPropertyIndex = 0;
2564 readSuccessful = StorageImpl_ReadProperty(
2565 This,
2566 currentPropertyIndex,
2567 &currentProperty);
2569 if (readSuccessful)
2571 if ( (currentProperty.sizeOfNameString != 0 ) &&
2572 (currentProperty.propertyType == PROPTYPE_ROOT) )
2574 This->base.rootPropertySetIndex = currentPropertyIndex;
2578 currentPropertyIndex++;
2580 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2582 if (!readSuccessful)
2584 /* TODO CLEANUP */
2585 return STG_E_READFAULT;
2589 * Create the block chain abstraction for the small block root chain.
2591 if(!(This->smallBlockRootChain =
2592 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2593 return STG_E_READFAULT;
2595 return hr;
2598 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2600 StorageImpl *This = (StorageImpl*) iface;
2601 TRACE("(%p)\n", This);
2603 StorageBaseImpl_DeleteAll(&This->base);
2605 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2607 BlockChainStream_Destroy(This->smallBlockRootChain);
2608 BlockChainStream_Destroy(This->rootBlockChain);
2609 BlockChainStream_Destroy(This->smallBlockDepotChain);
2611 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2612 HeapFree(GetProcessHeap(), 0, This);
2615 /******************************************************************************
2616 * Storage32Impl_GetNextFreeBigBlock
2618 * Returns the index of the next free big block.
2619 * If the big block depot is filled, this method will enlarge it.
2622 static ULONG StorageImpl_GetNextFreeBigBlock(
2623 StorageImpl* This)
2625 ULONG depotBlockIndexPos;
2626 BYTE depotBuffer[BIG_BLOCK_SIZE];
2627 BOOL success;
2628 ULONG depotBlockOffset;
2629 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2630 ULONG nextBlockIndex = BLOCK_SPECIAL;
2631 int depotIndex = 0;
2632 ULONG freeBlock = BLOCK_UNUSED;
2634 depotIndex = This->prevFreeBlock / blocksPerDepot;
2635 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2638 * Scan the entire big block depot until we find a block marked free
2640 while (nextBlockIndex != BLOCK_UNUSED)
2642 if (depotIndex < COUNT_BBDEPOTINHEADER)
2644 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2647 * Grow the primary depot.
2649 if (depotBlockIndexPos == BLOCK_UNUSED)
2651 depotBlockIndexPos = depotIndex*blocksPerDepot;
2654 * Add a block depot.
2656 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2657 This->bigBlockDepotCount++;
2658 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2661 * Flag it as a block depot.
2663 StorageImpl_SetNextBlockInChain(This,
2664 depotBlockIndexPos,
2665 BLOCK_SPECIAL);
2667 /* Save new header information.
2669 StorageImpl_SaveFileHeader(This);
2672 else
2674 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2676 if (depotBlockIndexPos == BLOCK_UNUSED)
2679 * Grow the extended depot.
2681 ULONG extIndex = BLOCK_UNUSED;
2682 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2683 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2685 if (extBlockOffset == 0)
2687 /* We need an extended block.
2689 extIndex = Storage32Impl_AddExtBlockDepot(This);
2690 This->extBigBlockDepotCount++;
2691 depotBlockIndexPos = extIndex + 1;
2693 else
2694 depotBlockIndexPos = depotIndex * blocksPerDepot;
2697 * Add a block depot and mark it in the extended block.
2699 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2700 This->bigBlockDepotCount++;
2701 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2703 /* Flag the block depot.
2705 StorageImpl_SetNextBlockInChain(This,
2706 depotBlockIndexPos,
2707 BLOCK_SPECIAL);
2709 /* If necessary, flag the extended depot block.
2711 if (extIndex != BLOCK_UNUSED)
2712 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2714 /* Save header information.
2716 StorageImpl_SaveFileHeader(This);
2720 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2722 if (success)
2724 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2725 ( nextBlockIndex != BLOCK_UNUSED))
2727 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2729 if (nextBlockIndex == BLOCK_UNUSED)
2731 freeBlock = (depotIndex * blocksPerDepot) +
2732 (depotBlockOffset/sizeof(ULONG));
2735 depotBlockOffset += sizeof(ULONG);
2739 depotIndex++;
2740 depotBlockOffset = 0;
2744 * make sure that the block physically exists before using it
2746 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2748 This->prevFreeBlock = freeBlock;
2750 return freeBlock;
2753 /******************************************************************************
2754 * Storage32Impl_AddBlockDepot
2756 * This will create a depot block, essentially it is a block initialized
2757 * to BLOCK_UNUSEDs.
2759 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2761 BYTE blockBuffer[BIG_BLOCK_SIZE];
2764 * Initialize blocks as free
2766 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2767 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2770 /******************************************************************************
2771 * Storage32Impl_GetExtDepotBlock
2773 * Returns the index of the block that corresponds to the specified depot
2774 * index. This method is only for depot indexes equal or greater than
2775 * COUNT_BBDEPOTINHEADER.
2777 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2779 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2780 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2781 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2782 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2783 ULONG blockIndex = BLOCK_UNUSED;
2784 ULONG extBlockIndex = This->extBigBlockDepotStart;
2786 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2788 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2789 return BLOCK_UNUSED;
2791 while (extBlockCount > 0)
2793 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2794 extBlockCount--;
2797 if (extBlockIndex != BLOCK_UNUSED)
2798 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2799 extBlockOffset * sizeof(ULONG), &blockIndex);
2801 return blockIndex;
2804 /******************************************************************************
2805 * Storage32Impl_SetExtDepotBlock
2807 * Associates the specified block index to the specified depot index.
2808 * This method is only for depot indexes equal or greater than
2809 * COUNT_BBDEPOTINHEADER.
2811 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2813 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2814 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2815 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2816 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2817 ULONG extBlockIndex = This->extBigBlockDepotStart;
2819 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2821 while (extBlockCount > 0)
2823 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2824 extBlockCount--;
2827 if (extBlockIndex != BLOCK_UNUSED)
2829 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2830 extBlockOffset * sizeof(ULONG),
2831 blockIndex);
2835 /******************************************************************************
2836 * Storage32Impl_AddExtBlockDepot
2838 * Creates an extended depot block.
2840 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2842 ULONG numExtBlocks = This->extBigBlockDepotCount;
2843 ULONG nextExtBlock = This->extBigBlockDepotStart;
2844 BYTE depotBuffer[BIG_BLOCK_SIZE];
2845 ULONG index = BLOCK_UNUSED;
2846 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2847 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2848 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2850 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2851 blocksPerDepotBlock;
2853 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2856 * The first extended block.
2858 This->extBigBlockDepotStart = index;
2860 else
2862 unsigned int i;
2864 * Follow the chain to the last one.
2866 for (i = 0; i < (numExtBlocks - 1); i++)
2868 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2872 * Add the new extended block to the chain.
2874 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2875 index);
2879 * Initialize this block.
2881 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2882 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2884 return index;
2887 /******************************************************************************
2888 * Storage32Impl_FreeBigBlock
2890 * This method will flag the specified block as free in the big block depot.
2892 static void StorageImpl_FreeBigBlock(
2893 StorageImpl* This,
2894 ULONG blockIndex)
2896 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2898 if (blockIndex < This->prevFreeBlock)
2899 This->prevFreeBlock = blockIndex;
2902 /************************************************************************
2903 * Storage32Impl_GetNextBlockInChain
2905 * This method will retrieve the block index of the next big block in
2906 * in the chain.
2908 * Params: This - Pointer to the Storage object.
2909 * blockIndex - Index of the block to retrieve the chain
2910 * for.
2911 * nextBlockIndex - receives the return value.
2913 * Returns: This method returns the index of the next block in the chain.
2914 * It will return the constants:
2915 * BLOCK_SPECIAL - If the block given was not part of a
2916 * chain.
2917 * BLOCK_END_OF_CHAIN - If the block given was the last in
2918 * a chain.
2919 * BLOCK_UNUSED - If the block given was not past of a chain
2920 * and is available.
2921 * BLOCK_EXTBBDEPOT - This block is part of the extended
2922 * big block depot.
2924 * See Windows documentation for more details on IStorage methods.
2926 static HRESULT StorageImpl_GetNextBlockInChain(
2927 StorageImpl* This,
2928 ULONG blockIndex,
2929 ULONG* nextBlockIndex)
2931 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2932 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2933 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2934 BYTE depotBuffer[BIG_BLOCK_SIZE];
2935 BOOL success;
2936 ULONG depotBlockIndexPos;
2937 int index;
2939 *nextBlockIndex = BLOCK_SPECIAL;
2941 if(depotBlockCount >= This->bigBlockDepotCount)
2943 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2944 This->bigBlockDepotCount);
2945 return STG_E_READFAULT;
2949 * Cache the currently accessed depot block.
2951 if (depotBlockCount != This->indexBlockDepotCached)
2953 This->indexBlockDepotCached = depotBlockCount;
2955 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2957 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2959 else
2962 * We have to look in the extended depot.
2964 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2967 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2969 if (!success)
2970 return STG_E_READFAULT;
2972 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2974 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2975 This->blockDepotCached[index] = *nextBlockIndex;
2979 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2981 return S_OK;
2984 /******************************************************************************
2985 * Storage32Impl_GetNextExtendedBlock
2987 * Given an extended block this method will return the next extended block.
2989 * NOTES:
2990 * The last ULONG of an extended block is the block index of the next
2991 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2992 * depot.
2994 * Return values:
2995 * - The index of the next extended block
2996 * - BLOCK_UNUSED: there is no next extended block.
2997 * - Any other return values denotes failure.
2999 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3001 ULONG nextBlockIndex = BLOCK_SPECIAL;
3002 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3004 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3005 &nextBlockIndex);
3007 return nextBlockIndex;
3010 /******************************************************************************
3011 * Storage32Impl_SetNextBlockInChain
3013 * This method will write the index of the specified block's next block
3014 * in the big block depot.
3016 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3017 * do the following
3019 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3020 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3021 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3024 static void StorageImpl_SetNextBlockInChain(
3025 StorageImpl* This,
3026 ULONG blockIndex,
3027 ULONG nextBlock)
3029 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3030 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3031 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3032 ULONG depotBlockIndexPos;
3034 assert(depotBlockCount < This->bigBlockDepotCount);
3035 assert(blockIndex != nextBlock);
3037 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3039 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3041 else
3044 * We have to look in the extended depot.
3046 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3049 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3050 nextBlock);
3052 * Update the cached block depot, if necessary.
3054 if (depotBlockCount == This->indexBlockDepotCached)
3056 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3060 /******************************************************************************
3061 * Storage32Impl_LoadFileHeader
3063 * This method will read in the file header, i.e. big block index -1.
3065 static HRESULT StorageImpl_LoadFileHeader(
3066 StorageImpl* This)
3068 HRESULT hr = STG_E_FILENOTFOUND;
3069 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3070 BOOL success;
3071 int index;
3073 TRACE("\n");
3075 * Get a pointer to the big block of data containing the header.
3077 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3080 * Extract the information from the header.
3082 if (success)
3085 * Check for the "magic number" signature and return an error if it is not
3086 * found.
3088 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3090 return STG_E_OLDFORMAT;
3093 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3095 return STG_E_INVALIDHEADER;
3098 StorageUtl_ReadWord(
3099 headerBigBlock,
3100 OFFSET_BIGBLOCKSIZEBITS,
3101 &This->bigBlockSizeBits);
3103 StorageUtl_ReadWord(
3104 headerBigBlock,
3105 OFFSET_SMALLBLOCKSIZEBITS,
3106 &This->smallBlockSizeBits);
3108 StorageUtl_ReadDWord(
3109 headerBigBlock,
3110 OFFSET_BBDEPOTCOUNT,
3111 &This->bigBlockDepotCount);
3113 StorageUtl_ReadDWord(
3114 headerBigBlock,
3115 OFFSET_ROOTSTARTBLOCK,
3116 &This->rootStartBlock);
3118 StorageUtl_ReadDWord(
3119 headerBigBlock,
3120 OFFSET_SBDEPOTSTART,
3121 &This->smallBlockDepotStart);
3123 StorageUtl_ReadDWord(
3124 headerBigBlock,
3125 OFFSET_EXTBBDEPOTSTART,
3126 &This->extBigBlockDepotStart);
3128 StorageUtl_ReadDWord(
3129 headerBigBlock,
3130 OFFSET_EXTBBDEPOTCOUNT,
3131 &This->extBigBlockDepotCount);
3133 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3135 StorageUtl_ReadDWord(
3136 headerBigBlock,
3137 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3138 &(This->bigBlockDepotStart[index]));
3142 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3144 if ((1 << 2) == 4)
3146 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3147 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3149 else
3151 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3152 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3156 * Right now, the code is making some assumptions about the size of the
3157 * blocks, just make sure they are what we're expecting.
3159 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3160 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3162 WARN("Broken OLE storage file\n");
3163 hr = STG_E_INVALIDHEADER;
3165 else
3166 hr = S_OK;
3169 return hr;
3172 /******************************************************************************
3173 * Storage32Impl_SaveFileHeader
3175 * This method will save to the file the header, i.e. big block -1.
3177 static void StorageImpl_SaveFileHeader(
3178 StorageImpl* This)
3180 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3181 int index;
3182 BOOL success;
3185 * Get a pointer to the big block of data containing the header.
3187 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3190 * If the block read failed, the file is probably new.
3192 if (!success)
3195 * Initialize for all unknown fields.
3197 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3200 * Initialize the magic number.
3202 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3205 * And a bunch of things we don't know what they mean
3207 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3208 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3209 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3210 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3214 * Write the information to the header.
3216 StorageUtl_WriteWord(
3217 headerBigBlock,
3218 OFFSET_BIGBLOCKSIZEBITS,
3219 This->bigBlockSizeBits);
3221 StorageUtl_WriteWord(
3222 headerBigBlock,
3223 OFFSET_SMALLBLOCKSIZEBITS,
3224 This->smallBlockSizeBits);
3226 StorageUtl_WriteDWord(
3227 headerBigBlock,
3228 OFFSET_BBDEPOTCOUNT,
3229 This->bigBlockDepotCount);
3231 StorageUtl_WriteDWord(
3232 headerBigBlock,
3233 OFFSET_ROOTSTARTBLOCK,
3234 This->rootStartBlock);
3236 StorageUtl_WriteDWord(
3237 headerBigBlock,
3238 OFFSET_SBDEPOTSTART,
3239 This->smallBlockDepotStart);
3241 StorageUtl_WriteDWord(
3242 headerBigBlock,
3243 OFFSET_SBDEPOTCOUNT,
3244 This->smallBlockDepotChain ?
3245 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3247 StorageUtl_WriteDWord(
3248 headerBigBlock,
3249 OFFSET_EXTBBDEPOTSTART,
3250 This->extBigBlockDepotStart);
3252 StorageUtl_WriteDWord(
3253 headerBigBlock,
3254 OFFSET_EXTBBDEPOTCOUNT,
3255 This->extBigBlockDepotCount);
3257 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3259 StorageUtl_WriteDWord(
3260 headerBigBlock,
3261 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3262 (This->bigBlockDepotStart[index]));
3266 * Write the big block back to the file.
3268 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3271 /******************************************************************************
3272 * Storage32Impl_ReadProperty
3274 * This method will read the specified property from the property chain.
3276 BOOL StorageImpl_ReadProperty(
3277 StorageImpl* This,
3278 ULONG index,
3279 StgProperty* buffer)
3281 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3282 ULARGE_INTEGER offsetInPropSet;
3283 HRESULT readRes;
3284 ULONG bytesRead;
3286 offsetInPropSet.u.HighPart = 0;
3287 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3289 readRes = BlockChainStream_ReadAt(
3290 This->rootBlockChain,
3291 offsetInPropSet,
3292 PROPSET_BLOCK_SIZE,
3293 currentProperty,
3294 &bytesRead);
3296 if (SUCCEEDED(readRes))
3298 /* replace the name of root entry (often "Root Entry") by the file name */
3299 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3300 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3302 memset(buffer->name, 0, sizeof(buffer->name));
3303 memcpy(
3304 buffer->name,
3305 propName,
3306 PROPERTY_NAME_BUFFER_LEN );
3307 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3309 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3311 StorageUtl_ReadWord(
3312 currentProperty,
3313 OFFSET_PS_NAMELENGTH,
3314 &buffer->sizeOfNameString);
3316 StorageUtl_ReadDWord(
3317 currentProperty,
3318 OFFSET_PS_PREVIOUSPROP,
3319 &buffer->previousProperty);
3321 StorageUtl_ReadDWord(
3322 currentProperty,
3323 OFFSET_PS_NEXTPROP,
3324 &buffer->nextProperty);
3326 StorageUtl_ReadDWord(
3327 currentProperty,
3328 OFFSET_PS_DIRPROP,
3329 &buffer->dirProperty);
3331 StorageUtl_ReadGUID(
3332 currentProperty,
3333 OFFSET_PS_GUID,
3334 &buffer->propertyUniqueID);
3336 StorageUtl_ReadDWord(
3337 currentProperty,
3338 OFFSET_PS_TSS1,
3339 &buffer->timeStampS1);
3341 StorageUtl_ReadDWord(
3342 currentProperty,
3343 OFFSET_PS_TSD1,
3344 &buffer->timeStampD1);
3346 StorageUtl_ReadDWord(
3347 currentProperty,
3348 OFFSET_PS_TSS2,
3349 &buffer->timeStampS2);
3351 StorageUtl_ReadDWord(
3352 currentProperty,
3353 OFFSET_PS_TSD2,
3354 &buffer->timeStampD2);
3356 StorageUtl_ReadDWord(
3357 currentProperty,
3358 OFFSET_PS_STARTBLOCK,
3359 &buffer->startingBlock);
3361 StorageUtl_ReadDWord(
3362 currentProperty,
3363 OFFSET_PS_SIZE,
3364 &buffer->size.u.LowPart);
3366 buffer->size.u.HighPart = 0;
3369 return SUCCEEDED(readRes) ? TRUE : FALSE;
3372 /*********************************************************************
3373 * Write the specified property into the property chain
3375 BOOL StorageImpl_WriteProperty(
3376 StorageImpl* This,
3377 ULONG index,
3378 const StgProperty* buffer)
3380 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3381 ULARGE_INTEGER offsetInPropSet;
3382 HRESULT writeRes;
3383 ULONG bytesWritten;
3385 offsetInPropSet.u.HighPart = 0;
3386 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3388 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3390 memcpy(
3391 currentProperty + OFFSET_PS_NAME,
3392 buffer->name,
3393 PROPERTY_NAME_BUFFER_LEN );
3395 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3397 StorageUtl_WriteWord(
3398 currentProperty,
3399 OFFSET_PS_NAMELENGTH,
3400 buffer->sizeOfNameString);
3402 StorageUtl_WriteDWord(
3403 currentProperty,
3404 OFFSET_PS_PREVIOUSPROP,
3405 buffer->previousProperty);
3407 StorageUtl_WriteDWord(
3408 currentProperty,
3409 OFFSET_PS_NEXTPROP,
3410 buffer->nextProperty);
3412 StorageUtl_WriteDWord(
3413 currentProperty,
3414 OFFSET_PS_DIRPROP,
3415 buffer->dirProperty);
3417 StorageUtl_WriteGUID(
3418 currentProperty,
3419 OFFSET_PS_GUID,
3420 &buffer->propertyUniqueID);
3422 StorageUtl_WriteDWord(
3423 currentProperty,
3424 OFFSET_PS_TSS1,
3425 buffer->timeStampS1);
3427 StorageUtl_WriteDWord(
3428 currentProperty,
3429 OFFSET_PS_TSD1,
3430 buffer->timeStampD1);
3432 StorageUtl_WriteDWord(
3433 currentProperty,
3434 OFFSET_PS_TSS2,
3435 buffer->timeStampS2);
3437 StorageUtl_WriteDWord(
3438 currentProperty,
3439 OFFSET_PS_TSD2,
3440 buffer->timeStampD2);
3442 StorageUtl_WriteDWord(
3443 currentProperty,
3444 OFFSET_PS_STARTBLOCK,
3445 buffer->startingBlock);
3447 StorageUtl_WriteDWord(
3448 currentProperty,
3449 OFFSET_PS_SIZE,
3450 buffer->size.u.LowPart);
3452 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3453 offsetInPropSet,
3454 PROPSET_BLOCK_SIZE,
3455 currentProperty,
3456 &bytesWritten);
3457 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3460 static BOOL StorageImpl_ReadBigBlock(
3461 StorageImpl* This,
3462 ULONG blockIndex,
3463 void* buffer)
3465 ULARGE_INTEGER ulOffset;
3466 DWORD read;
3468 ulOffset.u.HighPart = 0;
3469 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3471 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3472 return (read == This->bigBlockSize);
3475 static BOOL StorageImpl_ReadDWordFromBigBlock(
3476 StorageImpl* This,
3477 ULONG blockIndex,
3478 ULONG offset,
3479 DWORD* value)
3481 ULARGE_INTEGER ulOffset;
3482 DWORD read;
3483 DWORD tmp;
3485 ulOffset.u.HighPart = 0;
3486 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3487 ulOffset.u.LowPart += offset;
3489 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3490 *value = le32toh(tmp);
3491 return (read == sizeof(DWORD));
3494 static BOOL StorageImpl_WriteBigBlock(
3495 StorageImpl* This,
3496 ULONG blockIndex,
3497 const void* buffer)
3499 ULARGE_INTEGER ulOffset;
3500 DWORD wrote;
3502 ulOffset.u.HighPart = 0;
3503 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3505 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3506 return (wrote == This->bigBlockSize);
3509 static BOOL StorageImpl_WriteDWordToBigBlock(
3510 StorageImpl* This,
3511 ULONG blockIndex,
3512 ULONG offset,
3513 DWORD value)
3515 ULARGE_INTEGER ulOffset;
3516 DWORD wrote;
3518 ulOffset.u.HighPart = 0;
3519 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3520 ulOffset.u.LowPart += offset;
3522 value = htole32(value);
3523 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3524 return (wrote == sizeof(DWORD));
3527 /******************************************************************************
3528 * Storage32Impl_SmallBlocksToBigBlocks
3530 * This method will convert a small block chain to a big block chain.
3531 * The small block chain will be destroyed.
3533 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3534 StorageImpl* This,
3535 SmallBlockChainStream** ppsbChain)
3537 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3538 ULARGE_INTEGER size, offset;
3539 ULONG cbRead, cbWritten;
3540 ULARGE_INTEGER cbTotalRead;
3541 ULONG propertyIndex;
3542 HRESULT resWrite = S_OK;
3543 HRESULT resRead;
3544 StgProperty chainProperty;
3545 BYTE *buffer;
3546 BlockChainStream *bbTempChain = NULL;
3547 BlockChainStream *bigBlockChain = NULL;
3550 * Create a temporary big block chain that doesn't have
3551 * an associated property. This temporary chain will be
3552 * used to copy data from small blocks to big blocks.
3554 bbTempChain = BlockChainStream_Construct(This,
3555 &bbHeadOfChain,
3556 PROPERTY_NULL);
3557 if(!bbTempChain) return NULL;
3559 * Grow the big block chain.
3561 size = SmallBlockChainStream_GetSize(*ppsbChain);
3562 BlockChainStream_SetSize(bbTempChain, size);
3565 * Copy the contents of the small block chain to the big block chain
3566 * by small block size increments.
3568 offset.u.LowPart = 0;
3569 offset.u.HighPart = 0;
3570 cbTotalRead.QuadPart = 0;
3572 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3575 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3576 offset,
3577 This->smallBlockSize,
3578 buffer,
3579 &cbRead);
3580 if (FAILED(resRead))
3581 break;
3583 if (cbRead > 0)
3585 cbTotalRead.QuadPart += cbRead;
3587 resWrite = BlockChainStream_WriteAt(bbTempChain,
3588 offset,
3589 cbRead,
3590 buffer,
3591 &cbWritten);
3593 if (FAILED(resWrite))
3594 break;
3596 offset.u.LowPart += This->smallBlockSize;
3598 } while (cbTotalRead.QuadPart < size.QuadPart);
3599 HeapFree(GetProcessHeap(),0,buffer);
3601 if (FAILED(resRead) || FAILED(resWrite))
3603 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3604 BlockChainStream_Destroy(bbTempChain);
3605 return NULL;
3609 * Destroy the small block chain.
3611 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3612 size.u.HighPart = 0;
3613 size.u.LowPart = 0;
3614 SmallBlockChainStream_SetSize(*ppsbChain, size);
3615 SmallBlockChainStream_Destroy(*ppsbChain);
3616 *ppsbChain = 0;
3619 * Change the property information. This chain is now a big block chain
3620 * and it doesn't reside in the small blocks chain anymore.
3622 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3624 chainProperty.startingBlock = bbHeadOfChain;
3626 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3629 * Destroy the temporary propertyless big block chain.
3630 * Create a new big block chain associated with this property.
3632 BlockChainStream_Destroy(bbTempChain);
3633 bigBlockChain = BlockChainStream_Construct(This,
3634 NULL,
3635 propertyIndex);
3637 return bigBlockChain;
3640 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3642 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3644 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3645 HeapFree(GetProcessHeap(), 0, This);
3648 /******************************************************************************
3650 ** Storage32InternalImpl_Commit
3652 ** The non-root storages cannot be opened in transacted mode thus this function
3653 ** does nothing.
3655 static HRESULT WINAPI StorageInternalImpl_Commit(
3656 IStorage* iface,
3657 DWORD grfCommitFlags) /* [in] */
3659 return S_OK;
3662 /******************************************************************************
3664 ** Storage32InternalImpl_Revert
3666 ** The non-root storages cannot be opened in transacted mode thus this function
3667 ** does nothing.
3669 static HRESULT WINAPI StorageInternalImpl_Revert(
3670 IStorage* iface)
3672 return S_OK;
3675 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3677 IStorage_Release((IStorage*)This->parentStorage);
3678 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3679 HeapFree(GetProcessHeap(), 0, This);
3682 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3683 IEnumSTATSTG* iface,
3684 REFIID riid,
3685 void** ppvObject)
3687 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3690 * Perform a sanity check on the parameters.
3692 if (ppvObject==0)
3693 return E_INVALIDARG;
3696 * Initialize the return parameter.
3698 *ppvObject = 0;
3701 * Compare the riid with the interface IDs implemented by this object.
3703 if (IsEqualGUID(&IID_IUnknown, riid) ||
3704 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3706 *ppvObject = (IEnumSTATSTG*)This;
3707 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3708 return S_OK;
3711 return E_NOINTERFACE;
3714 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3715 IEnumSTATSTG* iface)
3717 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3718 return InterlockedIncrement(&This->ref);
3721 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3722 IEnumSTATSTG* iface)
3724 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3726 ULONG newRef;
3728 newRef = InterlockedDecrement(&This->ref);
3731 * If the reference count goes down to 0, perform suicide.
3733 if (newRef==0)
3735 IEnumSTATSTGImpl_Destroy(This);
3738 return newRef;
3741 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3742 IEnumSTATSTG* iface,
3743 ULONG celt,
3744 STATSTG* rgelt,
3745 ULONG* pceltFetched)
3747 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3749 StgProperty currentProperty;
3750 STATSTG* currentReturnStruct = rgelt;
3751 ULONG objectFetched = 0;
3752 ULONG currentSearchNode;
3755 * Perform a sanity check on the parameters.
3757 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3758 return E_INVALIDARG;
3761 * To avoid the special case, get another pointer to a ULONG value if
3762 * the caller didn't supply one.
3764 if (pceltFetched==0)
3765 pceltFetched = &objectFetched;
3768 * Start the iteration, we will iterate until we hit the end of the
3769 * linked list or until we hit the number of items to iterate through
3771 *pceltFetched = 0;
3774 * Start with the node at the top of the stack.
3776 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3778 while ( ( *pceltFetched < celt) &&
3779 ( currentSearchNode!=PROPERTY_NULL) )
3782 * Remove the top node from the stack
3784 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3787 * Read the property from the storage.
3789 StorageImpl_ReadProperty(This->parentStorage,
3790 currentSearchNode,
3791 &currentProperty);
3794 * Copy the information to the return buffer.
3796 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3797 &currentProperty,
3798 STATFLAG_DEFAULT);
3801 * Step to the next item in the iteration
3803 (*pceltFetched)++;
3804 currentReturnStruct++;
3807 * Push the next search node in the search stack.
3809 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3812 * continue the iteration.
3814 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3817 if (*pceltFetched == celt)
3818 return S_OK;
3820 return S_FALSE;
3824 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3825 IEnumSTATSTG* iface,
3826 ULONG celt)
3828 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3830 StgProperty currentProperty;
3831 ULONG objectFetched = 0;
3832 ULONG currentSearchNode;
3835 * Start with the node at the top of the stack.
3837 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3839 while ( (objectFetched < celt) &&
3840 (currentSearchNode!=PROPERTY_NULL) )
3843 * Remove the top node from the stack
3845 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3848 * Read the property from the storage.
3850 StorageImpl_ReadProperty(This->parentStorage,
3851 currentSearchNode,
3852 &currentProperty);
3855 * Step to the next item in the iteration
3857 objectFetched++;
3860 * Push the next search node in the search stack.
3862 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3865 * continue the iteration.
3867 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3870 if (objectFetched == celt)
3871 return S_OK;
3873 return S_FALSE;
3876 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3877 IEnumSTATSTG* iface)
3879 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3881 StgProperty rootProperty;
3882 BOOL readSuccessful;
3885 * Re-initialize the search stack to an empty stack
3887 This->stackSize = 0;
3890 * Read the root property from the storage.
3892 readSuccessful = StorageImpl_ReadProperty(
3893 This->parentStorage,
3894 This->firstPropertyNode,
3895 &rootProperty);
3897 if (readSuccessful)
3899 assert(rootProperty.sizeOfNameString!=0);
3902 * Push the search node in the search stack.
3904 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3907 return S_OK;
3910 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3911 IEnumSTATSTG* iface,
3912 IEnumSTATSTG** ppenum)
3914 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3916 IEnumSTATSTGImpl* newClone;
3919 * Perform a sanity check on the parameters.
3921 if (ppenum==0)
3922 return E_INVALIDARG;
3924 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3925 This->firstPropertyNode);
3929 * The new clone enumeration must point to the same current node as
3930 * the ole one.
3932 newClone->stackSize = This->stackSize ;
3933 newClone->stackMaxSize = This->stackMaxSize ;
3934 newClone->stackToVisit =
3935 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3937 memcpy(
3938 newClone->stackToVisit,
3939 This->stackToVisit,
3940 sizeof(ULONG) * newClone->stackSize);
3942 *ppenum = (IEnumSTATSTG*)newClone;
3945 * Don't forget to nail down a reference to the clone before
3946 * returning it.
3948 IEnumSTATSTGImpl_AddRef(*ppenum);
3950 return S_OK;
3953 static INT IEnumSTATSTGImpl_FindParentProperty(
3954 IEnumSTATSTGImpl *This,
3955 ULONG childProperty,
3956 StgProperty *currentProperty,
3957 ULONG *thisNodeId)
3959 ULONG currentSearchNode;
3960 ULONG foundNode;
3963 * To avoid the special case, get another pointer to a ULONG value if
3964 * the caller didn't supply one.
3966 if (thisNodeId==0)
3967 thisNodeId = &foundNode;
3970 * Start with the node at the top of the stack.
3972 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3975 while (currentSearchNode!=PROPERTY_NULL)
3978 * Store the current node in the returned parameters
3980 *thisNodeId = currentSearchNode;
3983 * Remove the top node from the stack
3985 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3988 * Read the property from the storage.
3990 StorageImpl_ReadProperty(
3991 This->parentStorage,
3992 currentSearchNode,
3993 currentProperty);
3995 if (currentProperty->previousProperty == childProperty)
3996 return PROPERTY_RELATION_PREVIOUS;
3998 else if (currentProperty->nextProperty == childProperty)
3999 return PROPERTY_RELATION_NEXT;
4001 else if (currentProperty->dirProperty == childProperty)
4002 return PROPERTY_RELATION_DIR;
4005 * Push the next search node in the search stack.
4007 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4010 * continue the iteration.
4012 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4015 return PROPERTY_NULL;
4018 static ULONG IEnumSTATSTGImpl_FindProperty(
4019 IEnumSTATSTGImpl* This,
4020 const OLECHAR* lpszPropName,
4021 StgProperty* currentProperty)
4023 ULONG currentSearchNode;
4026 * Start with the node at the top of the stack.
4028 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4030 while (currentSearchNode!=PROPERTY_NULL)
4033 * Remove the top node from the stack
4035 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4038 * Read the property from the storage.
4040 StorageImpl_ReadProperty(This->parentStorage,
4041 currentSearchNode,
4042 currentProperty);
4044 if ( propertyNameCmp(
4045 (const OLECHAR*)currentProperty->name,
4046 (const OLECHAR*)lpszPropName) == 0)
4047 return currentSearchNode;
4050 * Push the next search node in the search stack.
4052 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4055 * continue the iteration.
4057 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4060 return PROPERTY_NULL;
4063 static void IEnumSTATSTGImpl_PushSearchNode(
4064 IEnumSTATSTGImpl* This,
4065 ULONG nodeToPush)
4067 StgProperty rootProperty;
4068 BOOL readSuccessful;
4071 * First, make sure we're not trying to push an unexisting node.
4073 if (nodeToPush==PROPERTY_NULL)
4074 return;
4077 * First push the node to the stack
4079 if (This->stackSize == This->stackMaxSize)
4081 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4083 This->stackToVisit = HeapReAlloc(
4084 GetProcessHeap(),
4086 This->stackToVisit,
4087 sizeof(ULONG) * This->stackMaxSize);
4090 This->stackToVisit[This->stackSize] = nodeToPush;
4091 This->stackSize++;
4094 * Read the root property from the storage.
4096 readSuccessful = StorageImpl_ReadProperty(
4097 This->parentStorage,
4098 nodeToPush,
4099 &rootProperty);
4101 if (readSuccessful)
4103 assert(rootProperty.sizeOfNameString!=0);
4106 * Push the previous search node in the search stack.
4108 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4112 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4113 IEnumSTATSTGImpl* This,
4114 BOOL remove)
4116 ULONG topNode;
4118 if (This->stackSize == 0)
4119 return PROPERTY_NULL;
4121 topNode = This->stackToVisit[This->stackSize-1];
4123 if (remove)
4124 This->stackSize--;
4126 return topNode;
4130 * Virtual function table for the IEnumSTATSTGImpl class.
4132 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4134 IEnumSTATSTGImpl_QueryInterface,
4135 IEnumSTATSTGImpl_AddRef,
4136 IEnumSTATSTGImpl_Release,
4137 IEnumSTATSTGImpl_Next,
4138 IEnumSTATSTGImpl_Skip,
4139 IEnumSTATSTGImpl_Reset,
4140 IEnumSTATSTGImpl_Clone
4143 /******************************************************************************
4144 ** IEnumSTATSTGImpl implementation
4147 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4148 StorageImpl* parentStorage,
4149 ULONG firstPropertyNode)
4151 IEnumSTATSTGImpl* newEnumeration;
4153 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4155 if (newEnumeration!=0)
4158 * Set-up the virtual function table and reference count.
4160 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4161 newEnumeration->ref = 0;
4164 * We want to nail-down the reference to the storage in case the
4165 * enumeration out-lives the storage in the client application.
4167 newEnumeration->parentStorage = parentStorage;
4168 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4170 newEnumeration->firstPropertyNode = firstPropertyNode;
4173 * Initialize the search stack
4175 newEnumeration->stackSize = 0;
4176 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4177 newEnumeration->stackToVisit =
4178 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4181 * Make sure the current node of the iterator is the first one.
4183 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4186 return newEnumeration;
4190 * Virtual function table for the Storage32InternalImpl class.
4192 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4194 StorageBaseImpl_QueryInterface,
4195 StorageBaseImpl_AddRef,
4196 StorageBaseImpl_Release,
4197 StorageBaseImpl_CreateStream,
4198 StorageBaseImpl_OpenStream,
4199 StorageImpl_CreateStorage,
4200 StorageBaseImpl_OpenStorage,
4201 StorageImpl_CopyTo,
4202 StorageImpl_MoveElementTo,
4203 StorageInternalImpl_Commit,
4204 StorageInternalImpl_Revert,
4205 StorageBaseImpl_EnumElements,
4206 StorageImpl_DestroyElement,
4207 StorageBaseImpl_RenameElement,
4208 StorageImpl_SetElementTimes,
4209 StorageBaseImpl_SetClass,
4210 StorageImpl_SetStateBits,
4211 StorageBaseImpl_Stat
4214 /******************************************************************************
4215 ** Storage32InternalImpl implementation
4218 static StorageInternalImpl* StorageInternalImpl_Construct(
4219 StorageImpl* ancestorStorage,
4220 DWORD openFlags,
4221 ULONG rootPropertyIndex)
4223 StorageInternalImpl* newStorage;
4226 * Allocate space for the new storage object
4228 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4230 if (newStorage!=0)
4233 * Initialize the stream list
4235 list_init(&newStorage->base.strmHead);
4238 * Initialize the virtual function table.
4240 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4241 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4242 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4245 * Keep the ancestor storage pointer and nail a reference to it.
4247 newStorage->base.ancestorStorage = ancestorStorage;
4248 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4251 * Keep the index of the root property set for this storage,
4253 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4255 return newStorage;
4258 return 0;
4261 /******************************************************************************
4262 ** StorageUtl implementation
4265 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4267 WORD tmp;
4269 memcpy(&tmp, buffer+offset, sizeof(WORD));
4270 *value = le16toh(tmp);
4273 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4275 value = htole16(value);
4276 memcpy(buffer+offset, &value, sizeof(WORD));
4279 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4281 DWORD tmp;
4283 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4284 *value = le32toh(tmp);
4287 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4289 value = htole32(value);
4290 memcpy(buffer+offset, &value, sizeof(DWORD));
4293 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4294 ULARGE_INTEGER* value)
4296 #ifdef WORDS_BIGENDIAN
4297 ULARGE_INTEGER tmp;
4299 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4300 value->u.LowPart = htole32(tmp.u.HighPart);
4301 value->u.HighPart = htole32(tmp.u.LowPart);
4302 #else
4303 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4304 #endif
4307 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4308 const ULARGE_INTEGER *value)
4310 #ifdef WORDS_BIGENDIAN
4311 ULARGE_INTEGER tmp;
4313 tmp.u.LowPart = htole32(value->u.HighPart);
4314 tmp.u.HighPart = htole32(value->u.LowPart);
4315 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4316 #else
4317 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4318 #endif
4321 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4323 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4324 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4325 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4327 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4330 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4332 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4333 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4334 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4336 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4339 void StorageUtl_CopyPropertyToSTATSTG(
4340 STATSTG* destination,
4341 const StgProperty* source,
4342 int statFlags)
4345 * The copy of the string occurs only when the flag is not set
4347 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4348 (source->name == NULL) ||
4349 (source->name[0] == 0) )
4351 destination->pwcsName = 0;
4353 else
4355 destination->pwcsName =
4356 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4358 strcpyW((LPWSTR)destination->pwcsName, source->name);
4361 switch (source->propertyType)
4363 case PROPTYPE_STORAGE:
4364 case PROPTYPE_ROOT:
4365 destination->type = STGTY_STORAGE;
4366 break;
4367 case PROPTYPE_STREAM:
4368 destination->type = STGTY_STREAM;
4369 break;
4370 default:
4371 destination->type = STGTY_STREAM;
4372 break;
4375 destination->cbSize = source->size;
4377 currentReturnStruct->mtime = {0}; TODO
4378 currentReturnStruct->ctime = {0};
4379 currentReturnStruct->atime = {0};
4381 destination->grfMode = 0;
4382 destination->grfLocksSupported = 0;
4383 destination->clsid = source->propertyUniqueID;
4384 destination->grfStateBits = 0;
4385 destination->reserved = 0;
4388 /******************************************************************************
4389 ** BlockChainStream implementation
4392 BlockChainStream* BlockChainStream_Construct(
4393 StorageImpl* parentStorage,
4394 ULONG* headOfStreamPlaceHolder,
4395 ULONG propertyIndex)
4397 BlockChainStream* newStream;
4398 ULONG blockIndex;
4400 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4402 newStream->parentStorage = parentStorage;
4403 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4404 newStream->ownerPropertyIndex = propertyIndex;
4405 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4406 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4407 newStream->numBlocks = 0;
4409 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4411 while (blockIndex != BLOCK_END_OF_CHAIN)
4413 newStream->numBlocks++;
4414 newStream->tailIndex = blockIndex;
4416 if(FAILED(StorageImpl_GetNextBlockInChain(
4417 parentStorage,
4418 blockIndex,
4419 &blockIndex)))
4421 HeapFree(GetProcessHeap(), 0, newStream);
4422 return NULL;
4426 return newStream;
4429 void BlockChainStream_Destroy(BlockChainStream* This)
4431 HeapFree(GetProcessHeap(), 0, This);
4434 /******************************************************************************
4435 * BlockChainStream_GetHeadOfChain
4437 * Returns the head of this stream chain.
4438 * Some special chains don't have properties, their heads are kept in
4439 * This->headOfStreamPlaceHolder.
4442 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4444 StgProperty chainProperty;
4445 BOOL readSuccessful;
4447 if (This->headOfStreamPlaceHolder != 0)
4448 return *(This->headOfStreamPlaceHolder);
4450 if (This->ownerPropertyIndex != PROPERTY_NULL)
4452 readSuccessful = StorageImpl_ReadProperty(
4453 This->parentStorage,
4454 This->ownerPropertyIndex,
4455 &chainProperty);
4457 if (readSuccessful)
4459 return chainProperty.startingBlock;
4463 return BLOCK_END_OF_CHAIN;
4466 /******************************************************************************
4467 * BlockChainStream_GetCount
4469 * Returns the number of blocks that comprises this chain.
4470 * This is not the size of the stream as the last block may not be full!
4473 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4475 ULONG blockIndex;
4476 ULONG count = 0;
4478 blockIndex = BlockChainStream_GetHeadOfChain(This);
4480 while (blockIndex != BLOCK_END_OF_CHAIN)
4482 count++;
4484 if(FAILED(StorageImpl_GetNextBlockInChain(
4485 This->parentStorage,
4486 blockIndex,
4487 &blockIndex)))
4488 return 0;
4491 return count;
4494 /******************************************************************************
4495 * BlockChainStream_ReadAt
4497 * Reads a specified number of bytes from this chain at the specified offset.
4498 * bytesRead may be NULL.
4499 * Failure will be returned if the specified number of bytes has not been read.
4501 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4502 ULARGE_INTEGER offset,
4503 ULONG size,
4504 void* buffer,
4505 ULONG* bytesRead)
4507 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4508 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4509 ULONG bytesToReadInBuffer;
4510 ULONG blockIndex;
4511 BYTE* bufferWalker;
4513 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4516 * Find the first block in the stream that contains part of the buffer.
4518 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4519 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4520 (blockNoInSequence < This->lastBlockNoInSequence) )
4522 blockIndex = BlockChainStream_GetHeadOfChain(This);
4523 This->lastBlockNoInSequence = blockNoInSequence;
4525 else
4527 ULONG temp = blockNoInSequence;
4529 blockIndex = This->lastBlockNoInSequenceIndex;
4530 blockNoInSequence -= This->lastBlockNoInSequence;
4531 This->lastBlockNoInSequence = temp;
4534 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4536 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4537 return STG_E_DOCFILECORRUPT;
4538 blockNoInSequence--;
4541 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4542 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4544 This->lastBlockNoInSequenceIndex = blockIndex;
4547 * Start reading the buffer.
4549 *bytesRead = 0;
4550 bufferWalker = buffer;
4552 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4554 ULARGE_INTEGER ulOffset;
4555 DWORD bytesReadAt;
4557 * Calculate how many bytes we can copy from this big block.
4559 bytesToReadInBuffer =
4560 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4562 TRACE("block %i\n",blockIndex);
4563 ulOffset.u.HighPart = 0;
4564 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4565 offsetInBlock;
4567 StorageImpl_ReadAt(This->parentStorage,
4568 ulOffset,
4569 bufferWalker,
4570 bytesToReadInBuffer,
4571 &bytesReadAt);
4573 * Step to the next big block.
4575 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4576 return STG_E_DOCFILECORRUPT;
4578 bufferWalker += bytesReadAt;
4579 size -= bytesReadAt;
4580 *bytesRead += bytesReadAt;
4581 offsetInBlock = 0; /* There is no offset on the next block */
4583 if (bytesToReadInBuffer != bytesReadAt)
4584 break;
4587 return (size == 0) ? S_OK : STG_E_READFAULT;
4590 /******************************************************************************
4591 * BlockChainStream_WriteAt
4593 * Writes the specified number of bytes to this chain at the specified offset.
4594 * bytesWritten may be NULL.
4595 * Will fail if not all specified number of bytes have been written.
4597 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4598 ULARGE_INTEGER offset,
4599 ULONG size,
4600 const void* buffer,
4601 ULONG* bytesWritten)
4603 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4604 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4605 ULONG bytesToWrite;
4606 ULONG blockIndex;
4607 const BYTE* bufferWalker;
4610 * Find the first block in the stream that contains part of the buffer.
4612 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4613 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4614 (blockNoInSequence < This->lastBlockNoInSequence) )
4616 blockIndex = BlockChainStream_GetHeadOfChain(This);
4617 This->lastBlockNoInSequence = blockNoInSequence;
4619 else
4621 ULONG temp = blockNoInSequence;
4623 blockIndex = This->lastBlockNoInSequenceIndex;
4624 blockNoInSequence -= This->lastBlockNoInSequence;
4625 This->lastBlockNoInSequence = temp;
4628 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4630 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4631 &blockIndex)))
4632 return STG_E_DOCFILECORRUPT;
4633 blockNoInSequence--;
4636 This->lastBlockNoInSequenceIndex = blockIndex;
4638 /* BlockChainStream_SetSize should have already been called to ensure we have
4639 * enough blocks in the chain to write into */
4640 if (blockIndex == BLOCK_END_OF_CHAIN)
4642 ERR("not enough blocks in chain to write data\n");
4643 return STG_E_DOCFILECORRUPT;
4646 *bytesWritten = 0;
4647 bufferWalker = (const BYTE*)buffer;
4649 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4651 ULARGE_INTEGER ulOffset;
4652 DWORD bytesWrittenAt;
4654 * Calculate how many bytes we can copy from this big block.
4656 bytesToWrite =
4657 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4659 TRACE("block %i\n",blockIndex);
4660 ulOffset.u.HighPart = 0;
4661 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4662 offsetInBlock;
4664 StorageImpl_WriteAt(This->parentStorage,
4665 ulOffset,
4666 bufferWalker,
4667 bytesToWrite,
4668 &bytesWrittenAt);
4671 * Step to the next big block.
4673 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4674 &blockIndex)))
4675 return STG_E_DOCFILECORRUPT;
4677 bufferWalker += bytesWrittenAt;
4678 size -= bytesWrittenAt;
4679 *bytesWritten += bytesWrittenAt;
4680 offsetInBlock = 0; /* There is no offset on the next block */
4682 if (bytesWrittenAt != bytesToWrite)
4683 break;
4686 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4689 /******************************************************************************
4690 * BlockChainStream_Shrink
4692 * Shrinks this chain in the big block depot.
4694 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4695 ULARGE_INTEGER newSize)
4697 ULONG blockIndex, extraBlock;
4698 ULONG numBlocks;
4699 ULONG count = 1;
4702 * Reset the last accessed block cache.
4704 This->lastBlockNoInSequence = 0xFFFFFFFF;
4705 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4708 * Figure out how many blocks are needed to contain the new size
4710 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4712 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4713 numBlocks++;
4715 blockIndex = BlockChainStream_GetHeadOfChain(This);
4718 * Go to the new end of chain
4720 while (count < numBlocks)
4722 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4723 &blockIndex)))
4724 return FALSE;
4725 count++;
4728 /* Get the next block before marking the new end */
4729 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4730 &extraBlock)))
4731 return FALSE;
4733 /* Mark the new end of chain */
4734 StorageImpl_SetNextBlockInChain(
4735 This->parentStorage,
4736 blockIndex,
4737 BLOCK_END_OF_CHAIN);
4739 This->tailIndex = blockIndex;
4740 This->numBlocks = numBlocks;
4743 * Mark the extra blocks as free
4745 while (extraBlock != BLOCK_END_OF_CHAIN)
4747 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4748 &blockIndex)))
4749 return FALSE;
4750 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4751 extraBlock = blockIndex;
4754 return TRUE;
4757 /******************************************************************************
4758 * BlockChainStream_Enlarge
4760 * Grows this chain in the big block depot.
4762 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4763 ULARGE_INTEGER newSize)
4765 ULONG blockIndex, currentBlock;
4766 ULONG newNumBlocks;
4767 ULONG oldNumBlocks = 0;
4769 blockIndex = BlockChainStream_GetHeadOfChain(This);
4772 * Empty chain. Create the head.
4774 if (blockIndex == BLOCK_END_OF_CHAIN)
4776 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4777 StorageImpl_SetNextBlockInChain(This->parentStorage,
4778 blockIndex,
4779 BLOCK_END_OF_CHAIN);
4781 if (This->headOfStreamPlaceHolder != 0)
4783 *(This->headOfStreamPlaceHolder) = blockIndex;
4785 else
4787 StgProperty chainProp;
4788 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4790 StorageImpl_ReadProperty(
4791 This->parentStorage,
4792 This->ownerPropertyIndex,
4793 &chainProp);
4795 chainProp.startingBlock = blockIndex;
4797 StorageImpl_WriteProperty(
4798 This->parentStorage,
4799 This->ownerPropertyIndex,
4800 &chainProp);
4803 This->tailIndex = blockIndex;
4804 This->numBlocks = 1;
4808 * Figure out how many blocks are needed to contain this stream
4810 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4812 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4813 newNumBlocks++;
4816 * Go to the current end of chain
4818 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4820 currentBlock = blockIndex;
4822 while (blockIndex != BLOCK_END_OF_CHAIN)
4824 This->numBlocks++;
4825 currentBlock = blockIndex;
4827 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4828 &blockIndex)))
4829 return FALSE;
4832 This->tailIndex = currentBlock;
4835 currentBlock = This->tailIndex;
4836 oldNumBlocks = This->numBlocks;
4839 * Add new blocks to the chain
4841 if (oldNumBlocks < newNumBlocks)
4843 while (oldNumBlocks < newNumBlocks)
4845 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4847 StorageImpl_SetNextBlockInChain(
4848 This->parentStorage,
4849 currentBlock,
4850 blockIndex);
4852 StorageImpl_SetNextBlockInChain(
4853 This->parentStorage,
4854 blockIndex,
4855 BLOCK_END_OF_CHAIN);
4857 currentBlock = blockIndex;
4858 oldNumBlocks++;
4861 This->tailIndex = blockIndex;
4862 This->numBlocks = newNumBlocks;
4865 return TRUE;
4868 /******************************************************************************
4869 * BlockChainStream_SetSize
4871 * Sets the size of this stream. The big block depot will be updated.
4872 * The file will grow if we grow the chain.
4874 * TODO: Free the actual blocks in the file when we shrink the chain.
4875 * Currently, the blocks are still in the file. So the file size
4876 * doesn't shrink even if we shrink streams.
4878 BOOL BlockChainStream_SetSize(
4879 BlockChainStream* This,
4880 ULARGE_INTEGER newSize)
4882 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4884 if (newSize.u.LowPart == size.u.LowPart)
4885 return TRUE;
4887 if (newSize.u.LowPart < size.u.LowPart)
4889 BlockChainStream_Shrink(This, newSize);
4891 else
4893 BlockChainStream_Enlarge(This, newSize);
4896 return TRUE;
4899 /******************************************************************************
4900 * BlockChainStream_GetSize
4902 * Returns the size of this chain.
4903 * Will return the block count if this chain doesn't have a property.
4905 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4907 StgProperty chainProperty;
4909 if(This->headOfStreamPlaceHolder == NULL)
4912 * This chain is a data stream read the property and return
4913 * the appropriate size
4915 StorageImpl_ReadProperty(
4916 This->parentStorage,
4917 This->ownerPropertyIndex,
4918 &chainProperty);
4920 return chainProperty.size;
4922 else
4925 * this chain is a chain that does not have a property, figure out the
4926 * size by making the product number of used blocks times the
4927 * size of them
4929 ULARGE_INTEGER result;
4930 result.u.HighPart = 0;
4932 result.u.LowPart =
4933 BlockChainStream_GetCount(This) *
4934 This->parentStorage->bigBlockSize;
4936 return result;
4940 /******************************************************************************
4941 ** SmallBlockChainStream implementation
4944 SmallBlockChainStream* SmallBlockChainStream_Construct(
4945 StorageImpl* parentStorage,
4946 ULONG propertyIndex)
4948 SmallBlockChainStream* newStream;
4950 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4952 newStream->parentStorage = parentStorage;
4953 newStream->ownerPropertyIndex = propertyIndex;
4955 return newStream;
4958 void SmallBlockChainStream_Destroy(
4959 SmallBlockChainStream* This)
4961 HeapFree(GetProcessHeap(), 0, This);
4964 /******************************************************************************
4965 * SmallBlockChainStream_GetHeadOfChain
4967 * Returns the head of this chain of small blocks.
4969 static ULONG SmallBlockChainStream_GetHeadOfChain(
4970 SmallBlockChainStream* This)
4972 StgProperty chainProperty;
4973 BOOL readSuccessful;
4975 if (This->ownerPropertyIndex)
4977 readSuccessful = StorageImpl_ReadProperty(
4978 This->parentStorage,
4979 This->ownerPropertyIndex,
4980 &chainProperty);
4982 if (readSuccessful)
4984 return chainProperty.startingBlock;
4989 return BLOCK_END_OF_CHAIN;
4992 /******************************************************************************
4993 * SmallBlockChainStream_GetNextBlockInChain
4995 * Returns the index of the next small block in this chain.
4997 * Return Values:
4998 * - BLOCK_END_OF_CHAIN: end of this chain
4999 * - BLOCK_UNUSED: small block 'blockIndex' is free
5001 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5002 SmallBlockChainStream* This,
5003 ULONG blockIndex,
5004 ULONG* nextBlockInChain)
5006 ULARGE_INTEGER offsetOfBlockInDepot;
5007 DWORD buffer;
5008 ULONG bytesRead;
5009 HRESULT res;
5011 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5013 offsetOfBlockInDepot.u.HighPart = 0;
5014 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5017 * Read those bytes in the buffer from the small block file.
5019 res = BlockChainStream_ReadAt(
5020 This->parentStorage->smallBlockDepotChain,
5021 offsetOfBlockInDepot,
5022 sizeof(DWORD),
5023 &buffer,
5024 &bytesRead);
5026 if (SUCCEEDED(res))
5028 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5029 return S_OK;
5032 return res;
5035 /******************************************************************************
5036 * SmallBlockChainStream_SetNextBlockInChain
5038 * Writes the index of the next block of the specified block in the small
5039 * block depot.
5040 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5041 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5043 static void SmallBlockChainStream_SetNextBlockInChain(
5044 SmallBlockChainStream* This,
5045 ULONG blockIndex,
5046 ULONG nextBlock)
5048 ULARGE_INTEGER offsetOfBlockInDepot;
5049 DWORD buffer;
5050 ULONG bytesWritten;
5052 offsetOfBlockInDepot.u.HighPart = 0;
5053 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5055 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5058 * Read those bytes in the buffer from the small block file.
5060 BlockChainStream_WriteAt(
5061 This->parentStorage->smallBlockDepotChain,
5062 offsetOfBlockInDepot,
5063 sizeof(DWORD),
5064 &buffer,
5065 &bytesWritten);
5068 /******************************************************************************
5069 * SmallBlockChainStream_FreeBlock
5071 * Flag small block 'blockIndex' as free in the small block depot.
5073 static void SmallBlockChainStream_FreeBlock(
5074 SmallBlockChainStream* This,
5075 ULONG blockIndex)
5077 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5080 /******************************************************************************
5081 * SmallBlockChainStream_GetNextFreeBlock
5083 * Returns the index of a free small block. The small block depot will be
5084 * enlarged if necessary. The small block chain will also be enlarged if
5085 * necessary.
5087 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5088 SmallBlockChainStream* This)
5090 ULARGE_INTEGER offsetOfBlockInDepot;
5091 DWORD buffer;
5092 ULONG bytesRead;
5093 ULONG blockIndex = 0;
5094 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5095 HRESULT res = S_OK;
5096 ULONG smallBlocksPerBigBlock;
5098 offsetOfBlockInDepot.u.HighPart = 0;
5101 * Scan the small block depot for a free block
5103 while (nextBlockIndex != BLOCK_UNUSED)
5105 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5107 res = BlockChainStream_ReadAt(
5108 This->parentStorage->smallBlockDepotChain,
5109 offsetOfBlockInDepot,
5110 sizeof(DWORD),
5111 &buffer,
5112 &bytesRead);
5115 * If we run out of space for the small block depot, enlarge it
5117 if (SUCCEEDED(res))
5119 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5121 if (nextBlockIndex != BLOCK_UNUSED)
5122 blockIndex++;
5124 else
5126 ULONG count =
5127 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5129 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5130 ULONG nextBlock, newsbdIndex;
5131 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5133 nextBlock = sbdIndex;
5134 while (nextBlock != BLOCK_END_OF_CHAIN)
5136 sbdIndex = nextBlock;
5137 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5140 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5141 if (sbdIndex != BLOCK_END_OF_CHAIN)
5142 StorageImpl_SetNextBlockInChain(
5143 This->parentStorage,
5144 sbdIndex,
5145 newsbdIndex);
5147 StorageImpl_SetNextBlockInChain(
5148 This->parentStorage,
5149 newsbdIndex,
5150 BLOCK_END_OF_CHAIN);
5153 * Initialize all the small blocks to free
5155 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5156 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5158 if (count == 0)
5161 * We have just created the small block depot.
5163 StgProperty rootProp;
5164 ULONG sbStartIndex;
5167 * Save it in the header
5169 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5170 StorageImpl_SaveFileHeader(This->parentStorage);
5173 * And allocate the first big block that will contain small blocks
5175 sbStartIndex =
5176 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5178 StorageImpl_SetNextBlockInChain(
5179 This->parentStorage,
5180 sbStartIndex,
5181 BLOCK_END_OF_CHAIN);
5183 StorageImpl_ReadProperty(
5184 This->parentStorage,
5185 This->parentStorage->base.rootPropertySetIndex,
5186 &rootProp);
5188 rootProp.startingBlock = sbStartIndex;
5189 rootProp.size.u.HighPart = 0;
5190 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5192 StorageImpl_WriteProperty(
5193 This->parentStorage,
5194 This->parentStorage->base.rootPropertySetIndex,
5195 &rootProp);
5200 smallBlocksPerBigBlock =
5201 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5204 * Verify if we have to allocate big blocks to contain small blocks
5206 if (blockIndex % smallBlocksPerBigBlock == 0)
5208 StgProperty rootProp;
5209 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5211 StorageImpl_ReadProperty(
5212 This->parentStorage,
5213 This->parentStorage->base.rootPropertySetIndex,
5214 &rootProp);
5216 if (rootProp.size.u.LowPart <
5217 (blocksRequired * This->parentStorage->bigBlockSize))
5219 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5221 BlockChainStream_SetSize(
5222 This->parentStorage->smallBlockRootChain,
5223 rootProp.size);
5225 StorageImpl_WriteProperty(
5226 This->parentStorage,
5227 This->parentStorage->base.rootPropertySetIndex,
5228 &rootProp);
5232 return blockIndex;
5235 /******************************************************************************
5236 * SmallBlockChainStream_ReadAt
5238 * Reads a specified number of bytes from this chain at the specified offset.
5239 * bytesRead may be NULL.
5240 * Failure will be returned if the specified number of bytes has not been read.
5242 HRESULT SmallBlockChainStream_ReadAt(
5243 SmallBlockChainStream* This,
5244 ULARGE_INTEGER offset,
5245 ULONG size,
5246 void* buffer,
5247 ULONG* bytesRead)
5249 HRESULT rc = S_OK;
5250 ULARGE_INTEGER offsetInBigBlockFile;
5251 ULONG blockNoInSequence =
5252 offset.u.LowPart / This->parentStorage->smallBlockSize;
5254 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5255 ULONG bytesToReadInBuffer;
5256 ULONG blockIndex;
5257 ULONG bytesReadFromBigBlockFile;
5258 BYTE* bufferWalker;
5261 * This should never happen on a small block file.
5263 assert(offset.u.HighPart==0);
5266 * Find the first block in the stream that contains part of the buffer.
5268 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5270 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5272 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5273 if(FAILED(rc))
5274 return rc;
5275 blockNoInSequence--;
5279 * Start reading the buffer.
5281 *bytesRead = 0;
5282 bufferWalker = buffer;
5284 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5287 * Calculate how many bytes we can copy from this small block.
5289 bytesToReadInBuffer =
5290 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5293 * Calculate the offset of the small block in the small block file.
5295 offsetInBigBlockFile.u.HighPart = 0;
5296 offsetInBigBlockFile.u.LowPart =
5297 blockIndex * This->parentStorage->smallBlockSize;
5299 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5302 * Read those bytes in the buffer from the small block file.
5303 * The small block has already been identified so it shouldn't fail
5304 * unless the file is corrupt.
5306 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5307 offsetInBigBlockFile,
5308 bytesToReadInBuffer,
5309 bufferWalker,
5310 &bytesReadFromBigBlockFile);
5312 if (FAILED(rc))
5313 return rc;
5316 * Step to the next big block.
5318 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5319 if(FAILED(rc))
5320 return STG_E_DOCFILECORRUPT;
5322 bufferWalker += bytesReadFromBigBlockFile;
5323 size -= bytesReadFromBigBlockFile;
5324 *bytesRead += bytesReadFromBigBlockFile;
5325 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5328 return (size == 0) ? S_OK : STG_E_READFAULT;
5331 /******************************************************************************
5332 * SmallBlockChainStream_WriteAt
5334 * Writes the specified number of bytes to this chain at the specified offset.
5335 * bytesWritten may be NULL.
5336 * Will fail if not all specified number of bytes have been written.
5338 HRESULT SmallBlockChainStream_WriteAt(
5339 SmallBlockChainStream* This,
5340 ULARGE_INTEGER offset,
5341 ULONG size,
5342 const void* buffer,
5343 ULONG* bytesWritten)
5345 ULARGE_INTEGER offsetInBigBlockFile;
5346 ULONG blockNoInSequence =
5347 offset.u.LowPart / This->parentStorage->smallBlockSize;
5349 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5350 ULONG bytesToWriteInBuffer;
5351 ULONG blockIndex;
5352 ULONG bytesWrittenToBigBlockFile;
5353 const BYTE* bufferWalker;
5354 HRESULT res;
5357 * This should never happen on a small block file.
5359 assert(offset.u.HighPart==0);
5362 * Find the first block in the stream that contains part of the buffer.
5364 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5366 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5368 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5369 return STG_E_DOCFILECORRUPT;
5370 blockNoInSequence--;
5374 * Start writing the buffer.
5376 * Here, I'm casting away the constness on the buffer variable
5377 * This is OK since we don't intend to modify that buffer.
5379 *bytesWritten = 0;
5380 bufferWalker = (const BYTE*)buffer;
5381 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5384 * Calculate how many bytes we can copy to this small block.
5386 bytesToWriteInBuffer =
5387 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5390 * Calculate the offset of the small block in the small block file.
5392 offsetInBigBlockFile.u.HighPart = 0;
5393 offsetInBigBlockFile.u.LowPart =
5394 blockIndex * This->parentStorage->smallBlockSize;
5396 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5399 * Write those bytes in the buffer to the small block file.
5401 res = BlockChainStream_WriteAt(
5402 This->parentStorage->smallBlockRootChain,
5403 offsetInBigBlockFile,
5404 bytesToWriteInBuffer,
5405 bufferWalker,
5406 &bytesWrittenToBigBlockFile);
5407 if (FAILED(res))
5408 return res;
5411 * Step to the next big block.
5413 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5414 &blockIndex)))
5415 return FALSE;
5416 bufferWalker += bytesWrittenToBigBlockFile;
5417 size -= bytesWrittenToBigBlockFile;
5418 *bytesWritten += bytesWrittenToBigBlockFile;
5419 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5422 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5425 /******************************************************************************
5426 * SmallBlockChainStream_Shrink
5428 * Shrinks this chain in the small block depot.
5430 static BOOL SmallBlockChainStream_Shrink(
5431 SmallBlockChainStream* This,
5432 ULARGE_INTEGER newSize)
5434 ULONG blockIndex, extraBlock;
5435 ULONG numBlocks;
5436 ULONG count = 0;
5438 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5440 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5441 numBlocks++;
5443 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5446 * Go to the new end of chain
5448 while (count < numBlocks)
5450 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5451 &blockIndex)))
5452 return FALSE;
5453 count++;
5457 * If the count is 0, we have a special case, the head of the chain was
5458 * just freed.
5460 if (count == 0)
5462 StgProperty chainProp;
5464 StorageImpl_ReadProperty(This->parentStorage,
5465 This->ownerPropertyIndex,
5466 &chainProp);
5468 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5470 StorageImpl_WriteProperty(This->parentStorage,
5471 This->ownerPropertyIndex,
5472 &chainProp);
5475 * We start freeing the chain at the head block.
5477 extraBlock = blockIndex;
5479 else
5481 /* Get the next block before marking the new end */
5482 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5483 &extraBlock)))
5484 return FALSE;
5486 /* Mark the new end of chain */
5487 SmallBlockChainStream_SetNextBlockInChain(
5488 This,
5489 blockIndex,
5490 BLOCK_END_OF_CHAIN);
5494 * Mark the extra blocks as free
5496 while (extraBlock != BLOCK_END_OF_CHAIN)
5498 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5499 &blockIndex)))
5500 return FALSE;
5501 SmallBlockChainStream_FreeBlock(This, extraBlock);
5502 extraBlock = blockIndex;
5505 return TRUE;
5508 /******************************************************************************
5509 * SmallBlockChainStream_Enlarge
5511 * Grows this chain in the small block depot.
5513 static BOOL SmallBlockChainStream_Enlarge(
5514 SmallBlockChainStream* This,
5515 ULARGE_INTEGER newSize)
5517 ULONG blockIndex, currentBlock;
5518 ULONG newNumBlocks;
5519 ULONG oldNumBlocks = 0;
5521 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5524 * Empty chain
5526 if (blockIndex == BLOCK_END_OF_CHAIN)
5529 StgProperty chainProp;
5531 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5532 &chainProp);
5534 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5536 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5537 &chainProp);
5539 blockIndex = chainProp.startingBlock;
5540 SmallBlockChainStream_SetNextBlockInChain(
5541 This,
5542 blockIndex,
5543 BLOCK_END_OF_CHAIN);
5546 currentBlock = blockIndex;
5549 * Figure out how many blocks are needed to contain this stream
5551 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5553 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5554 newNumBlocks++;
5557 * Go to the current end of chain
5559 while (blockIndex != BLOCK_END_OF_CHAIN)
5561 oldNumBlocks++;
5562 currentBlock = blockIndex;
5563 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5564 return FALSE;
5568 * Add new blocks to the chain
5570 while (oldNumBlocks < newNumBlocks)
5572 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5573 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5575 SmallBlockChainStream_SetNextBlockInChain(
5576 This,
5577 blockIndex,
5578 BLOCK_END_OF_CHAIN);
5580 currentBlock = blockIndex;
5581 oldNumBlocks++;
5584 return TRUE;
5587 /******************************************************************************
5588 * SmallBlockChainStream_SetSize
5590 * Sets the size of this stream.
5591 * The file will grow if we grow the chain.
5593 * TODO: Free the actual blocks in the file when we shrink the chain.
5594 * Currently, the blocks are still in the file. So the file size
5595 * doesn't shrink even if we shrink streams.
5597 BOOL SmallBlockChainStream_SetSize(
5598 SmallBlockChainStream* This,
5599 ULARGE_INTEGER newSize)
5601 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5603 if (newSize.u.LowPart == size.u.LowPart)
5604 return TRUE;
5606 if (newSize.u.LowPart < size.u.LowPart)
5608 SmallBlockChainStream_Shrink(This, newSize);
5610 else
5612 SmallBlockChainStream_Enlarge(This, newSize);
5615 return TRUE;
5618 /******************************************************************************
5619 * SmallBlockChainStream_GetSize
5621 * Returns the size of this chain.
5623 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5625 StgProperty chainProperty;
5627 StorageImpl_ReadProperty(
5628 This->parentStorage,
5629 This->ownerPropertyIndex,
5630 &chainProperty);
5632 return chainProperty.size;
5635 /******************************************************************************
5636 * StgCreateDocfile [OLE32.@]
5637 * Creates a new compound file storage object
5639 * PARAMS
5640 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5641 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5642 * reserved [ ?] unused?, usually 0
5643 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5645 * RETURNS
5646 * S_OK if the file was successfully created
5647 * some STG_E_ value if error
5648 * NOTES
5649 * if pwcsName is NULL, create file with new unique name
5650 * the function can returns
5651 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5652 * (unrealized now)
5654 HRESULT WINAPI StgCreateDocfile(
5655 LPCOLESTR pwcsName,
5656 DWORD grfMode,
5657 DWORD reserved,
5658 IStorage **ppstgOpen)
5660 StorageImpl* newStorage = 0;
5661 HANDLE hFile = INVALID_HANDLE_VALUE;
5662 HRESULT hr = STG_E_INVALIDFLAG;
5663 DWORD shareMode;
5664 DWORD accessMode;
5665 DWORD creationMode;
5666 DWORD fileAttributes;
5667 WCHAR tempFileName[MAX_PATH];
5669 TRACE("(%s, %x, %d, %p)\n",
5670 debugstr_w(pwcsName), grfMode,
5671 reserved, ppstgOpen);
5674 * Validate the parameters
5676 if (ppstgOpen == 0)
5677 return STG_E_INVALIDPOINTER;
5678 if (reserved != 0)
5679 return STG_E_INVALIDPARAMETER;
5681 /* if no share mode given then DENY_NONE is the default */
5682 if (STGM_SHARE_MODE(grfMode) == 0)
5683 grfMode |= STGM_SHARE_DENY_NONE;
5686 * Validate the STGM flags
5688 if ( FAILED( validateSTGM(grfMode) ))
5689 goto end;
5691 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5692 switch(STGM_ACCESS_MODE(grfMode))
5694 case STGM_WRITE:
5695 case STGM_READWRITE:
5696 break;
5697 default:
5698 goto end;
5701 /* in direct mode, can only use SHARE_EXCLUSIVE */
5702 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5703 goto end;
5705 /* but in transacted mode, any share mode is valid */
5708 * Generate a unique name.
5710 if (pwcsName == 0)
5712 WCHAR tempPath[MAX_PATH];
5713 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5715 memset(tempPath, 0, sizeof(tempPath));
5716 memset(tempFileName, 0, sizeof(tempFileName));
5718 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5719 tempPath[0] = '.';
5721 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5722 pwcsName = tempFileName;
5723 else
5725 hr = STG_E_INSUFFICIENTMEMORY;
5726 goto end;
5729 creationMode = TRUNCATE_EXISTING;
5731 else
5733 creationMode = GetCreationModeFromSTGM(grfMode);
5737 * Interpret the STGM value grfMode
5739 shareMode = GetShareModeFromSTGM(grfMode);
5740 accessMode = GetAccessModeFromSTGM(grfMode);
5742 if (grfMode & STGM_DELETEONRELEASE)
5743 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5744 else
5745 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5747 if (grfMode & STGM_TRANSACTED)
5748 FIXME("Transacted mode not implemented.\n");
5751 * Initialize the "out" parameter.
5753 *ppstgOpen = 0;
5755 hFile = CreateFileW(pwcsName,
5756 accessMode,
5757 shareMode,
5758 NULL,
5759 creationMode,
5760 fileAttributes,
5763 if (hFile == INVALID_HANDLE_VALUE)
5765 if(GetLastError() == ERROR_FILE_EXISTS)
5766 hr = STG_E_FILEALREADYEXISTS;
5767 else
5768 hr = E_FAIL;
5769 goto end;
5773 * Allocate and initialize the new IStorage32object.
5775 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5777 if (newStorage == 0)
5779 hr = STG_E_INSUFFICIENTMEMORY;
5780 goto end;
5783 hr = StorageImpl_Construct(
5784 newStorage,
5785 hFile,
5786 pwcsName,
5787 NULL,
5788 grfMode,
5789 TRUE,
5790 TRUE);
5792 if (FAILED(hr))
5794 HeapFree(GetProcessHeap(), 0, newStorage);
5795 goto end;
5799 * Get an "out" pointer for the caller.
5801 hr = StorageBaseImpl_QueryInterface(
5802 (IStorage*)newStorage,
5803 (REFIID)&IID_IStorage,
5804 (void**)ppstgOpen);
5805 end:
5806 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5808 return hr;
5811 /******************************************************************************
5812 * StgCreateStorageEx [OLE32.@]
5814 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5816 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5817 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5819 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5821 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5822 return STG_E_INVALIDPARAMETER;
5825 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5827 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5828 return STG_E_INVALIDPARAMETER;
5831 if (stgfmt == STGFMT_FILE)
5833 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5834 return STG_E_INVALIDPARAMETER;
5837 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5839 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5840 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5843 ERR("Invalid stgfmt argument\n");
5844 return STG_E_INVALIDPARAMETER;
5847 /******************************************************************************
5848 * StgCreatePropSetStg [OLE32.@]
5850 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5851 IPropertySetStorage **ppPropSetStg)
5853 HRESULT hr;
5855 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5856 if (reserved)
5857 hr = STG_E_INVALIDPARAMETER;
5858 else
5859 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5860 (void**)ppPropSetStg);
5861 return hr;
5864 /******************************************************************************
5865 * StgOpenStorageEx [OLE32.@]
5867 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5869 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5870 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5872 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5874 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5875 return STG_E_INVALIDPARAMETER;
5878 switch (stgfmt)
5880 case STGFMT_FILE:
5881 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5882 return STG_E_INVALIDPARAMETER;
5884 case STGFMT_STORAGE:
5885 break;
5887 case STGFMT_DOCFILE:
5888 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5890 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5891 return STG_E_INVALIDPARAMETER;
5893 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5894 break;
5896 case STGFMT_ANY:
5897 WARN("STGFMT_ANY assuming storage\n");
5898 break;
5900 default:
5901 return STG_E_INVALIDPARAMETER;
5904 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5908 /******************************************************************************
5909 * StgOpenStorage [OLE32.@]
5911 HRESULT WINAPI StgOpenStorage(
5912 const OLECHAR *pwcsName,
5913 IStorage *pstgPriority,
5914 DWORD grfMode,
5915 SNB snbExclude,
5916 DWORD reserved,
5917 IStorage **ppstgOpen)
5919 StorageImpl* newStorage = 0;
5920 HRESULT hr = S_OK;
5921 HANDLE hFile = 0;
5922 DWORD shareMode;
5923 DWORD accessMode;
5924 WCHAR fullname[MAX_PATH];
5926 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5927 debugstr_w(pwcsName), pstgPriority, grfMode,
5928 snbExclude, reserved, ppstgOpen);
5931 * Perform sanity checks
5933 if (pwcsName == 0)
5935 hr = STG_E_INVALIDNAME;
5936 goto end;
5939 if (ppstgOpen == 0)
5941 hr = STG_E_INVALIDPOINTER;
5942 goto end;
5945 if (reserved)
5947 hr = STG_E_INVALIDPARAMETER;
5948 goto end;
5951 if (grfMode & STGM_PRIORITY)
5953 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5954 return STG_E_INVALIDFLAG;
5955 if (grfMode & STGM_DELETEONRELEASE)
5956 return STG_E_INVALIDFUNCTION;
5957 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5958 return STG_E_INVALIDFLAG;
5959 grfMode &= ~0xf0; /* remove the existing sharing mode */
5960 grfMode |= STGM_SHARE_DENY_NONE;
5962 /* STGM_PRIORITY stops other IStorage objects on the same file from
5963 * committing until the STGM_PRIORITY IStorage is closed. it also
5964 * stops non-transacted mode StgOpenStorage calls with write access from
5965 * succeeding. obviously, both of these cannot be achieved through just
5966 * file share flags */
5967 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5971 * Validate the sharing mode
5973 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5974 switch(STGM_SHARE_MODE(grfMode))
5976 case STGM_SHARE_EXCLUSIVE:
5977 case STGM_SHARE_DENY_WRITE:
5978 break;
5979 default:
5980 hr = STG_E_INVALIDFLAG;
5981 goto end;
5985 * Validate the STGM flags
5987 if ( FAILED( validateSTGM(grfMode) ) ||
5988 (grfMode&STGM_CREATE))
5990 hr = STG_E_INVALIDFLAG;
5991 goto end;
5994 /* shared reading requires transacted mode */
5995 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5996 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5997 !(grfMode&STGM_TRANSACTED) )
5999 hr = STG_E_INVALIDFLAG;
6000 goto end;
6004 * Interpret the STGM value grfMode
6006 shareMode = GetShareModeFromSTGM(grfMode);
6007 accessMode = GetAccessModeFromSTGM(grfMode);
6010 * Initialize the "out" parameter.
6012 *ppstgOpen = 0;
6014 hFile = CreateFileW( pwcsName,
6015 accessMode,
6016 shareMode,
6017 NULL,
6018 OPEN_EXISTING,
6019 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6022 if (hFile==INVALID_HANDLE_VALUE)
6024 DWORD last_error = GetLastError();
6026 hr = E_FAIL;
6028 switch (last_error)
6030 case ERROR_FILE_NOT_FOUND:
6031 hr = STG_E_FILENOTFOUND;
6032 break;
6034 case ERROR_PATH_NOT_FOUND:
6035 hr = STG_E_PATHNOTFOUND;
6036 break;
6038 case ERROR_ACCESS_DENIED:
6039 case ERROR_WRITE_PROTECT:
6040 hr = STG_E_ACCESSDENIED;
6041 break;
6043 case ERROR_SHARING_VIOLATION:
6044 hr = STG_E_SHAREVIOLATION;
6045 break;
6047 default:
6048 hr = E_FAIL;
6051 goto end;
6055 * Refuse to open the file if it's too small to be a structured storage file
6056 * FIXME: verify the file when reading instead of here
6058 if (GetFileSize(hFile, NULL) < 0x100)
6060 CloseHandle(hFile);
6061 hr = STG_E_FILEALREADYEXISTS;
6062 goto end;
6066 * Allocate and initialize the new IStorage32object.
6068 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6070 if (newStorage == 0)
6072 hr = STG_E_INSUFFICIENTMEMORY;
6073 goto end;
6076 /* Initialize the storage */
6077 hr = StorageImpl_Construct(
6078 newStorage,
6079 hFile,
6080 pwcsName,
6081 NULL,
6082 grfMode,
6083 TRUE,
6084 FALSE );
6086 if (FAILED(hr))
6088 HeapFree(GetProcessHeap(), 0, newStorage);
6090 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6092 if(hr == STG_E_INVALIDHEADER)
6093 hr = STG_E_FILEALREADYEXISTS;
6094 goto end;
6097 /* prepare the file name string given in lieu of the root property name */
6098 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6099 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6100 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6103 * Get an "out" pointer for the caller.
6105 hr = StorageBaseImpl_QueryInterface(
6106 (IStorage*)newStorage,
6107 (REFIID)&IID_IStorage,
6108 (void**)ppstgOpen);
6110 end:
6111 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6112 return hr;
6115 /******************************************************************************
6116 * StgCreateDocfileOnILockBytes [OLE32.@]
6118 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6119 ILockBytes *plkbyt,
6120 DWORD grfMode,
6121 DWORD reserved,
6122 IStorage** ppstgOpen)
6124 StorageImpl* newStorage = 0;
6125 HRESULT hr = S_OK;
6128 * Validate the parameters
6130 if ((ppstgOpen == 0) || (plkbyt == 0))
6131 return STG_E_INVALIDPOINTER;
6134 * Allocate and initialize the new IStorage object.
6136 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6138 if (newStorage == 0)
6139 return STG_E_INSUFFICIENTMEMORY;
6141 hr = StorageImpl_Construct(
6142 newStorage,
6145 plkbyt,
6146 grfMode,
6147 FALSE,
6148 TRUE);
6150 if (FAILED(hr))
6152 HeapFree(GetProcessHeap(), 0, newStorage);
6153 return hr;
6157 * Get an "out" pointer for the caller.
6159 hr = StorageBaseImpl_QueryInterface(
6160 (IStorage*)newStorage,
6161 (REFIID)&IID_IStorage,
6162 (void**)ppstgOpen);
6164 return hr;
6167 /******************************************************************************
6168 * StgOpenStorageOnILockBytes [OLE32.@]
6170 HRESULT WINAPI StgOpenStorageOnILockBytes(
6171 ILockBytes *plkbyt,
6172 IStorage *pstgPriority,
6173 DWORD grfMode,
6174 SNB snbExclude,
6175 DWORD reserved,
6176 IStorage **ppstgOpen)
6178 StorageImpl* newStorage = 0;
6179 HRESULT hr = S_OK;
6182 * Perform a sanity check
6184 if ((plkbyt == 0) || (ppstgOpen == 0))
6185 return STG_E_INVALIDPOINTER;
6188 * Validate the STGM flags
6190 if ( FAILED( validateSTGM(grfMode) ))
6191 return STG_E_INVALIDFLAG;
6194 * Initialize the "out" parameter.
6196 *ppstgOpen = 0;
6199 * Allocate and initialize the new IStorage object.
6201 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6203 if (newStorage == 0)
6204 return STG_E_INSUFFICIENTMEMORY;
6206 hr = StorageImpl_Construct(
6207 newStorage,
6210 plkbyt,
6211 grfMode,
6212 FALSE,
6213 FALSE);
6215 if (FAILED(hr))
6217 HeapFree(GetProcessHeap(), 0, newStorage);
6218 return hr;
6222 * Get an "out" pointer for the caller.
6224 hr = StorageBaseImpl_QueryInterface(
6225 (IStorage*)newStorage,
6226 (REFIID)&IID_IStorage,
6227 (void**)ppstgOpen);
6229 return hr;
6232 /******************************************************************************
6233 * StgSetTimes [ole32.@]
6234 * StgSetTimes [OLE32.@]
6238 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6239 FILETIME const *patime, FILETIME const *pmtime)
6241 IStorage *stg = NULL;
6242 HRESULT r;
6244 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6246 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6247 0, 0, &stg);
6248 if( SUCCEEDED(r) )
6250 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6251 IStorage_Release(stg);
6254 return r;
6257 /******************************************************************************
6258 * StgIsStorageILockBytes [OLE32.@]
6260 * Determines if the ILockBytes contains a storage object.
6262 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6264 BYTE sig[8];
6265 ULARGE_INTEGER offset;
6267 offset.u.HighPart = 0;
6268 offset.u.LowPart = 0;
6270 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6272 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6273 return S_OK;
6275 return S_FALSE;
6278 /******************************************************************************
6279 * WriteClassStg [OLE32.@]
6281 * This method will store the specified CLSID in the specified storage object
6283 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6285 HRESULT hRes;
6287 if(!pStg)
6288 return E_INVALIDARG;
6290 hRes = IStorage_SetClass(pStg, rclsid);
6292 return hRes;
6295 /***********************************************************************
6296 * ReadClassStg (OLE32.@)
6298 * This method reads the CLSID previously written to a storage object with
6299 * the WriteClassStg.
6301 * PARAMS
6302 * pstg [I] IStorage pointer
6303 * pclsid [O] Pointer to where the CLSID is written
6305 * RETURNS
6306 * Success: S_OK.
6307 * Failure: HRESULT code.
6309 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6311 STATSTG pstatstg;
6312 HRESULT hRes;
6314 TRACE("(%p, %p)\n", pstg, pclsid);
6316 if(!pstg || !pclsid)
6317 return E_INVALIDARG;
6320 * read a STATSTG structure (contains the clsid) from the storage
6322 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6324 if(SUCCEEDED(hRes))
6325 *pclsid=pstatstg.clsid;
6327 return hRes;
6330 /***********************************************************************
6331 * OleLoadFromStream (OLE32.@)
6333 * This function loads an object from stream
6335 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6337 CLSID clsid;
6338 HRESULT res;
6339 LPPERSISTSTREAM xstm;
6341 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6343 res=ReadClassStm(pStm,&clsid);
6344 if (!SUCCEEDED(res))
6345 return res;
6346 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6347 if (!SUCCEEDED(res))
6348 return res;
6349 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6350 if (!SUCCEEDED(res)) {
6351 IUnknown_Release((IUnknown*)*ppvObj);
6352 return res;
6354 res=IPersistStream_Load(xstm,pStm);
6355 IPersistStream_Release(xstm);
6356 /* FIXME: all refcounts ok at this point? I think they should be:
6357 * pStm : unchanged
6358 * ppvObj : 1
6359 * xstm : 0 (released)
6361 return res;
6364 /***********************************************************************
6365 * OleSaveToStream (OLE32.@)
6367 * This function saves an object with the IPersistStream interface on it
6368 * to the specified stream.
6370 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6373 CLSID clsid;
6374 HRESULT res;
6376 TRACE("(%p,%p)\n",pPStm,pStm);
6378 res=IPersistStream_GetClassID(pPStm,&clsid);
6380 if (SUCCEEDED(res)){
6382 res=WriteClassStm(pStm,&clsid);
6384 if (SUCCEEDED(res))
6386 res=IPersistStream_Save(pPStm,pStm,TRUE);
6389 TRACE("Finished Save\n");
6390 return res;
6393 /****************************************************************************
6394 * This method validate a STGM parameter that can contain the values below
6396 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6397 * The stgm values contained in 0xffff0000 are bitmasks.
6399 * STGM_DIRECT 0x00000000
6400 * STGM_TRANSACTED 0x00010000
6401 * STGM_SIMPLE 0x08000000
6403 * STGM_READ 0x00000000
6404 * STGM_WRITE 0x00000001
6405 * STGM_READWRITE 0x00000002
6407 * STGM_SHARE_DENY_NONE 0x00000040
6408 * STGM_SHARE_DENY_READ 0x00000030
6409 * STGM_SHARE_DENY_WRITE 0x00000020
6410 * STGM_SHARE_EXCLUSIVE 0x00000010
6412 * STGM_PRIORITY 0x00040000
6413 * STGM_DELETEONRELEASE 0x04000000
6415 * STGM_CREATE 0x00001000
6416 * STGM_CONVERT 0x00020000
6417 * STGM_FAILIFTHERE 0x00000000
6419 * STGM_NOSCRATCH 0x00100000
6420 * STGM_NOSNAPSHOT 0x00200000
6422 static HRESULT validateSTGM(DWORD stgm)
6424 DWORD access = STGM_ACCESS_MODE(stgm);
6425 DWORD share = STGM_SHARE_MODE(stgm);
6426 DWORD create = STGM_CREATE_MODE(stgm);
6428 if (stgm&~STGM_KNOWN_FLAGS)
6430 ERR("unknown flags %08x\n", stgm);
6431 return E_FAIL;
6434 switch (access)
6436 case STGM_READ:
6437 case STGM_WRITE:
6438 case STGM_READWRITE:
6439 break;
6440 default:
6441 return E_FAIL;
6444 switch (share)
6446 case STGM_SHARE_DENY_NONE:
6447 case STGM_SHARE_DENY_READ:
6448 case STGM_SHARE_DENY_WRITE:
6449 case STGM_SHARE_EXCLUSIVE:
6450 break;
6451 default:
6452 return E_FAIL;
6455 switch (create)
6457 case STGM_CREATE:
6458 case STGM_FAILIFTHERE:
6459 break;
6460 default:
6461 return E_FAIL;
6465 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6467 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6468 return E_FAIL;
6471 * STGM_CREATE | STGM_CONVERT
6472 * if both are false, STGM_FAILIFTHERE is set to TRUE
6474 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6475 return E_FAIL;
6478 * STGM_NOSCRATCH requires STGM_TRANSACTED
6480 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6481 return E_FAIL;
6484 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6485 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6487 if ( (stgm & STGM_NOSNAPSHOT) &&
6488 (!(stgm & STGM_TRANSACTED) ||
6489 share == STGM_SHARE_EXCLUSIVE ||
6490 share == STGM_SHARE_DENY_WRITE) )
6491 return E_FAIL;
6493 return S_OK;
6496 /****************************************************************************
6497 * GetShareModeFromSTGM
6499 * This method will return a share mode flag from a STGM value.
6500 * The STGM value is assumed valid.
6502 static DWORD GetShareModeFromSTGM(DWORD stgm)
6504 switch (STGM_SHARE_MODE(stgm))
6506 case STGM_SHARE_DENY_NONE:
6507 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6508 case STGM_SHARE_DENY_READ:
6509 return FILE_SHARE_WRITE;
6510 case STGM_SHARE_DENY_WRITE:
6511 return FILE_SHARE_READ;
6512 case STGM_SHARE_EXCLUSIVE:
6513 return 0;
6515 ERR("Invalid share mode!\n");
6516 assert(0);
6517 return 0;
6520 /****************************************************************************
6521 * GetAccessModeFromSTGM
6523 * This method will return an access mode flag from a STGM value.
6524 * The STGM value is assumed valid.
6526 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6528 switch (STGM_ACCESS_MODE(stgm))
6530 case STGM_READ:
6531 return GENERIC_READ;
6532 case STGM_WRITE:
6533 case STGM_READWRITE:
6534 return GENERIC_READ | GENERIC_WRITE;
6536 ERR("Invalid access mode!\n");
6537 assert(0);
6538 return 0;
6541 /****************************************************************************
6542 * GetCreationModeFromSTGM
6544 * This method will return a creation mode flag from a STGM value.
6545 * The STGM value is assumed valid.
6547 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6549 switch(STGM_CREATE_MODE(stgm))
6551 case STGM_CREATE:
6552 return CREATE_ALWAYS;
6553 case STGM_CONVERT:
6554 FIXME("STGM_CONVERT not implemented!\n");
6555 return CREATE_NEW;
6556 case STGM_FAILIFTHERE:
6557 return CREATE_NEW;
6559 ERR("Invalid create mode!\n");
6560 assert(0);
6561 return 0;
6565 /*************************************************************************
6566 * OLECONVERT_LoadOLE10 [Internal]
6568 * Loads the OLE10 STREAM to memory
6570 * PARAMS
6571 * pOleStream [I] The OLESTREAM
6572 * pData [I] Data Structure for the OLESTREAM Data
6574 * RETURNS
6575 * Success: S_OK
6576 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6577 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6579 * NOTES
6580 * This function is used by OleConvertOLESTREAMToIStorage only.
6582 * Memory allocated for pData must be freed by the caller
6584 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6586 DWORD dwSize;
6587 HRESULT hRes = S_OK;
6588 int nTryCnt=0;
6589 int max_try = 6;
6591 pData->pData = NULL;
6592 pData->pstrOleObjFileName = (CHAR *) NULL;
6594 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6596 /* Get the OleID */
6597 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6598 if(dwSize != sizeof(pData->dwOleID))
6600 hRes = CONVERT10_E_OLESTREAM_GET;
6602 else if(pData->dwOleID != OLESTREAM_ID)
6604 hRes = CONVERT10_E_OLESTREAM_FMT;
6606 else
6608 hRes = S_OK;
6609 break;
6613 if(hRes == S_OK)
6615 /* Get the TypeID...more info needed for this field */
6616 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6617 if(dwSize != sizeof(pData->dwTypeID))
6619 hRes = CONVERT10_E_OLESTREAM_GET;
6622 if(hRes == S_OK)
6624 if(pData->dwTypeID != 0)
6626 /* Get the length of the OleTypeName */
6627 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6628 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6630 hRes = CONVERT10_E_OLESTREAM_GET;
6633 if(hRes == S_OK)
6635 if(pData->dwOleTypeNameLength > 0)
6637 /* Get the OleTypeName */
6638 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6639 if(dwSize != pData->dwOleTypeNameLength)
6641 hRes = CONVERT10_E_OLESTREAM_GET;
6645 if(bStrem1)
6647 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6648 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6650 hRes = CONVERT10_E_OLESTREAM_GET;
6652 if(hRes == S_OK)
6654 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6655 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6656 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6657 if(pData->pstrOleObjFileName)
6659 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6660 if(dwSize != pData->dwOleObjFileNameLength)
6662 hRes = CONVERT10_E_OLESTREAM_GET;
6665 else
6666 hRes = CONVERT10_E_OLESTREAM_GET;
6669 else
6671 /* Get the Width of the Metafile */
6672 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6673 if(dwSize != sizeof(pData->dwMetaFileWidth))
6675 hRes = CONVERT10_E_OLESTREAM_GET;
6677 if(hRes == S_OK)
6679 /* Get the Height of the Metafile */
6680 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6681 if(dwSize != sizeof(pData->dwMetaFileHeight))
6683 hRes = CONVERT10_E_OLESTREAM_GET;
6687 if(hRes == S_OK)
6689 /* Get the Length of the Data */
6690 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6691 if(dwSize != sizeof(pData->dwDataLength))
6693 hRes = CONVERT10_E_OLESTREAM_GET;
6697 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6699 if(!bStrem1) /* if it is a second OLE stream data */
6701 pData->dwDataLength -= 8;
6702 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6703 if(dwSize != sizeof(pData->strUnknown))
6705 hRes = CONVERT10_E_OLESTREAM_GET;
6709 if(hRes == S_OK)
6711 if(pData->dwDataLength > 0)
6713 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6715 /* Get Data (ex. IStorage, Metafile, or BMP) */
6716 if(pData->pData)
6718 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6719 if(dwSize != pData->dwDataLength)
6721 hRes = CONVERT10_E_OLESTREAM_GET;
6724 else
6726 hRes = CONVERT10_E_OLESTREAM_GET;
6732 return hRes;
6735 /*************************************************************************
6736 * OLECONVERT_SaveOLE10 [Internal]
6738 * Saves the OLE10 STREAM From memory
6740 * PARAMS
6741 * pData [I] Data Structure for the OLESTREAM Data
6742 * pOleStream [I] The OLESTREAM to save
6744 * RETURNS
6745 * Success: S_OK
6746 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6748 * NOTES
6749 * This function is used by OleConvertIStorageToOLESTREAM only.
6752 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6754 DWORD dwSize;
6755 HRESULT hRes = S_OK;
6758 /* Set the OleID */
6759 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6760 if(dwSize != sizeof(pData->dwOleID))
6762 hRes = CONVERT10_E_OLESTREAM_PUT;
6765 if(hRes == S_OK)
6767 /* Set the TypeID */
6768 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6769 if(dwSize != sizeof(pData->dwTypeID))
6771 hRes = CONVERT10_E_OLESTREAM_PUT;
6775 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6777 /* Set the Length of the OleTypeName */
6778 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6779 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6781 hRes = CONVERT10_E_OLESTREAM_PUT;
6784 if(hRes == S_OK)
6786 if(pData->dwOleTypeNameLength > 0)
6788 /* Set the OleTypeName */
6789 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6790 if(dwSize != pData->dwOleTypeNameLength)
6792 hRes = CONVERT10_E_OLESTREAM_PUT;
6797 if(hRes == S_OK)
6799 /* Set the width of the Metafile */
6800 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6801 if(dwSize != sizeof(pData->dwMetaFileWidth))
6803 hRes = CONVERT10_E_OLESTREAM_PUT;
6807 if(hRes == S_OK)
6809 /* Set the height of the Metafile */
6810 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6811 if(dwSize != sizeof(pData->dwMetaFileHeight))
6813 hRes = CONVERT10_E_OLESTREAM_PUT;
6817 if(hRes == S_OK)
6819 /* Set the length of the Data */
6820 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6821 if(dwSize != sizeof(pData->dwDataLength))
6823 hRes = CONVERT10_E_OLESTREAM_PUT;
6827 if(hRes == S_OK)
6829 if(pData->dwDataLength > 0)
6831 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6832 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6833 if(dwSize != pData->dwDataLength)
6835 hRes = CONVERT10_E_OLESTREAM_PUT;
6840 return hRes;
6843 /*************************************************************************
6844 * OLECONVERT_GetOLE20FromOLE10[Internal]
6846 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6847 * opens it, and copies the content to the dest IStorage for
6848 * OleConvertOLESTREAMToIStorage
6851 * PARAMS
6852 * pDestStorage [I] The IStorage to copy the data to
6853 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6854 * nBufferLength [I] The size of the buffer
6856 * RETURNS
6857 * Nothing
6859 * NOTES
6863 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6865 HRESULT hRes;
6866 HANDLE hFile;
6867 IStorage *pTempStorage;
6868 DWORD dwNumOfBytesWritten;
6869 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6870 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6872 /* Create a temp File */
6873 GetTempPathW(MAX_PATH, wstrTempDir);
6874 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6875 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6877 if(hFile != INVALID_HANDLE_VALUE)
6879 /* Write IStorage Data to File */
6880 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6881 CloseHandle(hFile);
6883 /* Open and copy temp storage to the Dest Storage */
6884 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6885 if(hRes == S_OK)
6887 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6888 StorageBaseImpl_Release(pTempStorage);
6890 DeleteFileW(wstrTempFile);
6895 /*************************************************************************
6896 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6898 * Saves the OLE10 STREAM From memory
6900 * PARAMS
6901 * pStorage [I] The Src IStorage to copy
6902 * pData [I] The Dest Memory to write to.
6904 * RETURNS
6905 * The size in bytes allocated for pData
6907 * NOTES
6908 * Memory allocated for pData must be freed by the caller
6910 * Used by OleConvertIStorageToOLESTREAM only.
6913 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6915 HANDLE hFile;
6916 HRESULT hRes;
6917 DWORD nDataLength = 0;
6918 IStorage *pTempStorage;
6919 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6920 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6922 *pData = NULL;
6924 /* Create temp Storage */
6925 GetTempPathW(MAX_PATH, wstrTempDir);
6926 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6927 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6929 if(hRes == S_OK)
6931 /* Copy Src Storage to the Temp Storage */
6932 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6933 StorageBaseImpl_Release(pTempStorage);
6935 /* Open Temp Storage as a file and copy to memory */
6936 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6937 if(hFile != INVALID_HANDLE_VALUE)
6939 nDataLength = GetFileSize(hFile, NULL);
6940 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6941 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6942 CloseHandle(hFile);
6944 DeleteFileW(wstrTempFile);
6946 return nDataLength;
6949 /*************************************************************************
6950 * OLECONVERT_CreateOleStream [Internal]
6952 * Creates the "\001OLE" stream in the IStorage if necessary.
6954 * PARAMS
6955 * pStorage [I] Dest storage to create the stream in
6957 * RETURNS
6958 * Nothing
6960 * NOTES
6961 * This function is used by OleConvertOLESTREAMToIStorage only.
6963 * This stream is still unknown, MS Word seems to have extra data
6964 * but since the data is stored in the OLESTREAM there should be
6965 * no need to recreate the stream. If the stream is manually
6966 * deleted it will create it with this default data.
6969 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6971 HRESULT hRes;
6972 IStream *pStream;
6973 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6974 BYTE pOleStreamHeader [] =
6976 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6977 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6978 0x00, 0x00, 0x00, 0x00
6981 /* Create stream if not present */
6982 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6983 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6985 if(hRes == S_OK)
6987 /* Write default Data */
6988 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6989 IStream_Release(pStream);
6993 /* write a string to a stream, preceded by its length */
6994 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6996 HRESULT r;
6997 LPSTR str;
6998 DWORD len = 0;
7000 if( string )
7001 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7002 r = IStream_Write( stm, &len, sizeof(len), NULL);
7003 if( FAILED( r ) )
7004 return r;
7005 if(len == 0)
7006 return r;
7007 str = CoTaskMemAlloc( len );
7008 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7009 r = IStream_Write( stm, str, len, NULL);
7010 CoTaskMemFree( str );
7011 return r;
7014 /* read a string preceded by its length from a stream */
7015 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7017 HRESULT r;
7018 DWORD len, count = 0;
7019 LPSTR str;
7020 LPWSTR wstr;
7022 r = IStream_Read( stm, &len, sizeof(len), &count );
7023 if( FAILED( r ) )
7024 return r;
7025 if( count != sizeof(len) )
7026 return E_OUTOFMEMORY;
7028 TRACE("%d bytes\n",len);
7030 str = CoTaskMemAlloc( len );
7031 if( !str )
7032 return E_OUTOFMEMORY;
7033 count = 0;
7034 r = IStream_Read( stm, str, len, &count );
7035 if( FAILED( r ) )
7036 return r;
7037 if( count != len )
7039 CoTaskMemFree( str );
7040 return E_OUTOFMEMORY;
7043 TRACE("Read string %s\n",debugstr_an(str,len));
7045 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7046 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7047 if( wstr )
7048 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7049 CoTaskMemFree( str );
7051 *string = wstr;
7053 return r;
7057 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7058 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7060 IStream *pstm;
7061 HRESULT r = S_OK;
7062 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7064 static const BYTE unknown1[12] =
7065 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7066 0xFF, 0xFF, 0xFF, 0xFF};
7067 static const BYTE unknown2[16] =
7068 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7069 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7071 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7072 debugstr_w(lpszUserType), debugstr_w(szClipName),
7073 debugstr_w(szProgIDName));
7075 /* Create a CompObj stream if it doesn't exist */
7076 r = IStorage_CreateStream(pstg, szwStreamName,
7077 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7078 if( FAILED (r) )
7079 return r;
7081 /* Write CompObj Structure to stream */
7082 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7084 if( SUCCEEDED( r ) )
7085 r = WriteClassStm( pstm, clsid );
7087 if( SUCCEEDED( r ) )
7088 r = STREAM_WriteString( pstm, lpszUserType );
7089 if( SUCCEEDED( r ) )
7090 r = STREAM_WriteString( pstm, szClipName );
7091 if( SUCCEEDED( r ) )
7092 r = STREAM_WriteString( pstm, szProgIDName );
7093 if( SUCCEEDED( r ) )
7094 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7096 IStream_Release( pstm );
7098 return r;
7101 /***********************************************************************
7102 * WriteFmtUserTypeStg (OLE32.@)
7104 HRESULT WINAPI WriteFmtUserTypeStg(
7105 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7107 HRESULT r;
7108 WCHAR szwClipName[0x40];
7109 CLSID clsid = CLSID_NULL;
7110 LPWSTR wstrProgID = NULL;
7111 DWORD n;
7113 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7115 /* get the clipboard format name */
7116 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7117 szwClipName[n]=0;
7119 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7121 /* FIXME: There's room to save a CLSID and its ProgID, but
7122 the CLSID is not looked up in the registry and in all the
7123 tests I wrote it was CLSID_NULL. Where does it come from?
7126 /* get the real program ID. This may fail, but that's fine */
7127 ProgIDFromCLSID(&clsid, &wstrProgID);
7129 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7131 r = STORAGE_WriteCompObj( pstg, &clsid,
7132 lpszUserType, szwClipName, wstrProgID );
7134 CoTaskMemFree(wstrProgID);
7136 return r;
7140 /******************************************************************************
7141 * ReadFmtUserTypeStg [OLE32.@]
7143 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7145 HRESULT r;
7146 IStream *stm = 0;
7147 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7148 unsigned char unknown1[12];
7149 unsigned char unknown2[16];
7150 DWORD count;
7151 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7152 CLSID clsid;
7154 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7156 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7157 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7158 if( FAILED ( r ) )
7160 WARN("Failed to open stream r = %08x\n", r);
7161 return r;
7164 /* read the various parts of the structure */
7165 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7166 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7167 goto end;
7168 r = ReadClassStm( stm, &clsid );
7169 if( FAILED( r ) )
7170 goto end;
7172 r = STREAM_ReadString( stm, &szCLSIDName );
7173 if( FAILED( r ) )
7174 goto end;
7176 r = STREAM_ReadString( stm, &szOleTypeName );
7177 if( FAILED( r ) )
7178 goto end;
7180 r = STREAM_ReadString( stm, &szProgIDName );
7181 if( FAILED( r ) )
7182 goto end;
7184 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7185 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7186 goto end;
7188 /* ok, success... now we just need to store what we found */
7189 if( pcf )
7190 *pcf = RegisterClipboardFormatW( szOleTypeName );
7191 CoTaskMemFree( szOleTypeName );
7193 if( lplpszUserType )
7194 *lplpszUserType = szCLSIDName;
7195 CoTaskMemFree( szProgIDName );
7197 end:
7198 IStream_Release( stm );
7200 return r;
7204 /*************************************************************************
7205 * OLECONVERT_CreateCompObjStream [Internal]
7207 * Creates a "\001CompObj" is the destination IStorage if necessary.
7209 * PARAMS
7210 * pStorage [I] The dest IStorage to create the CompObj Stream
7211 * if necessary.
7212 * strOleTypeName [I] The ProgID
7214 * RETURNS
7215 * Success: S_OK
7216 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7218 * NOTES
7219 * This function is used by OleConvertOLESTREAMToIStorage only.
7221 * The stream data is stored in the OLESTREAM and there should be
7222 * no need to recreate the stream. If the stream is manually
7223 * deleted it will attempt to create it by querying the registry.
7227 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7229 IStream *pStream;
7230 HRESULT hStorageRes, hRes = S_OK;
7231 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7232 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7233 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7235 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7236 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7238 /* Initialize the CompObj structure */
7239 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7240 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7241 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7244 /* Create a CompObj stream if it doesn't exist */
7245 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7246 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7247 if(hStorageRes == S_OK)
7249 /* copy the OleTypeName to the compobj struct */
7250 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7251 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7253 /* copy the OleTypeName to the compobj struct */
7254 /* Note: in the test made, these were Identical */
7255 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7256 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7258 /* Get the CLSID */
7259 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7260 bufferW, OLESTREAM_MAX_STR_LEN );
7261 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7263 if(hRes == S_OK)
7265 HKEY hKey;
7266 LONG hErr;
7267 /* Get the CLSID Default Name from the Registry */
7268 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7269 if(hErr == ERROR_SUCCESS)
7271 char strTemp[OLESTREAM_MAX_STR_LEN];
7272 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7273 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7274 if(hErr == ERROR_SUCCESS)
7276 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7278 RegCloseKey(hKey);
7282 /* Write CompObj Structure to stream */
7283 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7285 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7287 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7288 if(IStorageCompObj.dwCLSIDNameLength > 0)
7290 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7292 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7293 if(IStorageCompObj.dwOleTypeNameLength > 0)
7295 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7297 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7298 if(IStorageCompObj.dwProgIDNameLength > 0)
7300 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7302 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7303 IStream_Release(pStream);
7305 return hRes;
7309 /*************************************************************************
7310 * OLECONVERT_CreateOlePresStream[Internal]
7312 * Creates the "\002OlePres000" Stream with the Metafile data
7314 * PARAMS
7315 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7316 * dwExtentX [I] Width of the Metafile
7317 * dwExtentY [I] Height of the Metafile
7318 * pData [I] Metafile data
7319 * dwDataLength [I] Size of the Metafile data
7321 * RETURNS
7322 * Success: S_OK
7323 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7325 * NOTES
7326 * This function is used by OleConvertOLESTREAMToIStorage only.
7329 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7331 HRESULT hRes;
7332 IStream *pStream;
7333 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7334 BYTE pOlePresStreamHeader [] =
7336 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7337 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7338 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7339 0x00, 0x00, 0x00, 0x00
7342 BYTE pOlePresStreamHeaderEmpty [] =
7344 0x00, 0x00, 0x00, 0x00,
7345 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7346 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7347 0x00, 0x00, 0x00, 0x00
7350 /* Create the OlePres000 Stream */
7351 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7352 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7354 if(hRes == S_OK)
7356 DWORD nHeaderSize;
7357 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7359 memset(&OlePres, 0, sizeof(OlePres));
7360 /* Do we have any metafile data to save */
7361 if(dwDataLength > 0)
7363 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7364 nHeaderSize = sizeof(pOlePresStreamHeader);
7366 else
7368 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7369 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7371 /* Set width and height of the metafile */
7372 OlePres.dwExtentX = dwExtentX;
7373 OlePres.dwExtentY = -dwExtentY;
7375 /* Set Data and Length */
7376 if(dwDataLength > sizeof(METAFILEPICT16))
7378 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7379 OlePres.pData = &(pData[8]);
7381 /* Save OlePres000 Data to Stream */
7382 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7383 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7384 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7385 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7386 if(OlePres.dwSize > 0)
7388 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7390 IStream_Release(pStream);
7394 /*************************************************************************
7395 * OLECONVERT_CreateOle10NativeStream [Internal]
7397 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7399 * PARAMS
7400 * pStorage [I] Dest storage to create the stream in
7401 * pData [I] Ole10 Native Data (ex. bmp)
7402 * dwDataLength [I] Size of the Ole10 Native Data
7404 * RETURNS
7405 * Nothing
7407 * NOTES
7408 * This function is used by OleConvertOLESTREAMToIStorage only.
7410 * Might need to verify the data and return appropriate error message
7413 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7415 HRESULT hRes;
7416 IStream *pStream;
7417 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7419 /* Create the Ole10Native Stream */
7420 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7421 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7423 if(hRes == S_OK)
7425 /* Write info to stream */
7426 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7427 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7428 IStream_Release(pStream);
7433 /*************************************************************************
7434 * OLECONVERT_GetOLE10ProgID [Internal]
7436 * Finds the ProgID (or OleTypeID) from the IStorage
7438 * PARAMS
7439 * pStorage [I] The Src IStorage to get the ProgID
7440 * strProgID [I] the ProgID string to get
7441 * dwSize [I] the size of the string
7443 * RETURNS
7444 * Success: S_OK
7445 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7447 * NOTES
7448 * This function is used by OleConvertIStorageToOLESTREAM only.
7452 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7454 HRESULT hRes;
7455 IStream *pStream;
7456 LARGE_INTEGER iSeekPos;
7457 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7458 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7460 /* Open the CompObj Stream */
7461 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7462 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7463 if(hRes == S_OK)
7466 /*Get the OleType from the CompObj Stream */
7467 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7468 iSeekPos.u.HighPart = 0;
7470 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7471 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7472 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7473 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7474 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7475 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7476 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7478 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7479 if(*dwSize > 0)
7481 IStream_Read(pStream, strProgID, *dwSize, NULL);
7483 IStream_Release(pStream);
7485 else
7487 STATSTG stat;
7488 LPOLESTR wstrProgID;
7490 /* Get the OleType from the registry */
7491 REFCLSID clsid = &(stat.clsid);
7492 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7493 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7494 if(hRes == S_OK)
7496 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7500 return hRes;
7503 /*************************************************************************
7504 * OLECONVERT_GetOle10PresData [Internal]
7506 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7508 * PARAMS
7509 * pStorage [I] Src IStroage
7510 * pOleStream [I] Dest OleStream Mem Struct
7512 * RETURNS
7513 * Nothing
7515 * NOTES
7516 * This function is used by OleConvertIStorageToOLESTREAM only.
7518 * Memory allocated for pData must be freed by the caller
7522 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7525 HRESULT hRes;
7526 IStream *pStream;
7527 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7529 /* Initialize Default data for OLESTREAM */
7530 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7531 pOleStreamData[0].dwTypeID = 2;
7532 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7533 pOleStreamData[1].dwTypeID = 0;
7534 pOleStreamData[0].dwMetaFileWidth = 0;
7535 pOleStreamData[0].dwMetaFileHeight = 0;
7536 pOleStreamData[0].pData = NULL;
7537 pOleStreamData[1].pData = NULL;
7539 /* Open Ole10Native Stream */
7540 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7541 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7542 if(hRes == S_OK)
7545 /* Read Size and Data */
7546 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7547 if(pOleStreamData->dwDataLength > 0)
7549 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7550 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7552 IStream_Release(pStream);
7558 /*************************************************************************
7559 * OLECONVERT_GetOle20PresData[Internal]
7561 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7563 * PARAMS
7564 * pStorage [I] Src IStroage
7565 * pOleStreamData [I] Dest OleStream Mem Struct
7567 * RETURNS
7568 * Nothing
7570 * NOTES
7571 * This function is used by OleConvertIStorageToOLESTREAM only.
7573 * Memory allocated for pData must be freed by the caller
7575 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7577 HRESULT hRes;
7578 IStream *pStream;
7579 OLECONVERT_ISTORAGE_OLEPRES olePress;
7580 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7582 /* Initialize Default data for OLESTREAM */
7583 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7584 pOleStreamData[0].dwTypeID = 2;
7585 pOleStreamData[0].dwMetaFileWidth = 0;
7586 pOleStreamData[0].dwMetaFileHeight = 0;
7587 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7588 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7589 pOleStreamData[1].dwTypeID = 0;
7590 pOleStreamData[1].dwOleTypeNameLength = 0;
7591 pOleStreamData[1].strOleTypeName[0] = 0;
7592 pOleStreamData[1].dwMetaFileWidth = 0;
7593 pOleStreamData[1].dwMetaFileHeight = 0;
7594 pOleStreamData[1].pData = NULL;
7595 pOleStreamData[1].dwDataLength = 0;
7598 /* Open OlePress000 stream */
7599 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7600 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7601 if(hRes == S_OK)
7603 LARGE_INTEGER iSeekPos;
7604 METAFILEPICT16 MetaFilePict;
7605 static const char strMetafilePictName[] = "METAFILEPICT";
7607 /* Set the TypeID for a Metafile */
7608 pOleStreamData[1].dwTypeID = 5;
7610 /* Set the OleTypeName to Metafile */
7611 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7612 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7614 iSeekPos.u.HighPart = 0;
7615 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7617 /* Get Presentation Data */
7618 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7619 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7620 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7621 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7623 /*Set width and Height */
7624 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7625 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7626 if(olePress.dwSize > 0)
7628 /* Set Length */
7629 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7631 /* Set MetaFilePict struct */
7632 MetaFilePict.mm = 8;
7633 MetaFilePict.xExt = olePress.dwExtentX;
7634 MetaFilePict.yExt = olePress.dwExtentY;
7635 MetaFilePict.hMF = 0;
7637 /* Get Metafile Data */
7638 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7639 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7640 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7642 IStream_Release(pStream);
7646 /*************************************************************************
7647 * OleConvertOLESTREAMToIStorage [OLE32.@]
7649 * Read info on MSDN
7651 * TODO
7652 * DVTARGETDEVICE paramenter is not handled
7653 * Still unsure of some mem fields for OLE 10 Stream
7654 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7655 * and "\001OLE" streams
7658 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7659 LPOLESTREAM pOleStream,
7660 LPSTORAGE pstg,
7661 const DVTARGETDEVICE* ptd)
7663 int i;
7664 HRESULT hRes=S_OK;
7665 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7667 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7669 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7671 if(ptd != NULL)
7673 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7676 if(pstg == NULL || pOleStream == NULL)
7678 hRes = E_INVALIDARG;
7681 if(hRes == S_OK)
7683 /* Load the OLESTREAM to Memory */
7684 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7687 if(hRes == S_OK)
7689 /* Load the OLESTREAM to Memory (part 2)*/
7690 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7693 if(hRes == S_OK)
7696 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7698 /* Do we have the IStorage Data in the OLESTREAM */
7699 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7701 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7702 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7704 else
7706 /* It must be an original OLE 1.0 source */
7707 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7710 else
7712 /* It must be an original OLE 1.0 source */
7713 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7716 /* Create CompObj Stream if necessary */
7717 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7718 if(hRes == S_OK)
7720 /*Create the Ole Stream if necessary */
7721 OLECONVERT_CreateOleStream(pstg);
7726 /* Free allocated memory */
7727 for(i=0; i < 2; i++)
7729 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7730 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7731 pOleStreamData[i].pstrOleObjFileName = NULL;
7733 return hRes;
7736 /*************************************************************************
7737 * OleConvertIStorageToOLESTREAM [OLE32.@]
7739 * Read info on MSDN
7741 * Read info on MSDN
7743 * TODO
7744 * Still unsure of some mem fields for OLE 10 Stream
7745 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7746 * and "\001OLE" streams.
7749 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7750 LPSTORAGE pstg,
7751 LPOLESTREAM pOleStream)
7753 int i;
7754 HRESULT hRes = S_OK;
7755 IStream *pStream;
7756 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7757 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7759 TRACE("%p %p\n", pstg, pOleStream);
7761 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7763 if(pstg == NULL || pOleStream == NULL)
7765 hRes = E_INVALIDARG;
7767 if(hRes == S_OK)
7769 /* Get the ProgID */
7770 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7771 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7773 if(hRes == S_OK)
7775 /* Was it originally Ole10 */
7776 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7777 if(hRes == S_OK)
7779 IStream_Release(pStream);
7780 /* Get Presentation Data for Ole10Native */
7781 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7783 else
7785 /* Get Presentation Data (OLE20) */
7786 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7789 /* Save OLESTREAM */
7790 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7791 if(hRes == S_OK)
7793 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7798 /* Free allocated memory */
7799 for(i=0; i < 2; i++)
7801 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7804 return hRes;
7807 /***********************************************************************
7808 * GetConvertStg (OLE32.@)
7810 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7811 FIXME("unimplemented stub!\n");
7812 return E_FAIL;
7815 /******************************************************************************
7816 * StgIsStorageFile [OLE32.@]
7817 * Verify if the file contains a storage object
7819 * PARAMS
7820 * fn [ I] Filename
7822 * RETURNS
7823 * S_OK if file has magic bytes as a storage object
7824 * S_FALSE if file is not storage
7826 HRESULT WINAPI
7827 StgIsStorageFile(LPCOLESTR fn)
7829 HANDLE hf;
7830 BYTE magic[8];
7831 DWORD bytes_read;
7833 TRACE("%s\n", debugstr_w(fn));
7834 hf = CreateFileW(fn, GENERIC_READ,
7835 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7836 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7838 if (hf == INVALID_HANDLE_VALUE)
7839 return STG_E_FILENOTFOUND;
7841 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7843 WARN(" unable to read file\n");
7844 CloseHandle(hf);
7845 return S_FALSE;
7848 CloseHandle(hf);
7850 if (bytes_read != 8) {
7851 WARN(" too short\n");
7852 return S_FALSE;
7855 if (!memcmp(magic,STORAGE_magic,8)) {
7856 WARN(" -> YES\n");
7857 return S_OK;
7860 WARN(" -> Invalid header.\n");
7861 return S_FALSE;
7864 /***********************************************************************
7865 * WriteClassStm (OLE32.@)
7867 * Writes a CLSID to a stream.
7869 * PARAMS
7870 * pStm [I] Stream to write to.
7871 * rclsid [I] CLSID to write.
7873 * RETURNS
7874 * Success: S_OK.
7875 * Failure: HRESULT code.
7877 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7879 TRACE("(%p,%p)\n",pStm,rclsid);
7881 if (!pStm || !rclsid)
7882 return E_INVALIDARG;
7884 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7887 /***********************************************************************
7888 * ReadClassStm (OLE32.@)
7890 * Reads a CLSID from a stream.
7892 * PARAMS
7893 * pStm [I] Stream to read from.
7894 * rclsid [O] CLSID to read.
7896 * RETURNS
7897 * Success: S_OK.
7898 * Failure: HRESULT code.
7900 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7902 ULONG nbByte;
7903 HRESULT res;
7905 TRACE("(%p,%p)\n",pStm,pclsid);
7907 if (!pStm || !pclsid)
7908 return E_INVALIDARG;
7910 /* clear the output args */
7911 memcpy(pclsid, &CLSID_NULL, sizeof(*pclsid));
7913 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7915 if (FAILED(res))
7916 return res;
7918 if (nbByte != sizeof(CLSID))
7919 return STG_E_READFAULT;
7920 else
7921 return S_OK;