msi: WriteEnvironmentStrings should also write to REG_EXPAND_SZ strings.
[wine/wine-kai.git] / dlls / ole32 / storage32.c
blob4bea1657b9450de876b35356e0c9c59670c735b9
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 StgStreamImpl *strm;
1020 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
1022 if (strm->ownerProperty == foundPropertyIndex)
1024 TRACE("Stream deleted %p\n", strm);
1025 strm->parentStorage = NULL;
1026 list_remove(&strm->StrmListEntry);
1029 IStorage_DestroyElement(iface, pwcsName);
1031 else
1032 return STG_E_FILEALREADYEXISTS;
1034 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1036 WARN("read-only storage\n");
1037 return STG_E_ACCESSDENIED;
1041 * memset the empty property
1043 memset(&newStreamProperty, 0, sizeof(StgProperty));
1045 newStreamProperty.sizeOfNameString =
1046 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1048 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1049 return STG_E_INVALIDNAME;
1051 strcpyW(newStreamProperty.name, pwcsName);
1053 newStreamProperty.propertyType = PROPTYPE_STREAM;
1054 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1055 newStreamProperty.size.u.LowPart = 0;
1056 newStreamProperty.size.u.HighPart = 0;
1058 newStreamProperty.previousProperty = PROPERTY_NULL;
1059 newStreamProperty.nextProperty = PROPERTY_NULL;
1060 newStreamProperty.dirProperty = PROPERTY_NULL;
1062 /* call CoFileTime to get the current time
1063 newStreamProperty.timeStampS1
1064 newStreamProperty.timeStampD1
1065 newStreamProperty.timeStampS2
1066 newStreamProperty.timeStampD2
1069 /* newStreamProperty.propertyUniqueID */
1072 * Get a free property or create a new one
1074 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1077 * Save the new property into the new property spot
1079 StorageImpl_WriteProperty(
1080 This->ancestorStorage,
1081 newPropertyIndex,
1082 &newStreamProperty);
1085 * Find a spot in the property chain for our newly created property.
1087 updatePropertyChain(
1088 (StorageImpl*)This,
1089 newPropertyIndex,
1090 newStreamProperty);
1093 * Open the stream to return it.
1095 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1097 if (newStream != 0)
1099 *ppstm = (IStream*)newStream;
1102 * Since we are returning a pointer to the interface, we have to nail down
1103 * the reference.
1105 IStream_AddRef(*ppstm);
1107 else
1109 return STG_E_INSUFFICIENTMEMORY;
1112 return S_OK;
1115 /************************************************************************
1116 * Storage32BaseImpl_SetClass (IStorage)
1118 * This method will write the specified CLSID in the property of this
1119 * storage.
1121 * See Windows documentation for more details on IStorage methods.
1123 static HRESULT WINAPI StorageBaseImpl_SetClass(
1124 IStorage* iface,
1125 REFCLSID clsid) /* [in] */
1127 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1128 HRESULT hRes = E_FAIL;
1129 StgProperty curProperty;
1130 BOOL success;
1132 TRACE("(%p, %p)\n", iface, clsid);
1134 success = StorageImpl_ReadProperty(This->ancestorStorage,
1135 This->rootPropertySetIndex,
1136 &curProperty);
1137 if (success)
1139 curProperty.propertyUniqueID = *clsid;
1141 success = StorageImpl_WriteProperty(This->ancestorStorage,
1142 This->rootPropertySetIndex,
1143 &curProperty);
1144 if (success)
1145 hRes = S_OK;
1148 return hRes;
1151 /************************************************************************
1152 ** Storage32Impl implementation
1155 /************************************************************************
1156 * Storage32Impl_CreateStorage (IStorage)
1158 * This method will create the storage object within the provided storage.
1160 * See Windows documentation for more details on IStorage methods.
1162 static HRESULT WINAPI StorageImpl_CreateStorage(
1163 IStorage* iface,
1164 const OLECHAR *pwcsName, /* [string][in] */
1165 DWORD grfMode, /* [in] */
1166 DWORD reserved1, /* [in] */
1167 DWORD reserved2, /* [in] */
1168 IStorage **ppstg) /* [out] */
1170 StorageImpl* const This=(StorageImpl*)iface;
1172 IEnumSTATSTGImpl *propertyEnumeration;
1173 StgProperty currentProperty;
1174 StgProperty newProperty;
1175 ULONG foundPropertyIndex;
1176 ULONG newPropertyIndex;
1177 HRESULT hr;
1179 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1180 iface, debugstr_w(pwcsName), grfMode,
1181 reserved1, reserved2, ppstg);
1184 * Validate parameters
1186 if (ppstg == 0)
1187 return STG_E_INVALIDPOINTER;
1189 if (pwcsName == 0)
1190 return STG_E_INVALIDNAME;
1193 * Initialize the out parameter
1195 *ppstg = NULL;
1198 * Validate the STGM flags
1200 if ( FAILED( validateSTGM(grfMode) ) ||
1201 (grfMode & STGM_DELETEONRELEASE) )
1203 WARN("bad grfMode: 0x%x\n", grfMode);
1204 return STG_E_INVALIDFLAG;
1208 * Check that we're compatible with the parent's storage mode
1210 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1212 WARN("access denied\n");
1213 return STG_E_ACCESSDENIED;
1217 * Create a property enumeration and search the properties
1219 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1220 This->base.rootPropertySetIndex);
1222 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1223 pwcsName,
1224 &currentProperty);
1225 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1227 if (foundPropertyIndex != PROPERTY_NULL)
1230 * An element with this name already exists
1232 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1233 IStorage_DestroyElement(iface, pwcsName);
1234 else
1236 WARN("file already exists\n");
1237 return STG_E_FILEALREADYEXISTS;
1240 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1242 WARN("read-only storage\n");
1243 return STG_E_ACCESSDENIED;
1247 * memset the empty property
1249 memset(&newProperty, 0, sizeof(StgProperty));
1251 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1253 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1255 FIXME("name too long\n");
1256 return STG_E_INVALIDNAME;
1259 strcpyW(newProperty.name, pwcsName);
1261 newProperty.propertyType = PROPTYPE_STORAGE;
1262 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1263 newProperty.size.u.LowPart = 0;
1264 newProperty.size.u.HighPart = 0;
1266 newProperty.previousProperty = PROPERTY_NULL;
1267 newProperty.nextProperty = PROPERTY_NULL;
1268 newProperty.dirProperty = PROPERTY_NULL;
1270 /* call CoFileTime to get the current time
1271 newProperty.timeStampS1
1272 newProperty.timeStampD1
1273 newProperty.timeStampS2
1274 newProperty.timeStampD2
1277 /* newStorageProperty.propertyUniqueID */
1280 * Obtain a free property in the property chain
1282 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1285 * Save the new property into the new property spot
1287 StorageImpl_WriteProperty(
1288 This->base.ancestorStorage,
1289 newPropertyIndex,
1290 &newProperty);
1293 * Find a spot in the property chain for our newly created property.
1295 updatePropertyChain(
1296 This,
1297 newPropertyIndex,
1298 newProperty);
1301 * Open it to get a pointer to return.
1303 hr = IStorage_OpenStorage(
1304 iface,
1305 (const OLECHAR*)pwcsName,
1307 grfMode,
1310 ppstg);
1312 if( (hr != S_OK) || (*ppstg == NULL))
1314 return hr;
1318 return S_OK;
1322 /***************************************************************************
1324 * Internal Method
1326 * Get a free property or create a new one.
1328 static ULONG getFreeProperty(
1329 StorageImpl *storage)
1331 ULONG currentPropertyIndex = 0;
1332 ULONG newPropertyIndex = PROPERTY_NULL;
1333 BOOL readSuccessful = TRUE;
1334 StgProperty currentProperty;
1339 * Start by reading the root property
1341 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1342 currentPropertyIndex,
1343 &currentProperty);
1344 if (readSuccessful)
1346 if (currentProperty.sizeOfNameString == 0)
1349 * The property existis and is available, we found it.
1351 newPropertyIndex = currentPropertyIndex;
1354 else
1357 * We exhausted the property list, we will create more space below
1359 newPropertyIndex = currentPropertyIndex;
1361 currentPropertyIndex++;
1363 } while (newPropertyIndex == PROPERTY_NULL);
1366 * grow the property chain
1368 if (! readSuccessful)
1370 StgProperty emptyProperty;
1371 ULARGE_INTEGER newSize;
1372 ULONG propertyIndex;
1373 ULONG lastProperty = 0;
1374 ULONG blockCount = 0;
1377 * obtain the new count of property blocks
1379 blockCount = BlockChainStream_GetCount(
1380 storage->base.ancestorStorage->rootBlockChain)+1;
1383 * initialize the size used by the property stream
1385 newSize.u.HighPart = 0;
1386 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1389 * add a property block to the property chain
1391 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1394 * memset the empty property in order to initialize the unused newly
1395 * created property
1397 memset(&emptyProperty, 0, sizeof(StgProperty));
1400 * initialize them
1402 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1404 for(
1405 propertyIndex = newPropertyIndex;
1406 propertyIndex < lastProperty;
1407 propertyIndex++)
1409 StorageImpl_WriteProperty(
1410 storage->base.ancestorStorage,
1411 propertyIndex,
1412 &emptyProperty);
1416 return newPropertyIndex;
1419 /****************************************************************************
1421 * Internal Method
1423 * Case insensitive comparaison of StgProperty.name by first considering
1424 * their size.
1426 * Returns <0 when newPrpoerty < currentProperty
1427 * >0 when newPrpoerty > currentProperty
1428 * 0 when newPrpoerty == currentProperty
1430 static LONG propertyNameCmp(
1431 const OLECHAR *newProperty,
1432 const OLECHAR *currentProperty)
1434 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1436 if (diff == 0)
1439 * We compare the string themselves only when they are of the same length
1441 diff = lstrcmpiW( newProperty, currentProperty);
1444 return diff;
1447 /****************************************************************************
1449 * Internal Method
1451 * Properly link this new element in the property chain.
1453 static void updatePropertyChain(
1454 StorageImpl *storage,
1455 ULONG newPropertyIndex,
1456 StgProperty newProperty)
1458 StgProperty currentProperty;
1461 * Read the root property
1463 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1464 storage->base.rootPropertySetIndex,
1465 &currentProperty);
1467 if (currentProperty.dirProperty != PROPERTY_NULL)
1470 * The root storage contains some element, therefore, start the research
1471 * for the appropriate location.
1473 BOOL found = 0;
1474 ULONG current, next, previous, currentPropertyId;
1477 * Keep the StgProperty sequence number of the storage first property
1479 currentPropertyId = currentProperty.dirProperty;
1482 * Read
1484 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1485 currentProperty.dirProperty,
1486 &currentProperty);
1488 previous = currentProperty.previousProperty;
1489 next = currentProperty.nextProperty;
1490 current = currentPropertyId;
1492 while (found == 0)
1494 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1496 if (diff < 0)
1498 if (previous != PROPERTY_NULL)
1500 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1501 previous,
1502 &currentProperty);
1503 current = previous;
1505 else
1507 currentProperty.previousProperty = newPropertyIndex;
1508 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1509 current,
1510 &currentProperty);
1511 found = 1;
1514 else if (diff > 0)
1516 if (next != PROPERTY_NULL)
1518 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1519 next,
1520 &currentProperty);
1521 current = next;
1523 else
1525 currentProperty.nextProperty = newPropertyIndex;
1526 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1527 current,
1528 &currentProperty);
1529 found = 1;
1532 else
1535 * Trying to insert an item with the same name in the
1536 * subtree structure.
1538 assert(FALSE);
1541 previous = currentProperty.previousProperty;
1542 next = currentProperty.nextProperty;
1545 else
1548 * The root storage is empty, link the new property to its dir property
1550 currentProperty.dirProperty = newPropertyIndex;
1551 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1552 storage->base.rootPropertySetIndex,
1553 &currentProperty);
1558 /*************************************************************************
1559 * CopyTo (IStorage)
1561 static HRESULT WINAPI StorageImpl_CopyTo(
1562 IStorage* iface,
1563 DWORD ciidExclude, /* [in] */
1564 const IID* rgiidExclude, /* [size_is][unique][in] */
1565 SNB snbExclude, /* [unique][in] */
1566 IStorage* pstgDest) /* [unique][in] */
1568 IEnumSTATSTG *elements = 0;
1569 STATSTG curElement, strStat;
1570 HRESULT hr;
1571 IStorage *pstgTmp, *pstgChild;
1572 IStream *pstrTmp, *pstrChild;
1574 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1575 FIXME("Exclude option not implemented\n");
1577 TRACE("(%p, %d, %p, %p, %p)\n",
1578 iface, ciidExclude, rgiidExclude,
1579 snbExclude, pstgDest);
1582 * Perform a sanity check
1584 if ( pstgDest == 0 )
1585 return STG_E_INVALIDPOINTER;
1588 * Enumerate the elements
1590 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1592 if ( hr != S_OK )
1593 return hr;
1596 * set the class ID
1598 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1599 IStorage_SetClass( pstgDest, &curElement.clsid );
1604 * Obtain the next element
1606 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1608 if ( hr == S_FALSE )
1610 hr = S_OK; /* done, every element has been copied */
1611 break;
1614 if (curElement.type == STGTY_STORAGE)
1617 * open child source storage
1619 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1620 STGM_READ|STGM_SHARE_EXCLUSIVE,
1621 NULL, 0, &pstgChild );
1623 if (hr != S_OK)
1624 break;
1627 * Check if destination storage is not a child of the source
1628 * storage, which will cause an infinite loop
1630 if (pstgChild == pstgDest)
1632 IEnumSTATSTG_Release(elements);
1634 return STG_E_ACCESSDENIED;
1638 * create a new storage in destination storage
1640 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1641 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1642 0, 0,
1643 &pstgTmp );
1645 * if it already exist, don't create a new one use this one
1647 if (hr == STG_E_FILEALREADYEXISTS)
1649 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1650 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1651 NULL, 0, &pstgTmp );
1654 if (hr != S_OK)
1655 break;
1659 * do the copy recursively
1661 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1662 snbExclude, pstgTmp );
1664 IStorage_Release( pstgTmp );
1665 IStorage_Release( pstgChild );
1667 else if (curElement.type == STGTY_STREAM)
1670 * create a new stream in destination storage. If the stream already
1671 * exist, it will be deleted and a new one will be created.
1673 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1674 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1675 0, 0, &pstrTmp );
1677 if (hr != S_OK)
1678 break;
1681 * open child stream storage
1683 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1684 STGM_READ|STGM_SHARE_EXCLUSIVE,
1685 0, &pstrChild );
1687 if (hr != S_OK)
1688 break;
1691 * Get the size of the source stream
1693 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1696 * Set the size of the destination stream.
1698 IStream_SetSize(pstrTmp, strStat.cbSize);
1701 * do the copy
1703 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1704 NULL, NULL );
1706 IStream_Release( pstrTmp );
1707 IStream_Release( pstrChild );
1709 else
1711 WARN("unknown element type: %d\n", curElement.type);
1714 } while (hr == S_OK);
1717 * Clean-up
1719 IEnumSTATSTG_Release(elements);
1721 return hr;
1724 /*************************************************************************
1725 * MoveElementTo (IStorage)
1727 static HRESULT WINAPI StorageImpl_MoveElementTo(
1728 IStorage* iface,
1729 const OLECHAR *pwcsName, /* [string][in] */
1730 IStorage *pstgDest, /* [unique][in] */
1731 const OLECHAR *pwcsNewName,/* [string][in] */
1732 DWORD grfFlags) /* [in] */
1734 FIXME("(%p %s %p %s %u): stub\n", iface,
1735 debugstr_w(pwcsName), pstgDest,
1736 debugstr_w(pwcsNewName), grfFlags);
1737 return E_NOTIMPL;
1740 /*************************************************************************
1741 * Commit (IStorage)
1743 * Ensures that any changes made to a storage object open in transacted mode
1744 * are reflected in the parent storage
1746 * NOTES
1747 * Wine doesn't implement transacted mode, which seems to be a basic
1748 * optimization, so we can ignore this stub for now.
1750 static HRESULT WINAPI StorageImpl_Commit(
1751 IStorage* iface,
1752 DWORD grfCommitFlags)/* [in] */
1754 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1755 return S_OK;
1758 /*************************************************************************
1759 * Revert (IStorage)
1761 * Discard all changes that have been made since the last commit operation
1763 static HRESULT WINAPI StorageImpl_Revert(
1764 IStorage* iface)
1766 FIXME("(%p): stub\n", iface);
1767 return E_NOTIMPL;
1770 /*************************************************************************
1771 * DestroyElement (IStorage)
1773 * Strategy: This implementation is built this way for simplicity not for speed.
1774 * I always delete the topmost element of the enumeration and adjust
1775 * the deleted element pointer all the time. This takes longer to
1776 * do but allow to reinvoke DestroyElement whenever we encounter a
1777 * storage object. The optimisation resides in the usage of another
1778 * enumeration strategy that would give all the leaves of a storage
1779 * first. (postfix order)
1781 static HRESULT WINAPI StorageImpl_DestroyElement(
1782 IStorage* iface,
1783 const OLECHAR *pwcsName)/* [string][in] */
1785 StorageImpl* const This=(StorageImpl*)iface;
1787 IEnumSTATSTGImpl* propertyEnumeration;
1788 HRESULT hr = S_OK;
1789 BOOL res;
1790 StgProperty propertyToDelete;
1791 StgProperty parentProperty;
1792 ULONG foundPropertyIndexToDelete;
1793 ULONG typeOfRelation;
1794 ULONG parentPropertyId = 0;
1796 TRACE("(%p, %s)\n",
1797 iface, debugstr_w(pwcsName));
1800 * Perform a sanity check on the parameters.
1802 if (pwcsName==NULL)
1803 return STG_E_INVALIDPOINTER;
1806 * Create a property enumeration to search the property with the given name
1808 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1809 This->base.ancestorStorage,
1810 This->base.rootPropertySetIndex);
1812 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1813 propertyEnumeration,
1814 pwcsName,
1815 &propertyToDelete);
1817 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1819 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1821 return STG_E_FILENOTFOUND;
1825 * Find the parent property of the property to delete (the one that
1826 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1827 * the parent is This. Otherwise, the parent is one of its sibling...
1831 * First, read This's StgProperty..
1833 res = StorageImpl_ReadProperty(
1834 This->base.ancestorStorage,
1835 This->base.rootPropertySetIndex,
1836 &parentProperty);
1838 assert(res);
1841 * Second, check to see if by any chance the actual storage (This) is not
1842 * the parent of the property to delete... We never know...
1844 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1847 * Set data as it would have been done in the else part...
1849 typeOfRelation = PROPERTY_RELATION_DIR;
1850 parentPropertyId = This->base.rootPropertySetIndex;
1852 else
1855 * Create a property enumeration to search the parent properties, and
1856 * delete it once done.
1858 IEnumSTATSTGImpl* propertyEnumeration2;
1860 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1861 This->base.ancestorStorage,
1862 This->base.rootPropertySetIndex);
1864 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1865 propertyEnumeration2,
1866 foundPropertyIndexToDelete,
1867 &parentProperty,
1868 &parentPropertyId);
1870 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1873 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1875 hr = deleteStorageProperty(
1876 This,
1877 foundPropertyIndexToDelete,
1878 propertyToDelete);
1880 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1882 hr = deleteStreamProperty(
1883 This,
1884 foundPropertyIndexToDelete,
1885 propertyToDelete);
1888 if (hr!=S_OK)
1889 return hr;
1892 * Adjust the property chain
1894 hr = adjustPropertyChain(
1895 This,
1896 propertyToDelete,
1897 parentProperty,
1898 parentPropertyId,
1899 typeOfRelation);
1901 return hr;
1905 /************************************************************************
1906 * StorageImpl_Stat (IStorage)
1908 * This method will retrieve information about this storage object.
1910 * See Windows documentation for more details on IStorage methods.
1912 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1913 STATSTG* pstatstg, /* [out] */
1914 DWORD grfStatFlag) /* [in] */
1916 StorageImpl* const This = (StorageImpl*)iface;
1917 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1919 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1921 CoTaskMemFree(pstatstg->pwcsName);
1922 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1923 strcpyW(pstatstg->pwcsName, This->pwcsName);
1926 return result;
1929 /******************************************************************************
1930 * Internal stream list handlers
1933 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1935 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1936 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1939 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1941 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1942 list_remove(&(strm->StrmListEntry));
1945 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1947 struct list *cur, *cur2;
1948 StgStreamImpl *strm=NULL;
1950 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1951 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1952 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1953 strm->parentStorage = NULL;
1954 list_remove(cur);
1959 /*********************************************************************
1961 * Internal Method
1963 * Perform the deletion of a complete storage node
1966 static HRESULT deleteStorageProperty(
1967 StorageImpl *parentStorage,
1968 ULONG indexOfPropertyToDelete,
1969 StgProperty propertyToDelete)
1971 IEnumSTATSTG *elements = 0;
1972 IStorage *childStorage = 0;
1973 STATSTG currentElement;
1974 HRESULT hr;
1975 HRESULT destroyHr = S_OK;
1978 * Open the storage and enumerate it
1980 hr = StorageBaseImpl_OpenStorage(
1981 (IStorage*)parentStorage,
1982 propertyToDelete.name,
1984 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1987 &childStorage);
1989 if (hr != S_OK)
1991 return hr;
1995 * Enumerate the elements
1997 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
2002 * Obtain the next element
2004 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
2005 if (hr==S_OK)
2007 destroyHr = StorageImpl_DestroyElement(
2008 (IStorage*)childStorage,
2009 (OLECHAR*)currentElement.pwcsName);
2011 CoTaskMemFree(currentElement.pwcsName);
2015 * We need to Reset the enumeration every time because we delete elements
2016 * and the enumeration could be invalid
2018 IEnumSTATSTG_Reset(elements);
2020 } while ((hr == S_OK) && (destroyHr == S_OK));
2023 * Invalidate the property by zeroing its name member.
2025 propertyToDelete.sizeOfNameString = 0;
2027 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2028 indexOfPropertyToDelete,
2029 &propertyToDelete);
2031 IStorage_Release(childStorage);
2032 IEnumSTATSTG_Release(elements);
2034 return destroyHr;
2037 /*********************************************************************
2039 * Internal Method
2041 * Perform the deletion of a stream node
2044 static HRESULT deleteStreamProperty(
2045 StorageImpl *parentStorage,
2046 ULONG indexOfPropertyToDelete,
2047 StgProperty propertyToDelete)
2049 IStream *pis;
2050 HRESULT hr;
2051 ULARGE_INTEGER size;
2053 size.u.HighPart = 0;
2054 size.u.LowPart = 0;
2056 hr = StorageBaseImpl_OpenStream(
2057 (IStorage*)parentStorage,
2058 (OLECHAR*)propertyToDelete.name,
2059 NULL,
2060 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2062 &pis);
2064 if (hr!=S_OK)
2066 return(hr);
2070 * Zap the stream
2072 hr = IStream_SetSize(pis, size);
2074 if(hr != S_OK)
2076 return hr;
2080 * Release the stream object.
2082 IStream_Release(pis);
2085 * Invalidate the property by zeroing its name member.
2087 propertyToDelete.sizeOfNameString = 0;
2090 * Here we should re-read the property so we get the updated pointer
2091 * but since we are here to zap it, I don't do it...
2093 StorageImpl_WriteProperty(
2094 parentStorage->base.ancestorStorage,
2095 indexOfPropertyToDelete,
2096 &propertyToDelete);
2098 return S_OK;
2101 /*********************************************************************
2103 * Internal Method
2105 * Finds a placeholder for the StgProperty within the Storage
2108 static HRESULT findPlaceholder(
2109 StorageImpl *storage,
2110 ULONG propertyIndexToStore,
2111 ULONG storePropertyIndex,
2112 INT typeOfRelation)
2114 StgProperty storeProperty;
2115 HRESULT hr = S_OK;
2116 BOOL res = TRUE;
2119 * Read the storage property
2121 res = StorageImpl_ReadProperty(
2122 storage->base.ancestorStorage,
2123 storePropertyIndex,
2124 &storeProperty);
2126 if(! res)
2128 return E_FAIL;
2131 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2133 if (storeProperty.previousProperty != PROPERTY_NULL)
2135 return findPlaceholder(
2136 storage,
2137 propertyIndexToStore,
2138 storeProperty.previousProperty,
2139 typeOfRelation);
2141 else
2143 storeProperty.previousProperty = propertyIndexToStore;
2146 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2148 if (storeProperty.nextProperty != PROPERTY_NULL)
2150 return findPlaceholder(
2151 storage,
2152 propertyIndexToStore,
2153 storeProperty.nextProperty,
2154 typeOfRelation);
2156 else
2158 storeProperty.nextProperty = propertyIndexToStore;
2161 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2163 if (storeProperty.dirProperty != PROPERTY_NULL)
2165 return findPlaceholder(
2166 storage,
2167 propertyIndexToStore,
2168 storeProperty.dirProperty,
2169 typeOfRelation);
2171 else
2173 storeProperty.dirProperty = propertyIndexToStore;
2177 hr = StorageImpl_WriteProperty(
2178 storage->base.ancestorStorage,
2179 storePropertyIndex,
2180 &storeProperty);
2182 if(! hr)
2184 return E_FAIL;
2187 return S_OK;
2190 /*************************************************************************
2192 * Internal Method
2194 * This method takes the previous and the next property link of a property
2195 * to be deleted and find them a place in the Storage.
2197 static HRESULT adjustPropertyChain(
2198 StorageImpl *This,
2199 StgProperty propertyToDelete,
2200 StgProperty parentProperty,
2201 ULONG parentPropertyId,
2202 INT typeOfRelation)
2204 ULONG newLinkProperty = PROPERTY_NULL;
2205 BOOL needToFindAPlaceholder = FALSE;
2206 ULONG storeNode = PROPERTY_NULL;
2207 ULONG toStoreNode = PROPERTY_NULL;
2208 INT relationType = 0;
2209 HRESULT hr = S_OK;
2210 BOOL res = TRUE;
2212 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2214 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2217 * Set the parent previous to the property to delete previous
2219 newLinkProperty = propertyToDelete.previousProperty;
2221 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2224 * We also need to find a storage for the other link, setup variables
2225 * to do this at the end...
2227 needToFindAPlaceholder = TRUE;
2228 storeNode = propertyToDelete.previousProperty;
2229 toStoreNode = propertyToDelete.nextProperty;
2230 relationType = PROPERTY_RELATION_NEXT;
2233 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2236 * Set the parent previous to the property to delete next
2238 newLinkProperty = propertyToDelete.nextProperty;
2242 * Link it for real...
2244 parentProperty.previousProperty = newLinkProperty;
2247 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2249 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2252 * Set the parent next to the property to delete next previous
2254 newLinkProperty = propertyToDelete.previousProperty;
2256 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2259 * We also need to find a storage for the other link, setup variables
2260 * to do this at the end...
2262 needToFindAPlaceholder = TRUE;
2263 storeNode = propertyToDelete.previousProperty;
2264 toStoreNode = propertyToDelete.nextProperty;
2265 relationType = PROPERTY_RELATION_NEXT;
2268 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2271 * Set the parent next to the property to delete next
2273 newLinkProperty = propertyToDelete.nextProperty;
2277 * Link it for real...
2279 parentProperty.nextProperty = newLinkProperty;
2281 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2283 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2286 * Set the parent dir to the property to delete previous
2288 newLinkProperty = propertyToDelete.previousProperty;
2290 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2293 * We also need to find a storage for the other link, setup variables
2294 * to do this at the end...
2296 needToFindAPlaceholder = TRUE;
2297 storeNode = propertyToDelete.previousProperty;
2298 toStoreNode = propertyToDelete.nextProperty;
2299 relationType = PROPERTY_RELATION_NEXT;
2302 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2305 * Set the parent dir to the property to delete next
2307 newLinkProperty = propertyToDelete.nextProperty;
2311 * Link it for real...
2313 parentProperty.dirProperty = newLinkProperty;
2317 * Write back the parent property
2319 res = StorageImpl_WriteProperty(
2320 This->base.ancestorStorage,
2321 parentPropertyId,
2322 &parentProperty);
2323 if(! res)
2325 return E_FAIL;
2329 * If a placeholder is required for the other link, then, find one and
2330 * get out of here...
2332 if (needToFindAPlaceholder)
2334 hr = findPlaceholder(
2335 This,
2336 toStoreNode,
2337 storeNode,
2338 relationType);
2341 return hr;
2345 /******************************************************************************
2346 * SetElementTimes (IStorage)
2348 static HRESULT WINAPI StorageImpl_SetElementTimes(
2349 IStorage* iface,
2350 const OLECHAR *pwcsName,/* [string][in] */
2351 const FILETIME *pctime, /* [in] */
2352 const FILETIME *patime, /* [in] */
2353 const FILETIME *pmtime) /* [in] */
2355 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2356 return S_OK;
2359 /******************************************************************************
2360 * SetStateBits (IStorage)
2362 static HRESULT WINAPI StorageImpl_SetStateBits(
2363 IStorage* iface,
2364 DWORD grfStateBits,/* [in] */
2365 DWORD grfMask) /* [in] */
2367 StorageImpl* const This = (StorageImpl*)iface;
2368 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2369 return S_OK;
2373 * Virtual function table for the IStorage32Impl class.
2375 static const IStorageVtbl Storage32Impl_Vtbl =
2377 StorageBaseImpl_QueryInterface,
2378 StorageBaseImpl_AddRef,
2379 StorageBaseImpl_Release,
2380 StorageBaseImpl_CreateStream,
2381 StorageBaseImpl_OpenStream,
2382 StorageImpl_CreateStorage,
2383 StorageBaseImpl_OpenStorage,
2384 StorageImpl_CopyTo,
2385 StorageImpl_MoveElementTo,
2386 StorageImpl_Commit,
2387 StorageImpl_Revert,
2388 StorageBaseImpl_EnumElements,
2389 StorageImpl_DestroyElement,
2390 StorageBaseImpl_RenameElement,
2391 StorageImpl_SetElementTimes,
2392 StorageBaseImpl_SetClass,
2393 StorageImpl_SetStateBits,
2394 StorageImpl_Stat
2397 static HRESULT StorageImpl_Construct(
2398 StorageImpl* This,
2399 HANDLE hFile,
2400 LPCOLESTR pwcsName,
2401 ILockBytes* pLkbyt,
2402 DWORD openFlags,
2403 BOOL fileBased,
2404 BOOL fileCreate)
2406 HRESULT hr = S_OK;
2407 StgProperty currentProperty;
2408 BOOL readSuccessful;
2409 ULONG currentPropertyIndex;
2411 if ( FAILED( validateSTGM(openFlags) ))
2412 return STG_E_INVALIDFLAG;
2414 memset(This, 0, sizeof(StorageImpl));
2417 * Initialize stream list
2420 list_init(&This->base.strmHead);
2423 * Initialize the virtual function table.
2425 This->base.lpVtbl = &Storage32Impl_Vtbl;
2426 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2427 This->base.v_destructor = &StorageImpl_Destroy;
2428 This->base.openFlags = (openFlags & ~STGM_CREATE);
2431 * This is the top-level storage so initialize the ancestor pointer
2432 * to this.
2434 This->base.ancestorStorage = This;
2437 * Initialize the physical support of the storage.
2439 This->hFile = hFile;
2442 * Store copy of file path.
2444 if(pwcsName) {
2445 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2446 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2447 if (!This->pwcsName)
2448 return STG_E_INSUFFICIENTMEMORY;
2449 strcpyW(This->pwcsName, pwcsName);
2453 * Initialize the big block cache.
2455 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2456 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2457 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2458 pLkbyt,
2459 openFlags,
2460 This->bigBlockSize,
2461 fileBased);
2463 if (This->bigBlockFile == 0)
2464 return E_FAIL;
2466 if (fileCreate)
2468 ULARGE_INTEGER size;
2469 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2472 * Initialize all header variables:
2473 * - The big block depot consists of one block and it is at block 0
2474 * - The properties start at block 1
2475 * - There is no small block depot
2477 memset( This->bigBlockDepotStart,
2478 BLOCK_UNUSED,
2479 sizeof(This->bigBlockDepotStart));
2481 This->bigBlockDepotCount = 1;
2482 This->bigBlockDepotStart[0] = 0;
2483 This->rootStartBlock = 1;
2484 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2485 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2486 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2487 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2488 This->extBigBlockDepotCount = 0;
2490 StorageImpl_SaveFileHeader(This);
2493 * Add one block for the big block depot and one block for the properties
2495 size.u.HighPart = 0;
2496 size.u.LowPart = This->bigBlockSize * 3;
2497 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2500 * Initialize the big block depot
2502 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2503 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2504 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2505 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2507 else
2510 * Load the header for the file.
2512 hr = StorageImpl_LoadFileHeader(This);
2514 if (FAILED(hr))
2516 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2518 return hr;
2523 * There is no block depot cached yet.
2525 This->indexBlockDepotCached = 0xFFFFFFFF;
2528 * Start searching for free blocks with block 0.
2530 This->prevFreeBlock = 0;
2533 * Create the block chain abstractions.
2535 if(!(This->rootBlockChain =
2536 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2537 return STG_E_READFAULT;
2539 if(!(This->smallBlockDepotChain =
2540 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2541 PROPERTY_NULL)))
2542 return STG_E_READFAULT;
2545 * Write the root property (memory only)
2547 if (fileCreate)
2549 StgProperty rootProp;
2551 * Initialize the property chain
2553 memset(&rootProp, 0, sizeof(rootProp));
2554 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2555 sizeof(rootProp.name)/sizeof(WCHAR) );
2556 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2557 rootProp.propertyType = PROPTYPE_ROOT;
2558 rootProp.previousProperty = PROPERTY_NULL;
2559 rootProp.nextProperty = PROPERTY_NULL;
2560 rootProp.dirProperty = PROPERTY_NULL;
2561 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2562 rootProp.size.u.HighPart = 0;
2563 rootProp.size.u.LowPart = 0;
2565 StorageImpl_WriteProperty(This, 0, &rootProp);
2569 * Find the ID of the root in the property sets.
2571 currentPropertyIndex = 0;
2575 readSuccessful = StorageImpl_ReadProperty(
2576 This,
2577 currentPropertyIndex,
2578 &currentProperty);
2580 if (readSuccessful)
2582 if ( (currentProperty.sizeOfNameString != 0 ) &&
2583 (currentProperty.propertyType == PROPTYPE_ROOT) )
2585 This->base.rootPropertySetIndex = currentPropertyIndex;
2589 currentPropertyIndex++;
2591 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2593 if (!readSuccessful)
2595 /* TODO CLEANUP */
2596 return STG_E_READFAULT;
2600 * Create the block chain abstraction for the small block root chain.
2602 if(!(This->smallBlockRootChain =
2603 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2604 return STG_E_READFAULT;
2606 return hr;
2609 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2611 StorageImpl *This = (StorageImpl*) iface;
2612 TRACE("(%p)\n", This);
2614 StorageBaseImpl_DeleteAll(&This->base);
2616 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2618 BlockChainStream_Destroy(This->smallBlockRootChain);
2619 BlockChainStream_Destroy(This->rootBlockChain);
2620 BlockChainStream_Destroy(This->smallBlockDepotChain);
2622 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2623 HeapFree(GetProcessHeap(), 0, This);
2626 /******************************************************************************
2627 * Storage32Impl_GetNextFreeBigBlock
2629 * Returns the index of the next free big block.
2630 * If the big block depot is filled, this method will enlarge it.
2633 static ULONG StorageImpl_GetNextFreeBigBlock(
2634 StorageImpl* This)
2636 ULONG depotBlockIndexPos;
2637 BYTE depotBuffer[BIG_BLOCK_SIZE];
2638 BOOL success;
2639 ULONG depotBlockOffset;
2640 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2641 ULONG nextBlockIndex = BLOCK_SPECIAL;
2642 int depotIndex = 0;
2643 ULONG freeBlock = BLOCK_UNUSED;
2645 depotIndex = This->prevFreeBlock / blocksPerDepot;
2646 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2649 * Scan the entire big block depot until we find a block marked free
2651 while (nextBlockIndex != BLOCK_UNUSED)
2653 if (depotIndex < COUNT_BBDEPOTINHEADER)
2655 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2658 * Grow the primary depot.
2660 if (depotBlockIndexPos == BLOCK_UNUSED)
2662 depotBlockIndexPos = depotIndex*blocksPerDepot;
2665 * Add a block depot.
2667 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2668 This->bigBlockDepotCount++;
2669 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2672 * Flag it as a block depot.
2674 StorageImpl_SetNextBlockInChain(This,
2675 depotBlockIndexPos,
2676 BLOCK_SPECIAL);
2678 /* Save new header information.
2680 StorageImpl_SaveFileHeader(This);
2683 else
2685 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2687 if (depotBlockIndexPos == BLOCK_UNUSED)
2690 * Grow the extended depot.
2692 ULONG extIndex = BLOCK_UNUSED;
2693 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2694 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2696 if (extBlockOffset == 0)
2698 /* We need an extended block.
2700 extIndex = Storage32Impl_AddExtBlockDepot(This);
2701 This->extBigBlockDepotCount++;
2702 depotBlockIndexPos = extIndex + 1;
2704 else
2705 depotBlockIndexPos = depotIndex * blocksPerDepot;
2708 * Add a block depot and mark it in the extended block.
2710 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2711 This->bigBlockDepotCount++;
2712 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2714 /* Flag the block depot.
2716 StorageImpl_SetNextBlockInChain(This,
2717 depotBlockIndexPos,
2718 BLOCK_SPECIAL);
2720 /* If necessary, flag the extended depot block.
2722 if (extIndex != BLOCK_UNUSED)
2723 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2725 /* Save header information.
2727 StorageImpl_SaveFileHeader(This);
2731 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2733 if (success)
2735 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2736 ( nextBlockIndex != BLOCK_UNUSED))
2738 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2740 if (nextBlockIndex == BLOCK_UNUSED)
2742 freeBlock = (depotIndex * blocksPerDepot) +
2743 (depotBlockOffset/sizeof(ULONG));
2746 depotBlockOffset += sizeof(ULONG);
2750 depotIndex++;
2751 depotBlockOffset = 0;
2755 * make sure that the block physically exists before using it
2757 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2759 This->prevFreeBlock = freeBlock;
2761 return freeBlock;
2764 /******************************************************************************
2765 * Storage32Impl_AddBlockDepot
2767 * This will create a depot block, essentially it is a block initialized
2768 * to BLOCK_UNUSEDs.
2770 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2772 BYTE blockBuffer[BIG_BLOCK_SIZE];
2775 * Initialize blocks as free
2777 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2778 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2781 /******************************************************************************
2782 * Storage32Impl_GetExtDepotBlock
2784 * Returns the index of the block that corresponds to the specified depot
2785 * index. This method is only for depot indexes equal or greater than
2786 * COUNT_BBDEPOTINHEADER.
2788 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2790 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2791 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2792 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2793 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2794 ULONG blockIndex = BLOCK_UNUSED;
2795 ULONG extBlockIndex = This->extBigBlockDepotStart;
2797 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2799 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2800 return BLOCK_UNUSED;
2802 while (extBlockCount > 0)
2804 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2805 extBlockCount--;
2808 if (extBlockIndex != BLOCK_UNUSED)
2809 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2810 extBlockOffset * sizeof(ULONG), &blockIndex);
2812 return blockIndex;
2815 /******************************************************************************
2816 * Storage32Impl_SetExtDepotBlock
2818 * Associates the specified block index to the specified depot index.
2819 * This method is only for depot indexes equal or greater than
2820 * COUNT_BBDEPOTINHEADER.
2822 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2824 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2825 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2826 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2827 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2828 ULONG extBlockIndex = This->extBigBlockDepotStart;
2830 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2832 while (extBlockCount > 0)
2834 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2835 extBlockCount--;
2838 if (extBlockIndex != BLOCK_UNUSED)
2840 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2841 extBlockOffset * sizeof(ULONG),
2842 blockIndex);
2846 /******************************************************************************
2847 * Storage32Impl_AddExtBlockDepot
2849 * Creates an extended depot block.
2851 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2853 ULONG numExtBlocks = This->extBigBlockDepotCount;
2854 ULONG nextExtBlock = This->extBigBlockDepotStart;
2855 BYTE depotBuffer[BIG_BLOCK_SIZE];
2856 ULONG index = BLOCK_UNUSED;
2857 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2858 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2859 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2861 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2862 blocksPerDepotBlock;
2864 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2867 * The first extended block.
2869 This->extBigBlockDepotStart = index;
2871 else
2873 unsigned int i;
2875 * Follow the chain to the last one.
2877 for (i = 0; i < (numExtBlocks - 1); i++)
2879 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2883 * Add the new extended block to the chain.
2885 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2886 index);
2890 * Initialize this block.
2892 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2893 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2895 return index;
2898 /******************************************************************************
2899 * Storage32Impl_FreeBigBlock
2901 * This method will flag the specified block as free in the big block depot.
2903 static void StorageImpl_FreeBigBlock(
2904 StorageImpl* This,
2905 ULONG blockIndex)
2907 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2909 if (blockIndex < This->prevFreeBlock)
2910 This->prevFreeBlock = blockIndex;
2913 /************************************************************************
2914 * Storage32Impl_GetNextBlockInChain
2916 * This method will retrieve the block index of the next big block in
2917 * in the chain.
2919 * Params: This - Pointer to the Storage object.
2920 * blockIndex - Index of the block to retrieve the chain
2921 * for.
2922 * nextBlockIndex - receives the return value.
2924 * Returns: This method returns the index of the next block in the chain.
2925 * It will return the constants:
2926 * BLOCK_SPECIAL - If the block given was not part of a
2927 * chain.
2928 * BLOCK_END_OF_CHAIN - If the block given was the last in
2929 * a chain.
2930 * BLOCK_UNUSED - If the block given was not past of a chain
2931 * and is available.
2932 * BLOCK_EXTBBDEPOT - This block is part of the extended
2933 * big block depot.
2935 * See Windows documentation for more details on IStorage methods.
2937 static HRESULT StorageImpl_GetNextBlockInChain(
2938 StorageImpl* This,
2939 ULONG blockIndex,
2940 ULONG* nextBlockIndex)
2942 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2943 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2944 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2945 BYTE depotBuffer[BIG_BLOCK_SIZE];
2946 BOOL success;
2947 ULONG depotBlockIndexPos;
2948 int index;
2950 *nextBlockIndex = BLOCK_SPECIAL;
2952 if(depotBlockCount >= This->bigBlockDepotCount)
2954 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2955 This->bigBlockDepotCount);
2956 return STG_E_READFAULT;
2960 * Cache the currently accessed depot block.
2962 if (depotBlockCount != This->indexBlockDepotCached)
2964 This->indexBlockDepotCached = depotBlockCount;
2966 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2968 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2970 else
2973 * We have to look in the extended depot.
2975 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2978 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2980 if (!success)
2981 return STG_E_READFAULT;
2983 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2985 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2986 This->blockDepotCached[index] = *nextBlockIndex;
2990 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2992 return S_OK;
2995 /******************************************************************************
2996 * Storage32Impl_GetNextExtendedBlock
2998 * Given an extended block this method will return the next extended block.
3000 * NOTES:
3001 * The last ULONG of an extended block is the block index of the next
3002 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
3003 * depot.
3005 * Return values:
3006 * - The index of the next extended block
3007 * - BLOCK_UNUSED: there is no next extended block.
3008 * - Any other return values denotes failure.
3010 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3012 ULONG nextBlockIndex = BLOCK_SPECIAL;
3013 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3015 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3016 &nextBlockIndex);
3018 return nextBlockIndex;
3021 /******************************************************************************
3022 * Storage32Impl_SetNextBlockInChain
3024 * This method will write the index of the specified block's next block
3025 * in the big block depot.
3027 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3028 * do the following
3030 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3031 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3032 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3035 static void StorageImpl_SetNextBlockInChain(
3036 StorageImpl* This,
3037 ULONG blockIndex,
3038 ULONG nextBlock)
3040 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3041 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3042 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3043 ULONG depotBlockIndexPos;
3045 assert(depotBlockCount < This->bigBlockDepotCount);
3046 assert(blockIndex != nextBlock);
3048 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3050 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3052 else
3055 * We have to look in the extended depot.
3057 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3060 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3061 nextBlock);
3063 * Update the cached block depot, if necessary.
3065 if (depotBlockCount == This->indexBlockDepotCached)
3067 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3071 /******************************************************************************
3072 * Storage32Impl_LoadFileHeader
3074 * This method will read in the file header, i.e. big block index -1.
3076 static HRESULT StorageImpl_LoadFileHeader(
3077 StorageImpl* This)
3079 HRESULT hr = STG_E_FILENOTFOUND;
3080 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3081 BOOL success;
3082 int index;
3084 TRACE("\n");
3086 * Get a pointer to the big block of data containing the header.
3088 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3091 * Extract the information from the header.
3093 if (success)
3096 * Check for the "magic number" signature and return an error if it is not
3097 * found.
3099 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3101 return STG_E_OLDFORMAT;
3104 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3106 return STG_E_INVALIDHEADER;
3109 StorageUtl_ReadWord(
3110 headerBigBlock,
3111 OFFSET_BIGBLOCKSIZEBITS,
3112 &This->bigBlockSizeBits);
3114 StorageUtl_ReadWord(
3115 headerBigBlock,
3116 OFFSET_SMALLBLOCKSIZEBITS,
3117 &This->smallBlockSizeBits);
3119 StorageUtl_ReadDWord(
3120 headerBigBlock,
3121 OFFSET_BBDEPOTCOUNT,
3122 &This->bigBlockDepotCount);
3124 StorageUtl_ReadDWord(
3125 headerBigBlock,
3126 OFFSET_ROOTSTARTBLOCK,
3127 &This->rootStartBlock);
3129 StorageUtl_ReadDWord(
3130 headerBigBlock,
3131 OFFSET_SBDEPOTSTART,
3132 &This->smallBlockDepotStart);
3134 StorageUtl_ReadDWord(
3135 headerBigBlock,
3136 OFFSET_EXTBBDEPOTSTART,
3137 &This->extBigBlockDepotStart);
3139 StorageUtl_ReadDWord(
3140 headerBigBlock,
3141 OFFSET_EXTBBDEPOTCOUNT,
3142 &This->extBigBlockDepotCount);
3144 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3146 StorageUtl_ReadDWord(
3147 headerBigBlock,
3148 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3149 &(This->bigBlockDepotStart[index]));
3153 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3155 if ((1 << 2) == 4)
3157 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3158 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3160 else
3162 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3163 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3167 * Right now, the code is making some assumptions about the size of the
3168 * blocks, just make sure they are what we're expecting.
3170 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3171 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3173 WARN("Broken OLE storage file\n");
3174 hr = STG_E_INVALIDHEADER;
3176 else
3177 hr = S_OK;
3180 return hr;
3183 /******************************************************************************
3184 * Storage32Impl_SaveFileHeader
3186 * This method will save to the file the header, i.e. big block -1.
3188 static void StorageImpl_SaveFileHeader(
3189 StorageImpl* This)
3191 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3192 int index;
3193 BOOL success;
3196 * Get a pointer to the big block of data containing the header.
3198 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3201 * If the block read failed, the file is probably new.
3203 if (!success)
3206 * Initialize for all unknown fields.
3208 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3211 * Initialize the magic number.
3213 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3216 * And a bunch of things we don't know what they mean
3218 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3219 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3220 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3221 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3225 * Write the information to the header.
3227 StorageUtl_WriteWord(
3228 headerBigBlock,
3229 OFFSET_BIGBLOCKSIZEBITS,
3230 This->bigBlockSizeBits);
3232 StorageUtl_WriteWord(
3233 headerBigBlock,
3234 OFFSET_SMALLBLOCKSIZEBITS,
3235 This->smallBlockSizeBits);
3237 StorageUtl_WriteDWord(
3238 headerBigBlock,
3239 OFFSET_BBDEPOTCOUNT,
3240 This->bigBlockDepotCount);
3242 StorageUtl_WriteDWord(
3243 headerBigBlock,
3244 OFFSET_ROOTSTARTBLOCK,
3245 This->rootStartBlock);
3247 StorageUtl_WriteDWord(
3248 headerBigBlock,
3249 OFFSET_SBDEPOTSTART,
3250 This->smallBlockDepotStart);
3252 StorageUtl_WriteDWord(
3253 headerBigBlock,
3254 OFFSET_SBDEPOTCOUNT,
3255 This->smallBlockDepotChain ?
3256 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3258 StorageUtl_WriteDWord(
3259 headerBigBlock,
3260 OFFSET_EXTBBDEPOTSTART,
3261 This->extBigBlockDepotStart);
3263 StorageUtl_WriteDWord(
3264 headerBigBlock,
3265 OFFSET_EXTBBDEPOTCOUNT,
3266 This->extBigBlockDepotCount);
3268 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3270 StorageUtl_WriteDWord(
3271 headerBigBlock,
3272 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3273 (This->bigBlockDepotStart[index]));
3277 * Write the big block back to the file.
3279 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3282 /******************************************************************************
3283 * Storage32Impl_ReadProperty
3285 * This method will read the specified property from the property chain.
3287 BOOL StorageImpl_ReadProperty(
3288 StorageImpl* This,
3289 ULONG index,
3290 StgProperty* buffer)
3292 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3293 ULARGE_INTEGER offsetInPropSet;
3294 HRESULT readRes;
3295 ULONG bytesRead;
3297 offsetInPropSet.u.HighPart = 0;
3298 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3300 readRes = BlockChainStream_ReadAt(
3301 This->rootBlockChain,
3302 offsetInPropSet,
3303 PROPSET_BLOCK_SIZE,
3304 currentProperty,
3305 &bytesRead);
3307 if (SUCCEEDED(readRes))
3309 /* replace the name of root entry (often "Root Entry") by the file name */
3310 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3311 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3313 memset(buffer->name, 0, sizeof(buffer->name));
3314 memcpy(
3315 buffer->name,
3316 propName,
3317 PROPERTY_NAME_BUFFER_LEN );
3318 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3320 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3322 StorageUtl_ReadWord(
3323 currentProperty,
3324 OFFSET_PS_NAMELENGTH,
3325 &buffer->sizeOfNameString);
3327 StorageUtl_ReadDWord(
3328 currentProperty,
3329 OFFSET_PS_PREVIOUSPROP,
3330 &buffer->previousProperty);
3332 StorageUtl_ReadDWord(
3333 currentProperty,
3334 OFFSET_PS_NEXTPROP,
3335 &buffer->nextProperty);
3337 StorageUtl_ReadDWord(
3338 currentProperty,
3339 OFFSET_PS_DIRPROP,
3340 &buffer->dirProperty);
3342 StorageUtl_ReadGUID(
3343 currentProperty,
3344 OFFSET_PS_GUID,
3345 &buffer->propertyUniqueID);
3347 StorageUtl_ReadDWord(
3348 currentProperty,
3349 OFFSET_PS_TSS1,
3350 &buffer->timeStampS1);
3352 StorageUtl_ReadDWord(
3353 currentProperty,
3354 OFFSET_PS_TSD1,
3355 &buffer->timeStampD1);
3357 StorageUtl_ReadDWord(
3358 currentProperty,
3359 OFFSET_PS_TSS2,
3360 &buffer->timeStampS2);
3362 StorageUtl_ReadDWord(
3363 currentProperty,
3364 OFFSET_PS_TSD2,
3365 &buffer->timeStampD2);
3367 StorageUtl_ReadDWord(
3368 currentProperty,
3369 OFFSET_PS_STARTBLOCK,
3370 &buffer->startingBlock);
3372 StorageUtl_ReadDWord(
3373 currentProperty,
3374 OFFSET_PS_SIZE,
3375 &buffer->size.u.LowPart);
3377 buffer->size.u.HighPart = 0;
3380 return SUCCEEDED(readRes) ? TRUE : FALSE;
3383 /*********************************************************************
3384 * Write the specified property into the property chain
3386 BOOL StorageImpl_WriteProperty(
3387 StorageImpl* This,
3388 ULONG index,
3389 const StgProperty* buffer)
3391 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3392 ULARGE_INTEGER offsetInPropSet;
3393 HRESULT writeRes;
3394 ULONG bytesWritten;
3396 offsetInPropSet.u.HighPart = 0;
3397 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3399 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3401 memcpy(
3402 currentProperty + OFFSET_PS_NAME,
3403 buffer->name,
3404 PROPERTY_NAME_BUFFER_LEN );
3406 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3408 StorageUtl_WriteWord(
3409 currentProperty,
3410 OFFSET_PS_NAMELENGTH,
3411 buffer->sizeOfNameString);
3413 StorageUtl_WriteDWord(
3414 currentProperty,
3415 OFFSET_PS_PREVIOUSPROP,
3416 buffer->previousProperty);
3418 StorageUtl_WriteDWord(
3419 currentProperty,
3420 OFFSET_PS_NEXTPROP,
3421 buffer->nextProperty);
3423 StorageUtl_WriteDWord(
3424 currentProperty,
3425 OFFSET_PS_DIRPROP,
3426 buffer->dirProperty);
3428 StorageUtl_WriteGUID(
3429 currentProperty,
3430 OFFSET_PS_GUID,
3431 &buffer->propertyUniqueID);
3433 StorageUtl_WriteDWord(
3434 currentProperty,
3435 OFFSET_PS_TSS1,
3436 buffer->timeStampS1);
3438 StorageUtl_WriteDWord(
3439 currentProperty,
3440 OFFSET_PS_TSD1,
3441 buffer->timeStampD1);
3443 StorageUtl_WriteDWord(
3444 currentProperty,
3445 OFFSET_PS_TSS2,
3446 buffer->timeStampS2);
3448 StorageUtl_WriteDWord(
3449 currentProperty,
3450 OFFSET_PS_TSD2,
3451 buffer->timeStampD2);
3453 StorageUtl_WriteDWord(
3454 currentProperty,
3455 OFFSET_PS_STARTBLOCK,
3456 buffer->startingBlock);
3458 StorageUtl_WriteDWord(
3459 currentProperty,
3460 OFFSET_PS_SIZE,
3461 buffer->size.u.LowPart);
3463 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3464 offsetInPropSet,
3465 PROPSET_BLOCK_SIZE,
3466 currentProperty,
3467 &bytesWritten);
3468 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3471 static BOOL StorageImpl_ReadBigBlock(
3472 StorageImpl* This,
3473 ULONG blockIndex,
3474 void* buffer)
3476 ULARGE_INTEGER ulOffset;
3477 DWORD read;
3479 ulOffset.u.HighPart = 0;
3480 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3482 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3483 return (read == This->bigBlockSize);
3486 static BOOL StorageImpl_ReadDWordFromBigBlock(
3487 StorageImpl* This,
3488 ULONG blockIndex,
3489 ULONG offset,
3490 DWORD* value)
3492 ULARGE_INTEGER ulOffset;
3493 DWORD read;
3494 DWORD tmp;
3496 ulOffset.u.HighPart = 0;
3497 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3498 ulOffset.u.LowPart += offset;
3500 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3501 *value = le32toh(tmp);
3502 return (read == sizeof(DWORD));
3505 static BOOL StorageImpl_WriteBigBlock(
3506 StorageImpl* This,
3507 ULONG blockIndex,
3508 const void* buffer)
3510 ULARGE_INTEGER ulOffset;
3511 DWORD wrote;
3513 ulOffset.u.HighPart = 0;
3514 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3516 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3517 return (wrote == This->bigBlockSize);
3520 static BOOL StorageImpl_WriteDWordToBigBlock(
3521 StorageImpl* This,
3522 ULONG blockIndex,
3523 ULONG offset,
3524 DWORD value)
3526 ULARGE_INTEGER ulOffset;
3527 DWORD wrote;
3529 ulOffset.u.HighPart = 0;
3530 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3531 ulOffset.u.LowPart += offset;
3533 value = htole32(value);
3534 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3535 return (wrote == sizeof(DWORD));
3538 /******************************************************************************
3539 * Storage32Impl_SmallBlocksToBigBlocks
3541 * This method will convert a small block chain to a big block chain.
3542 * The small block chain will be destroyed.
3544 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3545 StorageImpl* This,
3546 SmallBlockChainStream** ppsbChain)
3548 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3549 ULARGE_INTEGER size, offset;
3550 ULONG cbRead, cbWritten;
3551 ULARGE_INTEGER cbTotalRead;
3552 ULONG propertyIndex;
3553 HRESULT resWrite = S_OK;
3554 HRESULT resRead;
3555 StgProperty chainProperty;
3556 BYTE *buffer;
3557 BlockChainStream *bbTempChain = NULL;
3558 BlockChainStream *bigBlockChain = NULL;
3561 * Create a temporary big block chain that doesn't have
3562 * an associated property. This temporary chain will be
3563 * used to copy data from small blocks to big blocks.
3565 bbTempChain = BlockChainStream_Construct(This,
3566 &bbHeadOfChain,
3567 PROPERTY_NULL);
3568 if(!bbTempChain) return NULL;
3570 * Grow the big block chain.
3572 size = SmallBlockChainStream_GetSize(*ppsbChain);
3573 BlockChainStream_SetSize(bbTempChain, size);
3576 * Copy the contents of the small block chain to the big block chain
3577 * by small block size increments.
3579 offset.u.LowPart = 0;
3580 offset.u.HighPart = 0;
3581 cbTotalRead.QuadPart = 0;
3583 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3586 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3587 offset,
3588 This->smallBlockSize,
3589 buffer,
3590 &cbRead);
3591 if (FAILED(resRead))
3592 break;
3594 if (cbRead > 0)
3596 cbTotalRead.QuadPart += cbRead;
3598 resWrite = BlockChainStream_WriteAt(bbTempChain,
3599 offset,
3600 cbRead,
3601 buffer,
3602 &cbWritten);
3604 if (FAILED(resWrite))
3605 break;
3607 offset.u.LowPart += This->smallBlockSize;
3609 } while (cbTotalRead.QuadPart < size.QuadPart);
3610 HeapFree(GetProcessHeap(),0,buffer);
3612 if (FAILED(resRead) || FAILED(resWrite))
3614 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3615 BlockChainStream_Destroy(bbTempChain);
3616 return NULL;
3620 * Destroy the small block chain.
3622 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3623 size.u.HighPart = 0;
3624 size.u.LowPart = 0;
3625 SmallBlockChainStream_SetSize(*ppsbChain, size);
3626 SmallBlockChainStream_Destroy(*ppsbChain);
3627 *ppsbChain = 0;
3630 * Change the property information. This chain is now a big block chain
3631 * and it doesn't reside in the small blocks chain anymore.
3633 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3635 chainProperty.startingBlock = bbHeadOfChain;
3637 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3640 * Destroy the temporary propertyless big block chain.
3641 * Create a new big block chain associated with this property.
3643 BlockChainStream_Destroy(bbTempChain);
3644 bigBlockChain = BlockChainStream_Construct(This,
3645 NULL,
3646 propertyIndex);
3648 return bigBlockChain;
3651 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3653 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3655 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3656 HeapFree(GetProcessHeap(), 0, This);
3659 /******************************************************************************
3661 ** Storage32InternalImpl_Commit
3663 ** The non-root storages cannot be opened in transacted mode thus this function
3664 ** does nothing.
3666 static HRESULT WINAPI StorageInternalImpl_Commit(
3667 IStorage* iface,
3668 DWORD grfCommitFlags) /* [in] */
3670 return S_OK;
3673 /******************************************************************************
3675 ** Storage32InternalImpl_Revert
3677 ** The non-root storages cannot be opened in transacted mode thus this function
3678 ** does nothing.
3680 static HRESULT WINAPI StorageInternalImpl_Revert(
3681 IStorage* iface)
3683 return S_OK;
3686 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3688 IStorage_Release((IStorage*)This->parentStorage);
3689 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3690 HeapFree(GetProcessHeap(), 0, This);
3693 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3694 IEnumSTATSTG* iface,
3695 REFIID riid,
3696 void** ppvObject)
3698 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3701 * Perform a sanity check on the parameters.
3703 if (ppvObject==0)
3704 return E_INVALIDARG;
3707 * Initialize the return parameter.
3709 *ppvObject = 0;
3712 * Compare the riid with the interface IDs implemented by this object.
3714 if (IsEqualGUID(&IID_IUnknown, riid) ||
3715 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3717 *ppvObject = (IEnumSTATSTG*)This;
3718 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3719 return S_OK;
3722 return E_NOINTERFACE;
3725 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3726 IEnumSTATSTG* iface)
3728 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3729 return InterlockedIncrement(&This->ref);
3732 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3733 IEnumSTATSTG* iface)
3735 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3737 ULONG newRef;
3739 newRef = InterlockedDecrement(&This->ref);
3742 * If the reference count goes down to 0, perform suicide.
3744 if (newRef==0)
3746 IEnumSTATSTGImpl_Destroy(This);
3749 return newRef;
3752 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3753 IEnumSTATSTG* iface,
3754 ULONG celt,
3755 STATSTG* rgelt,
3756 ULONG* pceltFetched)
3758 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3760 StgProperty currentProperty;
3761 STATSTG* currentReturnStruct = rgelt;
3762 ULONG objectFetched = 0;
3763 ULONG currentSearchNode;
3766 * Perform a sanity check on the parameters.
3768 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3769 return E_INVALIDARG;
3772 * To avoid the special case, get another pointer to a ULONG value if
3773 * the caller didn't supply one.
3775 if (pceltFetched==0)
3776 pceltFetched = &objectFetched;
3779 * Start the iteration, we will iterate until we hit the end of the
3780 * linked list or until we hit the number of items to iterate through
3782 *pceltFetched = 0;
3785 * Start with the node at the top of the stack.
3787 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3789 while ( ( *pceltFetched < celt) &&
3790 ( currentSearchNode!=PROPERTY_NULL) )
3793 * Remove the top node from the stack
3795 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3798 * Read the property from the storage.
3800 StorageImpl_ReadProperty(This->parentStorage,
3801 currentSearchNode,
3802 &currentProperty);
3805 * Copy the information to the return buffer.
3807 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3808 &currentProperty,
3809 STATFLAG_DEFAULT);
3812 * Step to the next item in the iteration
3814 (*pceltFetched)++;
3815 currentReturnStruct++;
3818 * Push the next search node in the search stack.
3820 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3823 * continue the iteration.
3825 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3828 if (*pceltFetched == celt)
3829 return S_OK;
3831 return S_FALSE;
3835 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3836 IEnumSTATSTG* iface,
3837 ULONG celt)
3839 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3841 StgProperty currentProperty;
3842 ULONG objectFetched = 0;
3843 ULONG currentSearchNode;
3846 * Start with the node at the top of the stack.
3848 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3850 while ( (objectFetched < celt) &&
3851 (currentSearchNode!=PROPERTY_NULL) )
3854 * Remove the top node from the stack
3856 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3859 * Read the property from the storage.
3861 StorageImpl_ReadProperty(This->parentStorage,
3862 currentSearchNode,
3863 &currentProperty);
3866 * Step to the next item in the iteration
3868 objectFetched++;
3871 * Push the next search node in the search stack.
3873 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3876 * continue the iteration.
3878 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3881 if (objectFetched == celt)
3882 return S_OK;
3884 return S_FALSE;
3887 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3888 IEnumSTATSTG* iface)
3890 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3892 StgProperty rootProperty;
3893 BOOL readSuccessful;
3896 * Re-initialize the search stack to an empty stack
3898 This->stackSize = 0;
3901 * Read the root property from the storage.
3903 readSuccessful = StorageImpl_ReadProperty(
3904 This->parentStorage,
3905 This->firstPropertyNode,
3906 &rootProperty);
3908 if (readSuccessful)
3910 assert(rootProperty.sizeOfNameString!=0);
3913 * Push the search node in the search stack.
3915 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3918 return S_OK;
3921 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3922 IEnumSTATSTG* iface,
3923 IEnumSTATSTG** ppenum)
3925 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3927 IEnumSTATSTGImpl* newClone;
3930 * Perform a sanity check on the parameters.
3932 if (ppenum==0)
3933 return E_INVALIDARG;
3935 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3936 This->firstPropertyNode);
3940 * The new clone enumeration must point to the same current node as
3941 * the ole one.
3943 newClone->stackSize = This->stackSize ;
3944 newClone->stackMaxSize = This->stackMaxSize ;
3945 newClone->stackToVisit =
3946 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3948 memcpy(
3949 newClone->stackToVisit,
3950 This->stackToVisit,
3951 sizeof(ULONG) * newClone->stackSize);
3953 *ppenum = (IEnumSTATSTG*)newClone;
3956 * Don't forget to nail down a reference to the clone before
3957 * returning it.
3959 IEnumSTATSTGImpl_AddRef(*ppenum);
3961 return S_OK;
3964 static INT IEnumSTATSTGImpl_FindParentProperty(
3965 IEnumSTATSTGImpl *This,
3966 ULONG childProperty,
3967 StgProperty *currentProperty,
3968 ULONG *thisNodeId)
3970 ULONG currentSearchNode;
3971 ULONG foundNode;
3974 * To avoid the special case, get another pointer to a ULONG value if
3975 * the caller didn't supply one.
3977 if (thisNodeId==0)
3978 thisNodeId = &foundNode;
3981 * Start with the node at the top of the stack.
3983 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3986 while (currentSearchNode!=PROPERTY_NULL)
3989 * Store the current node in the returned parameters
3991 *thisNodeId = currentSearchNode;
3994 * Remove the top node from the stack
3996 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3999 * Read the property from the storage.
4001 StorageImpl_ReadProperty(
4002 This->parentStorage,
4003 currentSearchNode,
4004 currentProperty);
4006 if (currentProperty->previousProperty == childProperty)
4007 return PROPERTY_RELATION_PREVIOUS;
4009 else if (currentProperty->nextProperty == childProperty)
4010 return PROPERTY_RELATION_NEXT;
4012 else if (currentProperty->dirProperty == childProperty)
4013 return PROPERTY_RELATION_DIR;
4016 * Push the next search node in the search stack.
4018 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4021 * continue the iteration.
4023 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4026 return PROPERTY_NULL;
4029 static ULONG IEnumSTATSTGImpl_FindProperty(
4030 IEnumSTATSTGImpl* This,
4031 const OLECHAR* lpszPropName,
4032 StgProperty* currentProperty)
4034 ULONG currentSearchNode;
4037 * Start with the node at the top of the stack.
4039 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4041 while (currentSearchNode!=PROPERTY_NULL)
4044 * Remove the top node from the stack
4046 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4049 * Read the property from the storage.
4051 StorageImpl_ReadProperty(This->parentStorage,
4052 currentSearchNode,
4053 currentProperty);
4055 if ( propertyNameCmp(
4056 (const OLECHAR*)currentProperty->name,
4057 (const OLECHAR*)lpszPropName) == 0)
4058 return currentSearchNode;
4061 * Push the next search node in the search stack.
4063 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4066 * continue the iteration.
4068 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4071 return PROPERTY_NULL;
4074 static void IEnumSTATSTGImpl_PushSearchNode(
4075 IEnumSTATSTGImpl* This,
4076 ULONG nodeToPush)
4078 StgProperty rootProperty;
4079 BOOL readSuccessful;
4082 * First, make sure we're not trying to push an unexisting node.
4084 if (nodeToPush==PROPERTY_NULL)
4085 return;
4088 * First push the node to the stack
4090 if (This->stackSize == This->stackMaxSize)
4092 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4094 This->stackToVisit = HeapReAlloc(
4095 GetProcessHeap(),
4097 This->stackToVisit,
4098 sizeof(ULONG) * This->stackMaxSize);
4101 This->stackToVisit[This->stackSize] = nodeToPush;
4102 This->stackSize++;
4105 * Read the root property from the storage.
4107 readSuccessful = StorageImpl_ReadProperty(
4108 This->parentStorage,
4109 nodeToPush,
4110 &rootProperty);
4112 if (readSuccessful)
4114 assert(rootProperty.sizeOfNameString!=0);
4117 * Push the previous search node in the search stack.
4119 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4123 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4124 IEnumSTATSTGImpl* This,
4125 BOOL remove)
4127 ULONG topNode;
4129 if (This->stackSize == 0)
4130 return PROPERTY_NULL;
4132 topNode = This->stackToVisit[This->stackSize-1];
4134 if (remove)
4135 This->stackSize--;
4137 return topNode;
4141 * Virtual function table for the IEnumSTATSTGImpl class.
4143 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4145 IEnumSTATSTGImpl_QueryInterface,
4146 IEnumSTATSTGImpl_AddRef,
4147 IEnumSTATSTGImpl_Release,
4148 IEnumSTATSTGImpl_Next,
4149 IEnumSTATSTGImpl_Skip,
4150 IEnumSTATSTGImpl_Reset,
4151 IEnumSTATSTGImpl_Clone
4154 /******************************************************************************
4155 ** IEnumSTATSTGImpl implementation
4158 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4159 StorageImpl* parentStorage,
4160 ULONG firstPropertyNode)
4162 IEnumSTATSTGImpl* newEnumeration;
4164 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4166 if (newEnumeration!=0)
4169 * Set-up the virtual function table and reference count.
4171 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4172 newEnumeration->ref = 0;
4175 * We want to nail-down the reference to the storage in case the
4176 * enumeration out-lives the storage in the client application.
4178 newEnumeration->parentStorage = parentStorage;
4179 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4181 newEnumeration->firstPropertyNode = firstPropertyNode;
4184 * Initialize the search stack
4186 newEnumeration->stackSize = 0;
4187 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4188 newEnumeration->stackToVisit =
4189 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4192 * Make sure the current node of the iterator is the first one.
4194 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4197 return newEnumeration;
4201 * Virtual function table for the Storage32InternalImpl class.
4203 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4205 StorageBaseImpl_QueryInterface,
4206 StorageBaseImpl_AddRef,
4207 StorageBaseImpl_Release,
4208 StorageBaseImpl_CreateStream,
4209 StorageBaseImpl_OpenStream,
4210 StorageImpl_CreateStorage,
4211 StorageBaseImpl_OpenStorage,
4212 StorageImpl_CopyTo,
4213 StorageImpl_MoveElementTo,
4214 StorageInternalImpl_Commit,
4215 StorageInternalImpl_Revert,
4216 StorageBaseImpl_EnumElements,
4217 StorageImpl_DestroyElement,
4218 StorageBaseImpl_RenameElement,
4219 StorageImpl_SetElementTimes,
4220 StorageBaseImpl_SetClass,
4221 StorageImpl_SetStateBits,
4222 StorageBaseImpl_Stat
4225 /******************************************************************************
4226 ** Storage32InternalImpl implementation
4229 static StorageInternalImpl* StorageInternalImpl_Construct(
4230 StorageImpl* ancestorStorage,
4231 DWORD openFlags,
4232 ULONG rootPropertyIndex)
4234 StorageInternalImpl* newStorage;
4237 * Allocate space for the new storage object
4239 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4241 if (newStorage!=0)
4244 * Initialize the stream list
4246 list_init(&newStorage->base.strmHead);
4249 * Initialize the virtual function table.
4251 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4252 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4253 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4256 * Keep the ancestor storage pointer and nail a reference to it.
4258 newStorage->base.ancestorStorage = ancestorStorage;
4259 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4262 * Keep the index of the root property set for this storage,
4264 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4266 return newStorage;
4269 return 0;
4272 /******************************************************************************
4273 ** StorageUtl implementation
4276 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4278 WORD tmp;
4280 memcpy(&tmp, buffer+offset, sizeof(WORD));
4281 *value = le16toh(tmp);
4284 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4286 value = htole16(value);
4287 memcpy(buffer+offset, &value, sizeof(WORD));
4290 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4292 DWORD tmp;
4294 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4295 *value = le32toh(tmp);
4298 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4300 value = htole32(value);
4301 memcpy(buffer+offset, &value, sizeof(DWORD));
4304 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4305 ULARGE_INTEGER* value)
4307 #ifdef WORDS_BIGENDIAN
4308 ULARGE_INTEGER tmp;
4310 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4311 value->u.LowPart = htole32(tmp.u.HighPart);
4312 value->u.HighPart = htole32(tmp.u.LowPart);
4313 #else
4314 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4315 #endif
4318 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4319 const ULARGE_INTEGER *value)
4321 #ifdef WORDS_BIGENDIAN
4322 ULARGE_INTEGER tmp;
4324 tmp.u.LowPart = htole32(value->u.HighPart);
4325 tmp.u.HighPart = htole32(value->u.LowPart);
4326 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4327 #else
4328 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4329 #endif
4332 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4334 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4335 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4336 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4338 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4341 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4343 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4344 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4345 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4347 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4350 void StorageUtl_CopyPropertyToSTATSTG(
4351 STATSTG* destination,
4352 const StgProperty* source,
4353 int statFlags)
4356 * The copy of the string occurs only when the flag is not set
4358 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4359 (source->name == NULL) ||
4360 (source->name[0] == 0) )
4362 destination->pwcsName = 0;
4364 else
4366 destination->pwcsName =
4367 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4369 strcpyW((LPWSTR)destination->pwcsName, source->name);
4372 switch (source->propertyType)
4374 case PROPTYPE_STORAGE:
4375 case PROPTYPE_ROOT:
4376 destination->type = STGTY_STORAGE;
4377 break;
4378 case PROPTYPE_STREAM:
4379 destination->type = STGTY_STREAM;
4380 break;
4381 default:
4382 destination->type = STGTY_STREAM;
4383 break;
4386 destination->cbSize = source->size;
4388 currentReturnStruct->mtime = {0}; TODO
4389 currentReturnStruct->ctime = {0};
4390 currentReturnStruct->atime = {0};
4392 destination->grfMode = 0;
4393 destination->grfLocksSupported = 0;
4394 destination->clsid = source->propertyUniqueID;
4395 destination->grfStateBits = 0;
4396 destination->reserved = 0;
4399 /******************************************************************************
4400 ** BlockChainStream implementation
4403 BlockChainStream* BlockChainStream_Construct(
4404 StorageImpl* parentStorage,
4405 ULONG* headOfStreamPlaceHolder,
4406 ULONG propertyIndex)
4408 BlockChainStream* newStream;
4409 ULONG blockIndex;
4411 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4413 newStream->parentStorage = parentStorage;
4414 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4415 newStream->ownerPropertyIndex = propertyIndex;
4416 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4417 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4418 newStream->numBlocks = 0;
4420 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4422 while (blockIndex != BLOCK_END_OF_CHAIN)
4424 newStream->numBlocks++;
4425 newStream->tailIndex = blockIndex;
4427 if(FAILED(StorageImpl_GetNextBlockInChain(
4428 parentStorage,
4429 blockIndex,
4430 &blockIndex)))
4432 HeapFree(GetProcessHeap(), 0, newStream);
4433 return NULL;
4437 return newStream;
4440 void BlockChainStream_Destroy(BlockChainStream* This)
4442 HeapFree(GetProcessHeap(), 0, This);
4445 /******************************************************************************
4446 * BlockChainStream_GetHeadOfChain
4448 * Returns the head of this stream chain.
4449 * Some special chains don't have properties, their heads are kept in
4450 * This->headOfStreamPlaceHolder.
4453 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4455 StgProperty chainProperty;
4456 BOOL readSuccessful;
4458 if (This->headOfStreamPlaceHolder != 0)
4459 return *(This->headOfStreamPlaceHolder);
4461 if (This->ownerPropertyIndex != PROPERTY_NULL)
4463 readSuccessful = StorageImpl_ReadProperty(
4464 This->parentStorage,
4465 This->ownerPropertyIndex,
4466 &chainProperty);
4468 if (readSuccessful)
4470 return chainProperty.startingBlock;
4474 return BLOCK_END_OF_CHAIN;
4477 /******************************************************************************
4478 * BlockChainStream_GetCount
4480 * Returns the number of blocks that comprises this chain.
4481 * This is not the size of the stream as the last block may not be full!
4484 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4486 ULONG blockIndex;
4487 ULONG count = 0;
4489 blockIndex = BlockChainStream_GetHeadOfChain(This);
4491 while (blockIndex != BLOCK_END_OF_CHAIN)
4493 count++;
4495 if(FAILED(StorageImpl_GetNextBlockInChain(
4496 This->parentStorage,
4497 blockIndex,
4498 &blockIndex)))
4499 return 0;
4502 return count;
4505 /******************************************************************************
4506 * BlockChainStream_ReadAt
4508 * Reads a specified number of bytes from this chain at the specified offset.
4509 * bytesRead may be NULL.
4510 * Failure will be returned if the specified number of bytes has not been read.
4512 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4513 ULARGE_INTEGER offset,
4514 ULONG size,
4515 void* buffer,
4516 ULONG* bytesRead)
4518 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4519 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4520 ULONG bytesToReadInBuffer;
4521 ULONG blockIndex;
4522 BYTE* bufferWalker;
4524 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4527 * Find the first block in the stream that contains part of the buffer.
4529 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4530 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4531 (blockNoInSequence < This->lastBlockNoInSequence) )
4533 blockIndex = BlockChainStream_GetHeadOfChain(This);
4534 This->lastBlockNoInSequence = blockNoInSequence;
4536 else
4538 ULONG temp = blockNoInSequence;
4540 blockIndex = This->lastBlockNoInSequenceIndex;
4541 blockNoInSequence -= This->lastBlockNoInSequence;
4542 This->lastBlockNoInSequence = temp;
4545 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4547 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4548 return STG_E_DOCFILECORRUPT;
4549 blockNoInSequence--;
4552 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4553 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4555 This->lastBlockNoInSequenceIndex = blockIndex;
4558 * Start reading the buffer.
4560 *bytesRead = 0;
4561 bufferWalker = buffer;
4563 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4565 ULARGE_INTEGER ulOffset;
4566 DWORD bytesReadAt;
4568 * Calculate how many bytes we can copy from this big block.
4570 bytesToReadInBuffer =
4571 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4573 TRACE("block %i\n",blockIndex);
4574 ulOffset.u.HighPart = 0;
4575 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4576 offsetInBlock;
4578 StorageImpl_ReadAt(This->parentStorage,
4579 ulOffset,
4580 bufferWalker,
4581 bytesToReadInBuffer,
4582 &bytesReadAt);
4584 * Step to the next big block.
4586 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4587 return STG_E_DOCFILECORRUPT;
4589 bufferWalker += bytesReadAt;
4590 size -= bytesReadAt;
4591 *bytesRead += bytesReadAt;
4592 offsetInBlock = 0; /* There is no offset on the next block */
4594 if (bytesToReadInBuffer != bytesReadAt)
4595 break;
4598 return (size == 0) ? S_OK : STG_E_READFAULT;
4601 /******************************************************************************
4602 * BlockChainStream_WriteAt
4604 * Writes the specified number of bytes to this chain at the specified offset.
4605 * bytesWritten may be NULL.
4606 * Will fail if not all specified number of bytes have been written.
4608 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4609 ULARGE_INTEGER offset,
4610 ULONG size,
4611 const void* buffer,
4612 ULONG* bytesWritten)
4614 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4615 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4616 ULONG bytesToWrite;
4617 ULONG blockIndex;
4618 const BYTE* bufferWalker;
4621 * Find the first block in the stream that contains part of the buffer.
4623 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4624 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4625 (blockNoInSequence < This->lastBlockNoInSequence) )
4627 blockIndex = BlockChainStream_GetHeadOfChain(This);
4628 This->lastBlockNoInSequence = blockNoInSequence;
4630 else
4632 ULONG temp = blockNoInSequence;
4634 blockIndex = This->lastBlockNoInSequenceIndex;
4635 blockNoInSequence -= This->lastBlockNoInSequence;
4636 This->lastBlockNoInSequence = temp;
4639 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4641 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4642 &blockIndex)))
4643 return STG_E_DOCFILECORRUPT;
4644 blockNoInSequence--;
4647 This->lastBlockNoInSequenceIndex = blockIndex;
4649 /* BlockChainStream_SetSize should have already been called to ensure we have
4650 * enough blocks in the chain to write into */
4651 if (blockIndex == BLOCK_END_OF_CHAIN)
4653 ERR("not enough blocks in chain to write data\n");
4654 return STG_E_DOCFILECORRUPT;
4657 *bytesWritten = 0;
4658 bufferWalker = (const BYTE*)buffer;
4660 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4662 ULARGE_INTEGER ulOffset;
4663 DWORD bytesWrittenAt;
4665 * Calculate how many bytes we can copy from this big block.
4667 bytesToWrite =
4668 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4670 TRACE("block %i\n",blockIndex);
4671 ulOffset.u.HighPart = 0;
4672 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4673 offsetInBlock;
4675 StorageImpl_WriteAt(This->parentStorage,
4676 ulOffset,
4677 bufferWalker,
4678 bytesToWrite,
4679 &bytesWrittenAt);
4682 * Step to the next big block.
4684 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4685 &blockIndex)))
4686 return STG_E_DOCFILECORRUPT;
4688 bufferWalker += bytesWrittenAt;
4689 size -= bytesWrittenAt;
4690 *bytesWritten += bytesWrittenAt;
4691 offsetInBlock = 0; /* There is no offset on the next block */
4693 if (bytesWrittenAt != bytesToWrite)
4694 break;
4697 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4700 /******************************************************************************
4701 * BlockChainStream_Shrink
4703 * Shrinks this chain in the big block depot.
4705 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4706 ULARGE_INTEGER newSize)
4708 ULONG blockIndex, extraBlock;
4709 ULONG numBlocks;
4710 ULONG count = 1;
4713 * Reset the last accessed block cache.
4715 This->lastBlockNoInSequence = 0xFFFFFFFF;
4716 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4719 * Figure out how many blocks are needed to contain the new size
4721 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4723 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4724 numBlocks++;
4726 blockIndex = BlockChainStream_GetHeadOfChain(This);
4729 * Go to the new end of chain
4731 while (count < numBlocks)
4733 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4734 &blockIndex)))
4735 return FALSE;
4736 count++;
4739 /* Get the next block before marking the new end */
4740 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4741 &extraBlock)))
4742 return FALSE;
4744 /* Mark the new end of chain */
4745 StorageImpl_SetNextBlockInChain(
4746 This->parentStorage,
4747 blockIndex,
4748 BLOCK_END_OF_CHAIN);
4750 This->tailIndex = blockIndex;
4751 This->numBlocks = numBlocks;
4754 * Mark the extra blocks as free
4756 while (extraBlock != BLOCK_END_OF_CHAIN)
4758 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4759 &blockIndex)))
4760 return FALSE;
4761 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4762 extraBlock = blockIndex;
4765 return TRUE;
4768 /******************************************************************************
4769 * BlockChainStream_Enlarge
4771 * Grows this chain in the big block depot.
4773 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4774 ULARGE_INTEGER newSize)
4776 ULONG blockIndex, currentBlock;
4777 ULONG newNumBlocks;
4778 ULONG oldNumBlocks = 0;
4780 blockIndex = BlockChainStream_GetHeadOfChain(This);
4783 * Empty chain. Create the head.
4785 if (blockIndex == BLOCK_END_OF_CHAIN)
4787 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4788 StorageImpl_SetNextBlockInChain(This->parentStorage,
4789 blockIndex,
4790 BLOCK_END_OF_CHAIN);
4792 if (This->headOfStreamPlaceHolder != 0)
4794 *(This->headOfStreamPlaceHolder) = blockIndex;
4796 else
4798 StgProperty chainProp;
4799 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4801 StorageImpl_ReadProperty(
4802 This->parentStorage,
4803 This->ownerPropertyIndex,
4804 &chainProp);
4806 chainProp.startingBlock = blockIndex;
4808 StorageImpl_WriteProperty(
4809 This->parentStorage,
4810 This->ownerPropertyIndex,
4811 &chainProp);
4814 This->tailIndex = blockIndex;
4815 This->numBlocks = 1;
4819 * Figure out how many blocks are needed to contain this stream
4821 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4823 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4824 newNumBlocks++;
4827 * Go to the current end of chain
4829 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4831 currentBlock = blockIndex;
4833 while (blockIndex != BLOCK_END_OF_CHAIN)
4835 This->numBlocks++;
4836 currentBlock = blockIndex;
4838 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4839 &blockIndex)))
4840 return FALSE;
4843 This->tailIndex = currentBlock;
4846 currentBlock = This->tailIndex;
4847 oldNumBlocks = This->numBlocks;
4850 * Add new blocks to the chain
4852 if (oldNumBlocks < newNumBlocks)
4854 while (oldNumBlocks < newNumBlocks)
4856 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4858 StorageImpl_SetNextBlockInChain(
4859 This->parentStorage,
4860 currentBlock,
4861 blockIndex);
4863 StorageImpl_SetNextBlockInChain(
4864 This->parentStorage,
4865 blockIndex,
4866 BLOCK_END_OF_CHAIN);
4868 currentBlock = blockIndex;
4869 oldNumBlocks++;
4872 This->tailIndex = blockIndex;
4873 This->numBlocks = newNumBlocks;
4876 return TRUE;
4879 /******************************************************************************
4880 * BlockChainStream_SetSize
4882 * Sets the size of this stream. The big block depot will be updated.
4883 * The file will grow if we grow the chain.
4885 * TODO: Free the actual blocks in the file when we shrink the chain.
4886 * Currently, the blocks are still in the file. So the file size
4887 * doesn't shrink even if we shrink streams.
4889 BOOL BlockChainStream_SetSize(
4890 BlockChainStream* This,
4891 ULARGE_INTEGER newSize)
4893 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4895 if (newSize.u.LowPart == size.u.LowPart)
4896 return TRUE;
4898 if (newSize.u.LowPart < size.u.LowPart)
4900 BlockChainStream_Shrink(This, newSize);
4902 else
4904 BlockChainStream_Enlarge(This, newSize);
4907 return TRUE;
4910 /******************************************************************************
4911 * BlockChainStream_GetSize
4913 * Returns the size of this chain.
4914 * Will return the block count if this chain doesn't have a property.
4916 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4918 StgProperty chainProperty;
4920 if(This->headOfStreamPlaceHolder == NULL)
4923 * This chain is a data stream read the property and return
4924 * the appropriate size
4926 StorageImpl_ReadProperty(
4927 This->parentStorage,
4928 This->ownerPropertyIndex,
4929 &chainProperty);
4931 return chainProperty.size;
4933 else
4936 * this chain is a chain that does not have a property, figure out the
4937 * size by making the product number of used blocks times the
4938 * size of them
4940 ULARGE_INTEGER result;
4941 result.u.HighPart = 0;
4943 result.u.LowPart =
4944 BlockChainStream_GetCount(This) *
4945 This->parentStorage->bigBlockSize;
4947 return result;
4951 /******************************************************************************
4952 ** SmallBlockChainStream implementation
4955 SmallBlockChainStream* SmallBlockChainStream_Construct(
4956 StorageImpl* parentStorage,
4957 ULONG propertyIndex)
4959 SmallBlockChainStream* newStream;
4961 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4963 newStream->parentStorage = parentStorage;
4964 newStream->ownerPropertyIndex = propertyIndex;
4966 return newStream;
4969 void SmallBlockChainStream_Destroy(
4970 SmallBlockChainStream* This)
4972 HeapFree(GetProcessHeap(), 0, This);
4975 /******************************************************************************
4976 * SmallBlockChainStream_GetHeadOfChain
4978 * Returns the head of this chain of small blocks.
4980 static ULONG SmallBlockChainStream_GetHeadOfChain(
4981 SmallBlockChainStream* This)
4983 StgProperty chainProperty;
4984 BOOL readSuccessful;
4986 if (This->ownerPropertyIndex)
4988 readSuccessful = StorageImpl_ReadProperty(
4989 This->parentStorage,
4990 This->ownerPropertyIndex,
4991 &chainProperty);
4993 if (readSuccessful)
4995 return chainProperty.startingBlock;
5000 return BLOCK_END_OF_CHAIN;
5003 /******************************************************************************
5004 * SmallBlockChainStream_GetNextBlockInChain
5006 * Returns the index of the next small block in this chain.
5008 * Return Values:
5009 * - BLOCK_END_OF_CHAIN: end of this chain
5010 * - BLOCK_UNUSED: small block 'blockIndex' is free
5012 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5013 SmallBlockChainStream* This,
5014 ULONG blockIndex,
5015 ULONG* nextBlockInChain)
5017 ULARGE_INTEGER offsetOfBlockInDepot;
5018 DWORD buffer;
5019 ULONG bytesRead;
5020 HRESULT res;
5022 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5024 offsetOfBlockInDepot.u.HighPart = 0;
5025 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5028 * Read those bytes in the buffer from the small block file.
5030 res = BlockChainStream_ReadAt(
5031 This->parentStorage->smallBlockDepotChain,
5032 offsetOfBlockInDepot,
5033 sizeof(DWORD),
5034 &buffer,
5035 &bytesRead);
5037 if (SUCCEEDED(res))
5039 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5040 return S_OK;
5043 return res;
5046 /******************************************************************************
5047 * SmallBlockChainStream_SetNextBlockInChain
5049 * Writes the index of the next block of the specified block in the small
5050 * block depot.
5051 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5052 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5054 static void SmallBlockChainStream_SetNextBlockInChain(
5055 SmallBlockChainStream* This,
5056 ULONG blockIndex,
5057 ULONG nextBlock)
5059 ULARGE_INTEGER offsetOfBlockInDepot;
5060 DWORD buffer;
5061 ULONG bytesWritten;
5063 offsetOfBlockInDepot.u.HighPart = 0;
5064 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5066 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5069 * Read those bytes in the buffer from the small block file.
5071 BlockChainStream_WriteAt(
5072 This->parentStorage->smallBlockDepotChain,
5073 offsetOfBlockInDepot,
5074 sizeof(DWORD),
5075 &buffer,
5076 &bytesWritten);
5079 /******************************************************************************
5080 * SmallBlockChainStream_FreeBlock
5082 * Flag small block 'blockIndex' as free in the small block depot.
5084 static void SmallBlockChainStream_FreeBlock(
5085 SmallBlockChainStream* This,
5086 ULONG blockIndex)
5088 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5091 /******************************************************************************
5092 * SmallBlockChainStream_GetNextFreeBlock
5094 * Returns the index of a free small block. The small block depot will be
5095 * enlarged if necessary. The small block chain will also be enlarged if
5096 * necessary.
5098 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5099 SmallBlockChainStream* This)
5101 ULARGE_INTEGER offsetOfBlockInDepot;
5102 DWORD buffer;
5103 ULONG bytesRead;
5104 ULONG blockIndex = 0;
5105 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5106 HRESULT res = S_OK;
5107 ULONG smallBlocksPerBigBlock;
5109 offsetOfBlockInDepot.u.HighPart = 0;
5112 * Scan the small block depot for a free block
5114 while (nextBlockIndex != BLOCK_UNUSED)
5116 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5118 res = BlockChainStream_ReadAt(
5119 This->parentStorage->smallBlockDepotChain,
5120 offsetOfBlockInDepot,
5121 sizeof(DWORD),
5122 &buffer,
5123 &bytesRead);
5126 * If we run out of space for the small block depot, enlarge it
5128 if (SUCCEEDED(res))
5130 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5132 if (nextBlockIndex != BLOCK_UNUSED)
5133 blockIndex++;
5135 else
5137 ULONG count =
5138 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5140 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5141 ULONG nextBlock, newsbdIndex;
5142 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5144 nextBlock = sbdIndex;
5145 while (nextBlock != BLOCK_END_OF_CHAIN)
5147 sbdIndex = nextBlock;
5148 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5151 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5152 if (sbdIndex != BLOCK_END_OF_CHAIN)
5153 StorageImpl_SetNextBlockInChain(
5154 This->parentStorage,
5155 sbdIndex,
5156 newsbdIndex);
5158 StorageImpl_SetNextBlockInChain(
5159 This->parentStorage,
5160 newsbdIndex,
5161 BLOCK_END_OF_CHAIN);
5164 * Initialize all the small blocks to free
5166 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5167 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5169 if (count == 0)
5172 * We have just created the small block depot.
5174 StgProperty rootProp;
5175 ULONG sbStartIndex;
5178 * Save it in the header
5180 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5181 StorageImpl_SaveFileHeader(This->parentStorage);
5184 * And allocate the first big block that will contain small blocks
5186 sbStartIndex =
5187 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5189 StorageImpl_SetNextBlockInChain(
5190 This->parentStorage,
5191 sbStartIndex,
5192 BLOCK_END_OF_CHAIN);
5194 StorageImpl_ReadProperty(
5195 This->parentStorage,
5196 This->parentStorage->base.rootPropertySetIndex,
5197 &rootProp);
5199 rootProp.startingBlock = sbStartIndex;
5200 rootProp.size.u.HighPart = 0;
5201 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5203 StorageImpl_WriteProperty(
5204 This->parentStorage,
5205 This->parentStorage->base.rootPropertySetIndex,
5206 &rootProp);
5208 else
5209 StorageImpl_SaveFileHeader(This->parentStorage);
5213 smallBlocksPerBigBlock =
5214 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5217 * Verify if we have to allocate big blocks to contain small blocks
5219 if (blockIndex % smallBlocksPerBigBlock == 0)
5221 StgProperty rootProp;
5222 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5224 StorageImpl_ReadProperty(
5225 This->parentStorage,
5226 This->parentStorage->base.rootPropertySetIndex,
5227 &rootProp);
5229 if (rootProp.size.u.LowPart <
5230 (blocksRequired * This->parentStorage->bigBlockSize))
5232 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5234 BlockChainStream_SetSize(
5235 This->parentStorage->smallBlockRootChain,
5236 rootProp.size);
5238 StorageImpl_WriteProperty(
5239 This->parentStorage,
5240 This->parentStorage->base.rootPropertySetIndex,
5241 &rootProp);
5245 return blockIndex;
5248 /******************************************************************************
5249 * SmallBlockChainStream_ReadAt
5251 * Reads a specified number of bytes from this chain at the specified offset.
5252 * bytesRead may be NULL.
5253 * Failure will be returned if the specified number of bytes has not been read.
5255 HRESULT SmallBlockChainStream_ReadAt(
5256 SmallBlockChainStream* This,
5257 ULARGE_INTEGER offset,
5258 ULONG size,
5259 void* buffer,
5260 ULONG* bytesRead)
5262 HRESULT rc = S_OK;
5263 ULARGE_INTEGER offsetInBigBlockFile;
5264 ULONG blockNoInSequence =
5265 offset.u.LowPart / This->parentStorage->smallBlockSize;
5267 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5268 ULONG bytesToReadInBuffer;
5269 ULONG blockIndex;
5270 ULONG bytesReadFromBigBlockFile;
5271 BYTE* bufferWalker;
5274 * This should never happen on a small block file.
5276 assert(offset.u.HighPart==0);
5279 * Find the first block in the stream that contains part of the buffer.
5281 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5283 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5285 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5286 if(FAILED(rc))
5287 return rc;
5288 blockNoInSequence--;
5292 * Start reading the buffer.
5294 *bytesRead = 0;
5295 bufferWalker = buffer;
5297 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5300 * Calculate how many bytes we can copy from this small block.
5302 bytesToReadInBuffer =
5303 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5306 * Calculate the offset of the small block in the small block file.
5308 offsetInBigBlockFile.u.HighPart = 0;
5309 offsetInBigBlockFile.u.LowPart =
5310 blockIndex * This->parentStorage->smallBlockSize;
5312 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5315 * Read those bytes in the buffer from the small block file.
5316 * The small block has already been identified so it shouldn't fail
5317 * unless the file is corrupt.
5319 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5320 offsetInBigBlockFile,
5321 bytesToReadInBuffer,
5322 bufferWalker,
5323 &bytesReadFromBigBlockFile);
5325 if (FAILED(rc))
5326 return rc;
5329 * Step to the next big block.
5331 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5332 if(FAILED(rc))
5333 return STG_E_DOCFILECORRUPT;
5335 bufferWalker += bytesReadFromBigBlockFile;
5336 size -= bytesReadFromBigBlockFile;
5337 *bytesRead += bytesReadFromBigBlockFile;
5338 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5341 return (size == 0) ? S_OK : STG_E_READFAULT;
5344 /******************************************************************************
5345 * SmallBlockChainStream_WriteAt
5347 * Writes the specified number of bytes to this chain at the specified offset.
5348 * bytesWritten may be NULL.
5349 * Will fail if not all specified number of bytes have been written.
5351 HRESULT SmallBlockChainStream_WriteAt(
5352 SmallBlockChainStream* This,
5353 ULARGE_INTEGER offset,
5354 ULONG size,
5355 const void* buffer,
5356 ULONG* bytesWritten)
5358 ULARGE_INTEGER offsetInBigBlockFile;
5359 ULONG blockNoInSequence =
5360 offset.u.LowPart / This->parentStorage->smallBlockSize;
5362 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5363 ULONG bytesToWriteInBuffer;
5364 ULONG blockIndex;
5365 ULONG bytesWrittenToBigBlockFile;
5366 const BYTE* bufferWalker;
5367 HRESULT res;
5370 * This should never happen on a small block file.
5372 assert(offset.u.HighPart==0);
5375 * Find the first block in the stream that contains part of the buffer.
5377 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5379 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5381 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5382 return STG_E_DOCFILECORRUPT;
5383 blockNoInSequence--;
5387 * Start writing the buffer.
5389 * Here, I'm casting away the constness on the buffer variable
5390 * This is OK since we don't intend to modify that buffer.
5392 *bytesWritten = 0;
5393 bufferWalker = (const BYTE*)buffer;
5394 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5397 * Calculate how many bytes we can copy to this small block.
5399 bytesToWriteInBuffer =
5400 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5403 * Calculate the offset of the small block in the small block file.
5405 offsetInBigBlockFile.u.HighPart = 0;
5406 offsetInBigBlockFile.u.LowPart =
5407 blockIndex * This->parentStorage->smallBlockSize;
5409 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5412 * Write those bytes in the buffer to the small block file.
5414 res = BlockChainStream_WriteAt(
5415 This->parentStorage->smallBlockRootChain,
5416 offsetInBigBlockFile,
5417 bytesToWriteInBuffer,
5418 bufferWalker,
5419 &bytesWrittenToBigBlockFile);
5420 if (FAILED(res))
5421 return res;
5424 * Step to the next big block.
5426 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5427 &blockIndex)))
5428 return FALSE;
5429 bufferWalker += bytesWrittenToBigBlockFile;
5430 size -= bytesWrittenToBigBlockFile;
5431 *bytesWritten += bytesWrittenToBigBlockFile;
5432 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5435 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5438 /******************************************************************************
5439 * SmallBlockChainStream_Shrink
5441 * Shrinks this chain in the small block depot.
5443 static BOOL SmallBlockChainStream_Shrink(
5444 SmallBlockChainStream* This,
5445 ULARGE_INTEGER newSize)
5447 ULONG blockIndex, extraBlock;
5448 ULONG numBlocks;
5449 ULONG count = 0;
5451 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5453 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5454 numBlocks++;
5456 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5459 * Go to the new end of chain
5461 while (count < numBlocks)
5463 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5464 &blockIndex)))
5465 return FALSE;
5466 count++;
5470 * If the count is 0, we have a special case, the head of the chain was
5471 * just freed.
5473 if (count == 0)
5475 StgProperty chainProp;
5477 StorageImpl_ReadProperty(This->parentStorage,
5478 This->ownerPropertyIndex,
5479 &chainProp);
5481 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5483 StorageImpl_WriteProperty(This->parentStorage,
5484 This->ownerPropertyIndex,
5485 &chainProp);
5488 * We start freeing the chain at the head block.
5490 extraBlock = blockIndex;
5492 else
5494 /* Get the next block before marking the new end */
5495 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5496 &extraBlock)))
5497 return FALSE;
5499 /* Mark the new end of chain */
5500 SmallBlockChainStream_SetNextBlockInChain(
5501 This,
5502 blockIndex,
5503 BLOCK_END_OF_CHAIN);
5507 * Mark the extra blocks as free
5509 while (extraBlock != BLOCK_END_OF_CHAIN)
5511 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5512 &blockIndex)))
5513 return FALSE;
5514 SmallBlockChainStream_FreeBlock(This, extraBlock);
5515 extraBlock = blockIndex;
5518 return TRUE;
5521 /******************************************************************************
5522 * SmallBlockChainStream_Enlarge
5524 * Grows this chain in the small block depot.
5526 static BOOL SmallBlockChainStream_Enlarge(
5527 SmallBlockChainStream* This,
5528 ULARGE_INTEGER newSize)
5530 ULONG blockIndex, currentBlock;
5531 ULONG newNumBlocks;
5532 ULONG oldNumBlocks = 0;
5534 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5537 * Empty chain
5539 if (blockIndex == BLOCK_END_OF_CHAIN)
5542 StgProperty chainProp;
5544 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5545 &chainProp);
5547 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5549 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5550 &chainProp);
5552 blockIndex = chainProp.startingBlock;
5553 SmallBlockChainStream_SetNextBlockInChain(
5554 This,
5555 blockIndex,
5556 BLOCK_END_OF_CHAIN);
5559 currentBlock = blockIndex;
5562 * Figure out how many blocks are needed to contain this stream
5564 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5566 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5567 newNumBlocks++;
5570 * Go to the current end of chain
5572 while (blockIndex != BLOCK_END_OF_CHAIN)
5574 oldNumBlocks++;
5575 currentBlock = blockIndex;
5576 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5577 return FALSE;
5581 * Add new blocks to the chain
5583 while (oldNumBlocks < newNumBlocks)
5585 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5586 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5588 SmallBlockChainStream_SetNextBlockInChain(
5589 This,
5590 blockIndex,
5591 BLOCK_END_OF_CHAIN);
5593 currentBlock = blockIndex;
5594 oldNumBlocks++;
5597 return TRUE;
5600 /******************************************************************************
5601 * SmallBlockChainStream_SetSize
5603 * Sets the size of this stream.
5604 * The file will grow if we grow the chain.
5606 * TODO: Free the actual blocks in the file when we shrink the chain.
5607 * Currently, the blocks are still in the file. So the file size
5608 * doesn't shrink even if we shrink streams.
5610 BOOL SmallBlockChainStream_SetSize(
5611 SmallBlockChainStream* This,
5612 ULARGE_INTEGER newSize)
5614 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5616 if (newSize.u.LowPart == size.u.LowPart)
5617 return TRUE;
5619 if (newSize.u.LowPart < size.u.LowPart)
5621 SmallBlockChainStream_Shrink(This, newSize);
5623 else
5625 SmallBlockChainStream_Enlarge(This, newSize);
5628 return TRUE;
5631 /******************************************************************************
5632 * SmallBlockChainStream_GetSize
5634 * Returns the size of this chain.
5636 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5638 StgProperty chainProperty;
5640 StorageImpl_ReadProperty(
5641 This->parentStorage,
5642 This->ownerPropertyIndex,
5643 &chainProperty);
5645 return chainProperty.size;
5648 /******************************************************************************
5649 * StgCreateDocfile [OLE32.@]
5650 * Creates a new compound file storage object
5652 * PARAMS
5653 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5654 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5655 * reserved [ ?] unused?, usually 0
5656 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5658 * RETURNS
5659 * S_OK if the file was successfully created
5660 * some STG_E_ value if error
5661 * NOTES
5662 * if pwcsName is NULL, create file with new unique name
5663 * the function can returns
5664 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5665 * (unrealized now)
5667 HRESULT WINAPI StgCreateDocfile(
5668 LPCOLESTR pwcsName,
5669 DWORD grfMode,
5670 DWORD reserved,
5671 IStorage **ppstgOpen)
5673 StorageImpl* newStorage = 0;
5674 HANDLE hFile = INVALID_HANDLE_VALUE;
5675 HRESULT hr = STG_E_INVALIDFLAG;
5676 DWORD shareMode;
5677 DWORD accessMode;
5678 DWORD creationMode;
5679 DWORD fileAttributes;
5680 WCHAR tempFileName[MAX_PATH];
5682 TRACE("(%s, %x, %d, %p)\n",
5683 debugstr_w(pwcsName), grfMode,
5684 reserved, ppstgOpen);
5687 * Validate the parameters
5689 if (ppstgOpen == 0)
5690 return STG_E_INVALIDPOINTER;
5691 if (reserved != 0)
5692 return STG_E_INVALIDPARAMETER;
5694 /* if no share mode given then DENY_NONE is the default */
5695 if (STGM_SHARE_MODE(grfMode) == 0)
5696 grfMode |= STGM_SHARE_DENY_NONE;
5699 * Validate the STGM flags
5701 if ( FAILED( validateSTGM(grfMode) ))
5702 goto end;
5704 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5705 switch(STGM_ACCESS_MODE(grfMode))
5707 case STGM_WRITE:
5708 case STGM_READWRITE:
5709 break;
5710 default:
5711 goto end;
5714 /* in direct mode, can only use SHARE_EXCLUSIVE */
5715 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5716 goto end;
5718 /* but in transacted mode, any share mode is valid */
5721 * Generate a unique name.
5723 if (pwcsName == 0)
5725 WCHAR tempPath[MAX_PATH];
5726 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5728 memset(tempPath, 0, sizeof(tempPath));
5729 memset(tempFileName, 0, sizeof(tempFileName));
5731 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5732 tempPath[0] = '.';
5734 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5735 pwcsName = tempFileName;
5736 else
5738 hr = STG_E_INSUFFICIENTMEMORY;
5739 goto end;
5742 creationMode = TRUNCATE_EXISTING;
5744 else
5746 creationMode = GetCreationModeFromSTGM(grfMode);
5750 * Interpret the STGM value grfMode
5752 shareMode = GetShareModeFromSTGM(grfMode);
5753 accessMode = GetAccessModeFromSTGM(grfMode);
5755 if (grfMode & STGM_DELETEONRELEASE)
5756 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5757 else
5758 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5760 if (grfMode & STGM_TRANSACTED)
5761 FIXME("Transacted mode not implemented.\n");
5764 * Initialize the "out" parameter.
5766 *ppstgOpen = 0;
5768 hFile = CreateFileW(pwcsName,
5769 accessMode,
5770 shareMode,
5771 NULL,
5772 creationMode,
5773 fileAttributes,
5776 if (hFile == INVALID_HANDLE_VALUE)
5778 if(GetLastError() == ERROR_FILE_EXISTS)
5779 hr = STG_E_FILEALREADYEXISTS;
5780 else
5781 hr = E_FAIL;
5782 goto end;
5786 * Allocate and initialize the new IStorage32object.
5788 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5790 if (newStorage == 0)
5792 hr = STG_E_INSUFFICIENTMEMORY;
5793 goto end;
5796 hr = StorageImpl_Construct(
5797 newStorage,
5798 hFile,
5799 pwcsName,
5800 NULL,
5801 grfMode,
5802 TRUE,
5803 TRUE);
5805 if (FAILED(hr))
5807 HeapFree(GetProcessHeap(), 0, newStorage);
5808 goto end;
5812 * Get an "out" pointer for the caller.
5814 hr = StorageBaseImpl_QueryInterface(
5815 (IStorage*)newStorage,
5816 (REFIID)&IID_IStorage,
5817 (void**)ppstgOpen);
5818 end:
5819 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5821 return hr;
5824 /******************************************************************************
5825 * StgCreateStorageEx [OLE32.@]
5827 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5829 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5830 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5832 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5834 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5835 return STG_E_INVALIDPARAMETER;
5838 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5840 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5841 return STG_E_INVALIDPARAMETER;
5844 if (stgfmt == STGFMT_FILE)
5846 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5847 return STG_E_INVALIDPARAMETER;
5850 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5852 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5853 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5856 ERR("Invalid stgfmt argument\n");
5857 return STG_E_INVALIDPARAMETER;
5860 /******************************************************************************
5861 * StgCreatePropSetStg [OLE32.@]
5863 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5864 IPropertySetStorage **ppPropSetStg)
5866 HRESULT hr;
5868 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5869 if (reserved)
5870 hr = STG_E_INVALIDPARAMETER;
5871 else
5872 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5873 (void**)ppPropSetStg);
5874 return hr;
5877 /******************************************************************************
5878 * StgOpenStorageEx [OLE32.@]
5880 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5882 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5883 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5885 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5887 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5888 return STG_E_INVALIDPARAMETER;
5891 switch (stgfmt)
5893 case STGFMT_FILE:
5894 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5895 return STG_E_INVALIDPARAMETER;
5897 case STGFMT_STORAGE:
5898 break;
5900 case STGFMT_DOCFILE:
5901 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5903 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5904 return STG_E_INVALIDPARAMETER;
5906 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5907 break;
5909 case STGFMT_ANY:
5910 WARN("STGFMT_ANY assuming storage\n");
5911 break;
5913 default:
5914 return STG_E_INVALIDPARAMETER;
5917 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5921 /******************************************************************************
5922 * StgOpenStorage [OLE32.@]
5924 HRESULT WINAPI StgOpenStorage(
5925 const OLECHAR *pwcsName,
5926 IStorage *pstgPriority,
5927 DWORD grfMode,
5928 SNB snbExclude,
5929 DWORD reserved,
5930 IStorage **ppstgOpen)
5932 StorageImpl* newStorage = 0;
5933 HRESULT hr = S_OK;
5934 HANDLE hFile = 0;
5935 DWORD shareMode;
5936 DWORD accessMode;
5937 WCHAR fullname[MAX_PATH];
5939 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5940 debugstr_w(pwcsName), pstgPriority, grfMode,
5941 snbExclude, reserved, ppstgOpen);
5944 * Perform sanity checks
5946 if (pwcsName == 0)
5948 hr = STG_E_INVALIDNAME;
5949 goto end;
5952 if (ppstgOpen == 0)
5954 hr = STG_E_INVALIDPOINTER;
5955 goto end;
5958 if (reserved)
5960 hr = STG_E_INVALIDPARAMETER;
5961 goto end;
5964 if (grfMode & STGM_PRIORITY)
5966 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5967 return STG_E_INVALIDFLAG;
5968 if (grfMode & STGM_DELETEONRELEASE)
5969 return STG_E_INVALIDFUNCTION;
5970 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5971 return STG_E_INVALIDFLAG;
5972 grfMode &= ~0xf0; /* remove the existing sharing mode */
5973 grfMode |= STGM_SHARE_DENY_NONE;
5975 /* STGM_PRIORITY stops other IStorage objects on the same file from
5976 * committing until the STGM_PRIORITY IStorage is closed. it also
5977 * stops non-transacted mode StgOpenStorage calls with write access from
5978 * succeeding. obviously, both of these cannot be achieved through just
5979 * file share flags */
5980 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5984 * Validate the sharing mode
5986 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5987 switch(STGM_SHARE_MODE(grfMode))
5989 case STGM_SHARE_EXCLUSIVE:
5990 case STGM_SHARE_DENY_WRITE:
5991 break;
5992 default:
5993 hr = STG_E_INVALIDFLAG;
5994 goto end;
5998 * Validate the STGM flags
6000 if ( FAILED( validateSTGM(grfMode) ) ||
6001 (grfMode&STGM_CREATE))
6003 hr = STG_E_INVALIDFLAG;
6004 goto end;
6007 /* shared reading requires transacted mode */
6008 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6009 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6010 !(grfMode&STGM_TRANSACTED) )
6012 hr = STG_E_INVALIDFLAG;
6013 goto end;
6017 * Interpret the STGM value grfMode
6019 shareMode = GetShareModeFromSTGM(grfMode);
6020 accessMode = GetAccessModeFromSTGM(grfMode);
6023 * Initialize the "out" parameter.
6025 *ppstgOpen = 0;
6027 hFile = CreateFileW( pwcsName,
6028 accessMode,
6029 shareMode,
6030 NULL,
6031 OPEN_EXISTING,
6032 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6035 if (hFile==INVALID_HANDLE_VALUE)
6037 DWORD last_error = GetLastError();
6039 hr = E_FAIL;
6041 switch (last_error)
6043 case ERROR_FILE_NOT_FOUND:
6044 hr = STG_E_FILENOTFOUND;
6045 break;
6047 case ERROR_PATH_NOT_FOUND:
6048 hr = STG_E_PATHNOTFOUND;
6049 break;
6051 case ERROR_ACCESS_DENIED:
6052 case ERROR_WRITE_PROTECT:
6053 hr = STG_E_ACCESSDENIED;
6054 break;
6056 case ERROR_SHARING_VIOLATION:
6057 hr = STG_E_SHAREVIOLATION;
6058 break;
6060 default:
6061 hr = E_FAIL;
6064 goto end;
6068 * Refuse to open the file if it's too small to be a structured storage file
6069 * FIXME: verify the file when reading instead of here
6071 if (GetFileSize(hFile, NULL) < 0x100)
6073 CloseHandle(hFile);
6074 hr = STG_E_FILEALREADYEXISTS;
6075 goto end;
6079 * Allocate and initialize the new IStorage32object.
6081 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6083 if (newStorage == 0)
6085 hr = STG_E_INSUFFICIENTMEMORY;
6086 goto end;
6089 /* Initialize the storage */
6090 hr = StorageImpl_Construct(
6091 newStorage,
6092 hFile,
6093 pwcsName,
6094 NULL,
6095 grfMode,
6096 TRUE,
6097 FALSE );
6099 if (FAILED(hr))
6101 HeapFree(GetProcessHeap(), 0, newStorage);
6103 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6105 if(hr == STG_E_INVALIDHEADER)
6106 hr = STG_E_FILEALREADYEXISTS;
6107 goto end;
6110 /* prepare the file name string given in lieu of the root property name */
6111 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6112 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6113 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6116 * Get an "out" pointer for the caller.
6118 hr = StorageBaseImpl_QueryInterface(
6119 (IStorage*)newStorage,
6120 (REFIID)&IID_IStorage,
6121 (void**)ppstgOpen);
6123 end:
6124 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6125 return hr;
6128 /******************************************************************************
6129 * StgCreateDocfileOnILockBytes [OLE32.@]
6131 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6132 ILockBytes *plkbyt,
6133 DWORD grfMode,
6134 DWORD reserved,
6135 IStorage** ppstgOpen)
6137 StorageImpl* newStorage = 0;
6138 HRESULT hr = S_OK;
6141 * Validate the parameters
6143 if ((ppstgOpen == 0) || (plkbyt == 0))
6144 return STG_E_INVALIDPOINTER;
6147 * Allocate and initialize the new IStorage object.
6149 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6151 if (newStorage == 0)
6152 return STG_E_INSUFFICIENTMEMORY;
6154 hr = StorageImpl_Construct(
6155 newStorage,
6158 plkbyt,
6159 grfMode,
6160 FALSE,
6161 TRUE);
6163 if (FAILED(hr))
6165 HeapFree(GetProcessHeap(), 0, newStorage);
6166 return hr;
6170 * Get an "out" pointer for the caller.
6172 hr = StorageBaseImpl_QueryInterface(
6173 (IStorage*)newStorage,
6174 (REFIID)&IID_IStorage,
6175 (void**)ppstgOpen);
6177 return hr;
6180 /******************************************************************************
6181 * StgOpenStorageOnILockBytes [OLE32.@]
6183 HRESULT WINAPI StgOpenStorageOnILockBytes(
6184 ILockBytes *plkbyt,
6185 IStorage *pstgPriority,
6186 DWORD grfMode,
6187 SNB snbExclude,
6188 DWORD reserved,
6189 IStorage **ppstgOpen)
6191 StorageImpl* newStorage = 0;
6192 HRESULT hr = S_OK;
6195 * Perform a sanity check
6197 if ((plkbyt == 0) || (ppstgOpen == 0))
6198 return STG_E_INVALIDPOINTER;
6201 * Validate the STGM flags
6203 if ( FAILED( validateSTGM(grfMode) ))
6204 return STG_E_INVALIDFLAG;
6207 * Initialize the "out" parameter.
6209 *ppstgOpen = 0;
6212 * Allocate and initialize the new IStorage object.
6214 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6216 if (newStorage == 0)
6217 return STG_E_INSUFFICIENTMEMORY;
6219 hr = StorageImpl_Construct(
6220 newStorage,
6223 plkbyt,
6224 grfMode,
6225 FALSE,
6226 FALSE);
6228 if (FAILED(hr))
6230 HeapFree(GetProcessHeap(), 0, newStorage);
6231 return hr;
6235 * Get an "out" pointer for the caller.
6237 hr = StorageBaseImpl_QueryInterface(
6238 (IStorage*)newStorage,
6239 (REFIID)&IID_IStorage,
6240 (void**)ppstgOpen);
6242 return hr;
6245 /******************************************************************************
6246 * StgSetTimes [ole32.@]
6247 * StgSetTimes [OLE32.@]
6251 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6252 FILETIME const *patime, FILETIME const *pmtime)
6254 IStorage *stg = NULL;
6255 HRESULT r;
6257 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6259 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6260 0, 0, &stg);
6261 if( SUCCEEDED(r) )
6263 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6264 IStorage_Release(stg);
6267 return r;
6270 /******************************************************************************
6271 * StgIsStorageILockBytes [OLE32.@]
6273 * Determines if the ILockBytes contains a storage object.
6275 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6277 BYTE sig[8];
6278 ULARGE_INTEGER offset;
6280 offset.u.HighPart = 0;
6281 offset.u.LowPart = 0;
6283 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6285 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6286 return S_OK;
6288 return S_FALSE;
6291 /******************************************************************************
6292 * WriteClassStg [OLE32.@]
6294 * This method will store the specified CLSID in the specified storage object
6296 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6298 HRESULT hRes;
6300 if(!pStg)
6301 return E_INVALIDARG;
6303 hRes = IStorage_SetClass(pStg, rclsid);
6305 return hRes;
6308 /***********************************************************************
6309 * ReadClassStg (OLE32.@)
6311 * This method reads the CLSID previously written to a storage object with
6312 * the WriteClassStg.
6314 * PARAMS
6315 * pstg [I] IStorage pointer
6316 * pclsid [O] Pointer to where the CLSID is written
6318 * RETURNS
6319 * Success: S_OK.
6320 * Failure: HRESULT code.
6322 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6324 STATSTG pstatstg;
6325 HRESULT hRes;
6327 TRACE("(%p, %p)\n", pstg, pclsid);
6329 if(!pstg || !pclsid)
6330 return E_INVALIDARG;
6333 * read a STATSTG structure (contains the clsid) from the storage
6335 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6337 if(SUCCEEDED(hRes))
6338 *pclsid=pstatstg.clsid;
6340 return hRes;
6343 /***********************************************************************
6344 * OleLoadFromStream (OLE32.@)
6346 * This function loads an object from stream
6348 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6350 CLSID clsid;
6351 HRESULT res;
6352 LPPERSISTSTREAM xstm;
6354 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6356 res=ReadClassStm(pStm,&clsid);
6357 if (!SUCCEEDED(res))
6358 return res;
6359 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6360 if (!SUCCEEDED(res))
6361 return res;
6362 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6363 if (!SUCCEEDED(res)) {
6364 IUnknown_Release((IUnknown*)*ppvObj);
6365 return res;
6367 res=IPersistStream_Load(xstm,pStm);
6368 IPersistStream_Release(xstm);
6369 /* FIXME: all refcounts ok at this point? I think they should be:
6370 * pStm : unchanged
6371 * ppvObj : 1
6372 * xstm : 0 (released)
6374 return res;
6377 /***********************************************************************
6378 * OleSaveToStream (OLE32.@)
6380 * This function saves an object with the IPersistStream interface on it
6381 * to the specified stream.
6383 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6386 CLSID clsid;
6387 HRESULT res;
6389 TRACE("(%p,%p)\n",pPStm,pStm);
6391 res=IPersistStream_GetClassID(pPStm,&clsid);
6393 if (SUCCEEDED(res)){
6395 res=WriteClassStm(pStm,&clsid);
6397 if (SUCCEEDED(res))
6399 res=IPersistStream_Save(pPStm,pStm,TRUE);
6402 TRACE("Finished Save\n");
6403 return res;
6406 /****************************************************************************
6407 * This method validate a STGM parameter that can contain the values below
6409 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6410 * The stgm values contained in 0xffff0000 are bitmasks.
6412 * STGM_DIRECT 0x00000000
6413 * STGM_TRANSACTED 0x00010000
6414 * STGM_SIMPLE 0x08000000
6416 * STGM_READ 0x00000000
6417 * STGM_WRITE 0x00000001
6418 * STGM_READWRITE 0x00000002
6420 * STGM_SHARE_DENY_NONE 0x00000040
6421 * STGM_SHARE_DENY_READ 0x00000030
6422 * STGM_SHARE_DENY_WRITE 0x00000020
6423 * STGM_SHARE_EXCLUSIVE 0x00000010
6425 * STGM_PRIORITY 0x00040000
6426 * STGM_DELETEONRELEASE 0x04000000
6428 * STGM_CREATE 0x00001000
6429 * STGM_CONVERT 0x00020000
6430 * STGM_FAILIFTHERE 0x00000000
6432 * STGM_NOSCRATCH 0x00100000
6433 * STGM_NOSNAPSHOT 0x00200000
6435 static HRESULT validateSTGM(DWORD stgm)
6437 DWORD access = STGM_ACCESS_MODE(stgm);
6438 DWORD share = STGM_SHARE_MODE(stgm);
6439 DWORD create = STGM_CREATE_MODE(stgm);
6441 if (stgm&~STGM_KNOWN_FLAGS)
6443 ERR("unknown flags %08x\n", stgm);
6444 return E_FAIL;
6447 switch (access)
6449 case STGM_READ:
6450 case STGM_WRITE:
6451 case STGM_READWRITE:
6452 break;
6453 default:
6454 return E_FAIL;
6457 switch (share)
6459 case STGM_SHARE_DENY_NONE:
6460 case STGM_SHARE_DENY_READ:
6461 case STGM_SHARE_DENY_WRITE:
6462 case STGM_SHARE_EXCLUSIVE:
6463 break;
6464 default:
6465 return E_FAIL;
6468 switch (create)
6470 case STGM_CREATE:
6471 case STGM_FAILIFTHERE:
6472 break;
6473 default:
6474 return E_FAIL;
6478 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6480 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6481 return E_FAIL;
6484 * STGM_CREATE | STGM_CONVERT
6485 * if both are false, STGM_FAILIFTHERE is set to TRUE
6487 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6488 return E_FAIL;
6491 * STGM_NOSCRATCH requires STGM_TRANSACTED
6493 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6494 return E_FAIL;
6497 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6498 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6500 if ( (stgm & STGM_NOSNAPSHOT) &&
6501 (!(stgm & STGM_TRANSACTED) ||
6502 share == STGM_SHARE_EXCLUSIVE ||
6503 share == STGM_SHARE_DENY_WRITE) )
6504 return E_FAIL;
6506 return S_OK;
6509 /****************************************************************************
6510 * GetShareModeFromSTGM
6512 * This method will return a share mode flag from a STGM value.
6513 * The STGM value is assumed valid.
6515 static DWORD GetShareModeFromSTGM(DWORD stgm)
6517 switch (STGM_SHARE_MODE(stgm))
6519 case STGM_SHARE_DENY_NONE:
6520 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6521 case STGM_SHARE_DENY_READ:
6522 return FILE_SHARE_WRITE;
6523 case STGM_SHARE_DENY_WRITE:
6524 return FILE_SHARE_READ;
6525 case STGM_SHARE_EXCLUSIVE:
6526 return 0;
6528 ERR("Invalid share mode!\n");
6529 assert(0);
6530 return 0;
6533 /****************************************************************************
6534 * GetAccessModeFromSTGM
6536 * This method will return an access mode flag from a STGM value.
6537 * The STGM value is assumed valid.
6539 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6541 switch (STGM_ACCESS_MODE(stgm))
6543 case STGM_READ:
6544 return GENERIC_READ;
6545 case STGM_WRITE:
6546 case STGM_READWRITE:
6547 return GENERIC_READ | GENERIC_WRITE;
6549 ERR("Invalid access mode!\n");
6550 assert(0);
6551 return 0;
6554 /****************************************************************************
6555 * GetCreationModeFromSTGM
6557 * This method will return a creation mode flag from a STGM value.
6558 * The STGM value is assumed valid.
6560 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6562 switch(STGM_CREATE_MODE(stgm))
6564 case STGM_CREATE:
6565 return CREATE_ALWAYS;
6566 case STGM_CONVERT:
6567 FIXME("STGM_CONVERT not implemented!\n");
6568 return CREATE_NEW;
6569 case STGM_FAILIFTHERE:
6570 return CREATE_NEW;
6572 ERR("Invalid create mode!\n");
6573 assert(0);
6574 return 0;
6578 /*************************************************************************
6579 * OLECONVERT_LoadOLE10 [Internal]
6581 * Loads the OLE10 STREAM to memory
6583 * PARAMS
6584 * pOleStream [I] The OLESTREAM
6585 * pData [I] Data Structure for the OLESTREAM Data
6587 * RETURNS
6588 * Success: S_OK
6589 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6590 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6592 * NOTES
6593 * This function is used by OleConvertOLESTREAMToIStorage only.
6595 * Memory allocated for pData must be freed by the caller
6597 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6599 DWORD dwSize;
6600 HRESULT hRes = S_OK;
6601 int nTryCnt=0;
6602 int max_try = 6;
6604 pData->pData = NULL;
6605 pData->pstrOleObjFileName = (CHAR *) NULL;
6607 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6609 /* Get the OleID */
6610 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6611 if(dwSize != sizeof(pData->dwOleID))
6613 hRes = CONVERT10_E_OLESTREAM_GET;
6615 else if(pData->dwOleID != OLESTREAM_ID)
6617 hRes = CONVERT10_E_OLESTREAM_FMT;
6619 else
6621 hRes = S_OK;
6622 break;
6626 if(hRes == S_OK)
6628 /* Get the TypeID...more info needed for this field */
6629 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6630 if(dwSize != sizeof(pData->dwTypeID))
6632 hRes = CONVERT10_E_OLESTREAM_GET;
6635 if(hRes == S_OK)
6637 if(pData->dwTypeID != 0)
6639 /* Get the length of the OleTypeName */
6640 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6641 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6643 hRes = CONVERT10_E_OLESTREAM_GET;
6646 if(hRes == S_OK)
6648 if(pData->dwOleTypeNameLength > 0)
6650 /* Get the OleTypeName */
6651 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6652 if(dwSize != pData->dwOleTypeNameLength)
6654 hRes = CONVERT10_E_OLESTREAM_GET;
6658 if(bStrem1)
6660 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6661 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6663 hRes = CONVERT10_E_OLESTREAM_GET;
6665 if(hRes == S_OK)
6667 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6668 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6669 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6670 if(pData->pstrOleObjFileName)
6672 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6673 if(dwSize != pData->dwOleObjFileNameLength)
6675 hRes = CONVERT10_E_OLESTREAM_GET;
6678 else
6679 hRes = CONVERT10_E_OLESTREAM_GET;
6682 else
6684 /* Get the Width of the Metafile */
6685 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6686 if(dwSize != sizeof(pData->dwMetaFileWidth))
6688 hRes = CONVERT10_E_OLESTREAM_GET;
6690 if(hRes == S_OK)
6692 /* Get the Height of the Metafile */
6693 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6694 if(dwSize != sizeof(pData->dwMetaFileHeight))
6696 hRes = CONVERT10_E_OLESTREAM_GET;
6700 if(hRes == S_OK)
6702 /* Get the Length of the Data */
6703 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6704 if(dwSize != sizeof(pData->dwDataLength))
6706 hRes = CONVERT10_E_OLESTREAM_GET;
6710 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6712 if(!bStrem1) /* if it is a second OLE stream data */
6714 pData->dwDataLength -= 8;
6715 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6716 if(dwSize != sizeof(pData->strUnknown))
6718 hRes = CONVERT10_E_OLESTREAM_GET;
6722 if(hRes == S_OK)
6724 if(pData->dwDataLength > 0)
6726 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6728 /* Get Data (ex. IStorage, Metafile, or BMP) */
6729 if(pData->pData)
6731 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6732 if(dwSize != pData->dwDataLength)
6734 hRes = CONVERT10_E_OLESTREAM_GET;
6737 else
6739 hRes = CONVERT10_E_OLESTREAM_GET;
6745 return hRes;
6748 /*************************************************************************
6749 * OLECONVERT_SaveOLE10 [Internal]
6751 * Saves the OLE10 STREAM From memory
6753 * PARAMS
6754 * pData [I] Data Structure for the OLESTREAM Data
6755 * pOleStream [I] The OLESTREAM to save
6757 * RETURNS
6758 * Success: S_OK
6759 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6761 * NOTES
6762 * This function is used by OleConvertIStorageToOLESTREAM only.
6765 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6767 DWORD dwSize;
6768 HRESULT hRes = S_OK;
6771 /* Set the OleID */
6772 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6773 if(dwSize != sizeof(pData->dwOleID))
6775 hRes = CONVERT10_E_OLESTREAM_PUT;
6778 if(hRes == S_OK)
6780 /* Set the TypeID */
6781 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6782 if(dwSize != sizeof(pData->dwTypeID))
6784 hRes = CONVERT10_E_OLESTREAM_PUT;
6788 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6790 /* Set the Length of the OleTypeName */
6791 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6792 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6794 hRes = CONVERT10_E_OLESTREAM_PUT;
6797 if(hRes == S_OK)
6799 if(pData->dwOleTypeNameLength > 0)
6801 /* Set the OleTypeName */
6802 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6803 if(dwSize != pData->dwOleTypeNameLength)
6805 hRes = CONVERT10_E_OLESTREAM_PUT;
6810 if(hRes == S_OK)
6812 /* Set the width of the Metafile */
6813 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6814 if(dwSize != sizeof(pData->dwMetaFileWidth))
6816 hRes = CONVERT10_E_OLESTREAM_PUT;
6820 if(hRes == S_OK)
6822 /* Set the height of the Metafile */
6823 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6824 if(dwSize != sizeof(pData->dwMetaFileHeight))
6826 hRes = CONVERT10_E_OLESTREAM_PUT;
6830 if(hRes == S_OK)
6832 /* Set the length of the Data */
6833 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6834 if(dwSize != sizeof(pData->dwDataLength))
6836 hRes = CONVERT10_E_OLESTREAM_PUT;
6840 if(hRes == S_OK)
6842 if(pData->dwDataLength > 0)
6844 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6845 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6846 if(dwSize != pData->dwDataLength)
6848 hRes = CONVERT10_E_OLESTREAM_PUT;
6853 return hRes;
6856 /*************************************************************************
6857 * OLECONVERT_GetOLE20FromOLE10[Internal]
6859 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6860 * opens it, and copies the content to the dest IStorage for
6861 * OleConvertOLESTREAMToIStorage
6864 * PARAMS
6865 * pDestStorage [I] The IStorage to copy the data to
6866 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6867 * nBufferLength [I] The size of the buffer
6869 * RETURNS
6870 * Nothing
6872 * NOTES
6876 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6878 HRESULT hRes;
6879 HANDLE hFile;
6880 IStorage *pTempStorage;
6881 DWORD dwNumOfBytesWritten;
6882 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6883 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6885 /* Create a temp File */
6886 GetTempPathW(MAX_PATH, wstrTempDir);
6887 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6888 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6890 if(hFile != INVALID_HANDLE_VALUE)
6892 /* Write IStorage Data to File */
6893 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6894 CloseHandle(hFile);
6896 /* Open and copy temp storage to the Dest Storage */
6897 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6898 if(hRes == S_OK)
6900 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6901 StorageBaseImpl_Release(pTempStorage);
6903 DeleteFileW(wstrTempFile);
6908 /*************************************************************************
6909 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6911 * Saves the OLE10 STREAM From memory
6913 * PARAMS
6914 * pStorage [I] The Src IStorage to copy
6915 * pData [I] The Dest Memory to write to.
6917 * RETURNS
6918 * The size in bytes allocated for pData
6920 * NOTES
6921 * Memory allocated for pData must be freed by the caller
6923 * Used by OleConvertIStorageToOLESTREAM only.
6926 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6928 HANDLE hFile;
6929 HRESULT hRes;
6930 DWORD nDataLength = 0;
6931 IStorage *pTempStorage;
6932 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6933 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6935 *pData = NULL;
6937 /* Create temp Storage */
6938 GetTempPathW(MAX_PATH, wstrTempDir);
6939 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6940 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6942 if(hRes == S_OK)
6944 /* Copy Src Storage to the Temp Storage */
6945 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6946 StorageBaseImpl_Release(pTempStorage);
6948 /* Open Temp Storage as a file and copy to memory */
6949 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6950 if(hFile != INVALID_HANDLE_VALUE)
6952 nDataLength = GetFileSize(hFile, NULL);
6953 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6954 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6955 CloseHandle(hFile);
6957 DeleteFileW(wstrTempFile);
6959 return nDataLength;
6962 /*************************************************************************
6963 * OLECONVERT_CreateOleStream [Internal]
6965 * Creates the "\001OLE" stream in the IStorage if necessary.
6967 * PARAMS
6968 * pStorage [I] Dest storage to create the stream in
6970 * RETURNS
6971 * Nothing
6973 * NOTES
6974 * This function is used by OleConvertOLESTREAMToIStorage only.
6976 * This stream is still unknown, MS Word seems to have extra data
6977 * but since the data is stored in the OLESTREAM there should be
6978 * no need to recreate the stream. If the stream is manually
6979 * deleted it will create it with this default data.
6982 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6984 HRESULT hRes;
6985 IStream *pStream;
6986 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6987 BYTE pOleStreamHeader [] =
6989 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6990 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6991 0x00, 0x00, 0x00, 0x00
6994 /* Create stream if not present */
6995 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6996 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6998 if(hRes == S_OK)
7000 /* Write default Data */
7001 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
7002 IStream_Release(pStream);
7006 /* write a string to a stream, preceded by its length */
7007 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7009 HRESULT r;
7010 LPSTR str;
7011 DWORD len = 0;
7013 if( string )
7014 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7015 r = IStream_Write( stm, &len, sizeof(len), NULL);
7016 if( FAILED( r ) )
7017 return r;
7018 if(len == 0)
7019 return r;
7020 str = CoTaskMemAlloc( len );
7021 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7022 r = IStream_Write( stm, str, len, NULL);
7023 CoTaskMemFree( str );
7024 return r;
7027 /* read a string preceded by its length from a stream */
7028 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7030 HRESULT r;
7031 DWORD len, count = 0;
7032 LPSTR str;
7033 LPWSTR wstr;
7035 r = IStream_Read( stm, &len, sizeof(len), &count );
7036 if( FAILED( r ) )
7037 return r;
7038 if( count != sizeof(len) )
7039 return E_OUTOFMEMORY;
7041 TRACE("%d bytes\n",len);
7043 str = CoTaskMemAlloc( len );
7044 if( !str )
7045 return E_OUTOFMEMORY;
7046 count = 0;
7047 r = IStream_Read( stm, str, len, &count );
7048 if( FAILED( r ) )
7049 return r;
7050 if( count != len )
7052 CoTaskMemFree( str );
7053 return E_OUTOFMEMORY;
7056 TRACE("Read string %s\n",debugstr_an(str,len));
7058 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7059 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7060 if( wstr )
7061 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7062 CoTaskMemFree( str );
7064 *string = wstr;
7066 return r;
7070 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7071 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7073 IStream *pstm;
7074 HRESULT r = S_OK;
7075 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7077 static const BYTE unknown1[12] =
7078 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7079 0xFF, 0xFF, 0xFF, 0xFF};
7080 static const BYTE unknown2[16] =
7081 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7082 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7084 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7085 debugstr_w(lpszUserType), debugstr_w(szClipName),
7086 debugstr_w(szProgIDName));
7088 /* Create a CompObj stream if it doesn't exist */
7089 r = IStorage_CreateStream(pstg, szwStreamName,
7090 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7091 if( FAILED (r) )
7092 return r;
7094 /* Write CompObj Structure to stream */
7095 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7097 if( SUCCEEDED( r ) )
7098 r = WriteClassStm( pstm, clsid );
7100 if( SUCCEEDED( r ) )
7101 r = STREAM_WriteString( pstm, lpszUserType );
7102 if( SUCCEEDED( r ) )
7103 r = STREAM_WriteString( pstm, szClipName );
7104 if( SUCCEEDED( r ) )
7105 r = STREAM_WriteString( pstm, szProgIDName );
7106 if( SUCCEEDED( r ) )
7107 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7109 IStream_Release( pstm );
7111 return r;
7114 /***********************************************************************
7115 * WriteFmtUserTypeStg (OLE32.@)
7117 HRESULT WINAPI WriteFmtUserTypeStg(
7118 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7120 HRESULT r;
7121 WCHAR szwClipName[0x40];
7122 CLSID clsid = CLSID_NULL;
7123 LPWSTR wstrProgID = NULL;
7124 DWORD n;
7126 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7128 /* get the clipboard format name */
7129 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7130 szwClipName[n]=0;
7132 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7134 /* FIXME: There's room to save a CLSID and its ProgID, but
7135 the CLSID is not looked up in the registry and in all the
7136 tests I wrote it was CLSID_NULL. Where does it come from?
7139 /* get the real program ID. This may fail, but that's fine */
7140 ProgIDFromCLSID(&clsid, &wstrProgID);
7142 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7144 r = STORAGE_WriteCompObj( pstg, &clsid,
7145 lpszUserType, szwClipName, wstrProgID );
7147 CoTaskMemFree(wstrProgID);
7149 return r;
7153 /******************************************************************************
7154 * ReadFmtUserTypeStg [OLE32.@]
7156 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7158 HRESULT r;
7159 IStream *stm = 0;
7160 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7161 unsigned char unknown1[12];
7162 unsigned char unknown2[16];
7163 DWORD count;
7164 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7165 CLSID clsid;
7167 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7169 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7170 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7171 if( FAILED ( r ) )
7173 WARN("Failed to open stream r = %08x\n", r);
7174 return r;
7177 /* read the various parts of the structure */
7178 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7179 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7180 goto end;
7181 r = ReadClassStm( stm, &clsid );
7182 if( FAILED( r ) )
7183 goto end;
7185 r = STREAM_ReadString( stm, &szCLSIDName );
7186 if( FAILED( r ) )
7187 goto end;
7189 r = STREAM_ReadString( stm, &szOleTypeName );
7190 if( FAILED( r ) )
7191 goto end;
7193 r = STREAM_ReadString( stm, &szProgIDName );
7194 if( FAILED( r ) )
7195 goto end;
7197 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7198 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7199 goto end;
7201 /* ok, success... now we just need to store what we found */
7202 if( pcf )
7203 *pcf = RegisterClipboardFormatW( szOleTypeName );
7204 CoTaskMemFree( szOleTypeName );
7206 if( lplpszUserType )
7207 *lplpszUserType = szCLSIDName;
7208 CoTaskMemFree( szProgIDName );
7210 end:
7211 IStream_Release( stm );
7213 return r;
7217 /*************************************************************************
7218 * OLECONVERT_CreateCompObjStream [Internal]
7220 * Creates a "\001CompObj" is the destination IStorage if necessary.
7222 * PARAMS
7223 * pStorage [I] The dest IStorage to create the CompObj Stream
7224 * if necessary.
7225 * strOleTypeName [I] The ProgID
7227 * RETURNS
7228 * Success: S_OK
7229 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7231 * NOTES
7232 * This function is used by OleConvertOLESTREAMToIStorage only.
7234 * The stream data is stored in the OLESTREAM and there should be
7235 * no need to recreate the stream. If the stream is manually
7236 * deleted it will attempt to create it by querying the registry.
7240 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7242 IStream *pStream;
7243 HRESULT hStorageRes, hRes = S_OK;
7244 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7245 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7246 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7248 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7249 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7251 /* Initialize the CompObj structure */
7252 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7253 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7254 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7257 /* Create a CompObj stream if it doesn't exist */
7258 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7259 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7260 if(hStorageRes == S_OK)
7262 /* copy the OleTypeName to the compobj struct */
7263 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7264 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7266 /* copy the OleTypeName to the compobj struct */
7267 /* Note: in the test made, these were Identical */
7268 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7269 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7271 /* Get the CLSID */
7272 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7273 bufferW, OLESTREAM_MAX_STR_LEN );
7274 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7276 if(hRes == S_OK)
7278 HKEY hKey;
7279 LONG hErr;
7280 /* Get the CLSID Default Name from the Registry */
7281 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7282 if(hErr == ERROR_SUCCESS)
7284 char strTemp[OLESTREAM_MAX_STR_LEN];
7285 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7286 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7287 if(hErr == ERROR_SUCCESS)
7289 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7291 RegCloseKey(hKey);
7295 /* Write CompObj Structure to stream */
7296 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7298 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7300 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7301 if(IStorageCompObj.dwCLSIDNameLength > 0)
7303 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7305 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7306 if(IStorageCompObj.dwOleTypeNameLength > 0)
7308 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7310 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7311 if(IStorageCompObj.dwProgIDNameLength > 0)
7313 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7315 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7316 IStream_Release(pStream);
7318 return hRes;
7322 /*************************************************************************
7323 * OLECONVERT_CreateOlePresStream[Internal]
7325 * Creates the "\002OlePres000" Stream with the Metafile data
7327 * PARAMS
7328 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7329 * dwExtentX [I] Width of the Metafile
7330 * dwExtentY [I] Height of the Metafile
7331 * pData [I] Metafile data
7332 * dwDataLength [I] Size of the Metafile data
7334 * RETURNS
7335 * Success: S_OK
7336 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7338 * NOTES
7339 * This function is used by OleConvertOLESTREAMToIStorage only.
7342 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7344 HRESULT hRes;
7345 IStream *pStream;
7346 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7347 BYTE pOlePresStreamHeader [] =
7349 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7350 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7351 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7352 0x00, 0x00, 0x00, 0x00
7355 BYTE pOlePresStreamHeaderEmpty [] =
7357 0x00, 0x00, 0x00, 0x00,
7358 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7359 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7360 0x00, 0x00, 0x00, 0x00
7363 /* Create the OlePres000 Stream */
7364 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7365 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7367 if(hRes == S_OK)
7369 DWORD nHeaderSize;
7370 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7372 memset(&OlePres, 0, sizeof(OlePres));
7373 /* Do we have any metafile data to save */
7374 if(dwDataLength > 0)
7376 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7377 nHeaderSize = sizeof(pOlePresStreamHeader);
7379 else
7381 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7382 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7384 /* Set width and height of the metafile */
7385 OlePres.dwExtentX = dwExtentX;
7386 OlePres.dwExtentY = -dwExtentY;
7388 /* Set Data and Length */
7389 if(dwDataLength > sizeof(METAFILEPICT16))
7391 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7392 OlePres.pData = &(pData[8]);
7394 /* Save OlePres000 Data to Stream */
7395 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7396 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7397 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7398 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7399 if(OlePres.dwSize > 0)
7401 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7403 IStream_Release(pStream);
7407 /*************************************************************************
7408 * OLECONVERT_CreateOle10NativeStream [Internal]
7410 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7412 * PARAMS
7413 * pStorage [I] Dest storage to create the stream in
7414 * pData [I] Ole10 Native Data (ex. bmp)
7415 * dwDataLength [I] Size of the Ole10 Native Data
7417 * RETURNS
7418 * Nothing
7420 * NOTES
7421 * This function is used by OleConvertOLESTREAMToIStorage only.
7423 * Might need to verify the data and return appropriate error message
7426 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7428 HRESULT hRes;
7429 IStream *pStream;
7430 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7432 /* Create the Ole10Native Stream */
7433 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7434 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7436 if(hRes == S_OK)
7438 /* Write info to stream */
7439 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7440 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7441 IStream_Release(pStream);
7446 /*************************************************************************
7447 * OLECONVERT_GetOLE10ProgID [Internal]
7449 * Finds the ProgID (or OleTypeID) from the IStorage
7451 * PARAMS
7452 * pStorage [I] The Src IStorage to get the ProgID
7453 * strProgID [I] the ProgID string to get
7454 * dwSize [I] the size of the string
7456 * RETURNS
7457 * Success: S_OK
7458 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7460 * NOTES
7461 * This function is used by OleConvertIStorageToOLESTREAM only.
7465 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7467 HRESULT hRes;
7468 IStream *pStream;
7469 LARGE_INTEGER iSeekPos;
7470 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7471 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7473 /* Open the CompObj Stream */
7474 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7475 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7476 if(hRes == S_OK)
7479 /*Get the OleType from the CompObj Stream */
7480 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7481 iSeekPos.u.HighPart = 0;
7483 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7484 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7485 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7486 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7487 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7488 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7489 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7491 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7492 if(*dwSize > 0)
7494 IStream_Read(pStream, strProgID, *dwSize, NULL);
7496 IStream_Release(pStream);
7498 else
7500 STATSTG stat;
7501 LPOLESTR wstrProgID;
7503 /* Get the OleType from the registry */
7504 REFCLSID clsid = &(stat.clsid);
7505 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7506 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7507 if(hRes == S_OK)
7509 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7513 return hRes;
7516 /*************************************************************************
7517 * OLECONVERT_GetOle10PresData [Internal]
7519 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7521 * PARAMS
7522 * pStorage [I] Src IStroage
7523 * pOleStream [I] Dest OleStream Mem Struct
7525 * RETURNS
7526 * Nothing
7528 * NOTES
7529 * This function is used by OleConvertIStorageToOLESTREAM only.
7531 * Memory allocated for pData must be freed by the caller
7535 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7538 HRESULT hRes;
7539 IStream *pStream;
7540 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7542 /* Initialize Default data for OLESTREAM */
7543 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7544 pOleStreamData[0].dwTypeID = 2;
7545 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7546 pOleStreamData[1].dwTypeID = 0;
7547 pOleStreamData[0].dwMetaFileWidth = 0;
7548 pOleStreamData[0].dwMetaFileHeight = 0;
7549 pOleStreamData[0].pData = NULL;
7550 pOleStreamData[1].pData = NULL;
7552 /* Open Ole10Native Stream */
7553 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7554 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7555 if(hRes == S_OK)
7558 /* Read Size and Data */
7559 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7560 if(pOleStreamData->dwDataLength > 0)
7562 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7563 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7565 IStream_Release(pStream);
7571 /*************************************************************************
7572 * OLECONVERT_GetOle20PresData[Internal]
7574 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7576 * PARAMS
7577 * pStorage [I] Src IStroage
7578 * pOleStreamData [I] Dest OleStream Mem Struct
7580 * RETURNS
7581 * Nothing
7583 * NOTES
7584 * This function is used by OleConvertIStorageToOLESTREAM only.
7586 * Memory allocated for pData must be freed by the caller
7588 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7590 HRESULT hRes;
7591 IStream *pStream;
7592 OLECONVERT_ISTORAGE_OLEPRES olePress;
7593 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7595 /* Initialize Default data for OLESTREAM */
7596 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7597 pOleStreamData[0].dwTypeID = 2;
7598 pOleStreamData[0].dwMetaFileWidth = 0;
7599 pOleStreamData[0].dwMetaFileHeight = 0;
7600 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7601 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7602 pOleStreamData[1].dwTypeID = 0;
7603 pOleStreamData[1].dwOleTypeNameLength = 0;
7604 pOleStreamData[1].strOleTypeName[0] = 0;
7605 pOleStreamData[1].dwMetaFileWidth = 0;
7606 pOleStreamData[1].dwMetaFileHeight = 0;
7607 pOleStreamData[1].pData = NULL;
7608 pOleStreamData[1].dwDataLength = 0;
7611 /* Open OlePress000 stream */
7612 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7613 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7614 if(hRes == S_OK)
7616 LARGE_INTEGER iSeekPos;
7617 METAFILEPICT16 MetaFilePict;
7618 static const char strMetafilePictName[] = "METAFILEPICT";
7620 /* Set the TypeID for a Metafile */
7621 pOleStreamData[1].dwTypeID = 5;
7623 /* Set the OleTypeName to Metafile */
7624 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7625 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7627 iSeekPos.u.HighPart = 0;
7628 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7630 /* Get Presentation Data */
7631 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7632 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7633 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7634 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7636 /*Set width and Height */
7637 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7638 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7639 if(olePress.dwSize > 0)
7641 /* Set Length */
7642 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7644 /* Set MetaFilePict struct */
7645 MetaFilePict.mm = 8;
7646 MetaFilePict.xExt = olePress.dwExtentX;
7647 MetaFilePict.yExt = olePress.dwExtentY;
7648 MetaFilePict.hMF = 0;
7650 /* Get Metafile Data */
7651 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7652 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7653 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7655 IStream_Release(pStream);
7659 /*************************************************************************
7660 * OleConvertOLESTREAMToIStorage [OLE32.@]
7662 * Read info on MSDN
7664 * TODO
7665 * DVTARGETDEVICE paramenter is not handled
7666 * Still unsure of some mem fields for OLE 10 Stream
7667 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7668 * and "\001OLE" streams
7671 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7672 LPOLESTREAM pOleStream,
7673 LPSTORAGE pstg,
7674 const DVTARGETDEVICE* ptd)
7676 int i;
7677 HRESULT hRes=S_OK;
7678 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7680 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7682 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7684 if(ptd != NULL)
7686 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7689 if(pstg == NULL || pOleStream == NULL)
7691 hRes = E_INVALIDARG;
7694 if(hRes == S_OK)
7696 /* Load the OLESTREAM to Memory */
7697 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7700 if(hRes == S_OK)
7702 /* Load the OLESTREAM to Memory (part 2)*/
7703 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7706 if(hRes == S_OK)
7709 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7711 /* Do we have the IStorage Data in the OLESTREAM */
7712 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7714 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7715 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7717 else
7719 /* It must be an original OLE 1.0 source */
7720 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7723 else
7725 /* It must be an original OLE 1.0 source */
7726 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7729 /* Create CompObj Stream if necessary */
7730 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7731 if(hRes == S_OK)
7733 /*Create the Ole Stream if necessary */
7734 OLECONVERT_CreateOleStream(pstg);
7739 /* Free allocated memory */
7740 for(i=0; i < 2; i++)
7742 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7743 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7744 pOleStreamData[i].pstrOleObjFileName = NULL;
7746 return hRes;
7749 /*************************************************************************
7750 * OleConvertIStorageToOLESTREAM [OLE32.@]
7752 * Read info on MSDN
7754 * Read info on MSDN
7756 * TODO
7757 * Still unsure of some mem fields for OLE 10 Stream
7758 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7759 * and "\001OLE" streams.
7762 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7763 LPSTORAGE pstg,
7764 LPOLESTREAM pOleStream)
7766 int i;
7767 HRESULT hRes = S_OK;
7768 IStream *pStream;
7769 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7770 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7772 TRACE("%p %p\n", pstg, pOleStream);
7774 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7776 if(pstg == NULL || pOleStream == NULL)
7778 hRes = E_INVALIDARG;
7780 if(hRes == S_OK)
7782 /* Get the ProgID */
7783 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7784 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7786 if(hRes == S_OK)
7788 /* Was it originally Ole10 */
7789 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7790 if(hRes == S_OK)
7792 IStream_Release(pStream);
7793 /* Get Presentation Data for Ole10Native */
7794 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7796 else
7798 /* Get Presentation Data (OLE20) */
7799 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7802 /* Save OLESTREAM */
7803 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7804 if(hRes == S_OK)
7806 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7811 /* Free allocated memory */
7812 for(i=0; i < 2; i++)
7814 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7817 return hRes;
7820 /***********************************************************************
7821 * GetConvertStg (OLE32.@)
7823 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7824 FIXME("unimplemented stub!\n");
7825 return E_FAIL;
7828 /******************************************************************************
7829 * StgIsStorageFile [OLE32.@]
7830 * Verify if the file contains a storage object
7832 * PARAMS
7833 * fn [ I] Filename
7835 * RETURNS
7836 * S_OK if file has magic bytes as a storage object
7837 * S_FALSE if file is not storage
7839 HRESULT WINAPI
7840 StgIsStorageFile(LPCOLESTR fn)
7842 HANDLE hf;
7843 BYTE magic[8];
7844 DWORD bytes_read;
7846 TRACE("%s\n", debugstr_w(fn));
7847 hf = CreateFileW(fn, GENERIC_READ,
7848 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7849 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7851 if (hf == INVALID_HANDLE_VALUE)
7852 return STG_E_FILENOTFOUND;
7854 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7856 WARN(" unable to read file\n");
7857 CloseHandle(hf);
7858 return S_FALSE;
7861 CloseHandle(hf);
7863 if (bytes_read != 8) {
7864 WARN(" too short\n");
7865 return S_FALSE;
7868 if (!memcmp(magic,STORAGE_magic,8)) {
7869 WARN(" -> YES\n");
7870 return S_OK;
7873 WARN(" -> Invalid header.\n");
7874 return S_FALSE;
7877 /***********************************************************************
7878 * WriteClassStm (OLE32.@)
7880 * Writes a CLSID to a stream.
7882 * PARAMS
7883 * pStm [I] Stream to write to.
7884 * rclsid [I] CLSID to write.
7886 * RETURNS
7887 * Success: S_OK.
7888 * Failure: HRESULT code.
7890 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7892 TRACE("(%p,%p)\n",pStm,rclsid);
7894 if (!pStm || !rclsid)
7895 return E_INVALIDARG;
7897 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7900 /***********************************************************************
7901 * ReadClassStm (OLE32.@)
7903 * Reads a CLSID from a stream.
7905 * PARAMS
7906 * pStm [I] Stream to read from.
7907 * rclsid [O] CLSID to read.
7909 * RETURNS
7910 * Success: S_OK.
7911 * Failure: HRESULT code.
7913 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7915 ULONG nbByte;
7916 HRESULT res;
7918 TRACE("(%p,%p)\n",pStm,pclsid);
7920 if (!pStm || !pclsid)
7921 return E_INVALIDARG;
7923 /* clear the output args */
7924 memcpy(pclsid, &CLSID_NULL, sizeof(*pclsid));
7926 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7928 if (FAILED(res))
7929 return res;
7931 if (nbByte != sizeof(CLSID))
7932 return STG_E_READFAULT;
7933 else
7934 return S_OK;