mlang/tests: Use a table for testing GetLcidFromRfc1766.
[wine.git] / dlls / ole32 / storage32.c
blob748eac8048a78caaaff827d616c23987bb6d53a4
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "winreg.h"
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootPropertyName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
81 * There is no specific data for this class.
84 typedef struct StorageInternalImpl StorageInternalImpl;
86 /* Method definitions for the Storage32InternalImpl class. */
87 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
88 DWORD openFlags, ULONG rootTropertyIndex);
89 static void StorageImpl_Destroy(StorageBaseImpl* iface);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const void* buffer);
92 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
93 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
94 static void StorageImpl_SaveFileHeader(StorageImpl* This);
96 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
97 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
98 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
100 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
102 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
103 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
104 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
106 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
107 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
108 ULONG blockIndex, ULONG offset, DWORD value);
109 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
110 ULONG blockIndex, ULONG offset, DWORD* value);
112 /* OLESTREAM memory structure to use for Get and Put Routines */
113 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
114 typedef struct
116 DWORD dwOleID;
117 DWORD dwTypeID;
118 DWORD dwOleTypeNameLength;
119 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
120 CHAR *pstrOleObjFileName;
121 DWORD dwOleObjFileNameLength;
122 DWORD dwMetaFileWidth;
123 DWORD dwMetaFileHeight;
124 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
125 DWORD dwDataLength;
126 BYTE *pData;
127 }OLECONVERT_OLESTREAM_DATA;
129 /* CompObj Stream structure */
130 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
131 typedef struct
133 BYTE byUnknown1[12];
134 CLSID clsid;
135 DWORD dwCLSIDNameLength;
136 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
137 DWORD dwOleTypeNameLength;
138 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
139 DWORD dwProgIDNameLength;
140 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
141 BYTE byUnknown2[16];
142 }OLECONVERT_ISTORAGE_COMPOBJ;
145 /* Ole Presentation Stream structure */
146 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
147 typedef struct
149 BYTE byUnknown1[28];
150 DWORD dwExtentX;
151 DWORD dwExtentY;
152 DWORD dwSize;
153 BYTE *pData;
154 }OLECONVERT_ISTORAGE_OLEPRES;
158 /***********************************************************************
159 * Forward declaration of internal functions used by the method DestroyElement
161 static HRESULT deleteStorageProperty(
162 StorageImpl *parentStorage,
163 ULONG foundPropertyIndexToDelete,
164 StgProperty propertyToDelete);
166 static HRESULT deleteStreamProperty(
167 StorageImpl *parentStorage,
168 ULONG foundPropertyIndexToDelete,
169 StgProperty propertyToDelete);
171 static HRESULT findPlaceholder(
172 StorageImpl *storage,
173 ULONG propertyIndexToStore,
174 ULONG storagePropertyIndex,
175 INT typeOfRelation);
177 static HRESULT adjustPropertyChain(
178 StorageImpl *This,
179 StgProperty propertyToDelete,
180 StgProperty parentProperty,
181 ULONG parentPropertyId,
182 INT typeOfRelation);
184 /***********************************************************************
185 * Declaration of the functions used to manipulate StgProperty
188 static ULONG getFreeProperty(
189 StorageImpl *storage);
191 static void updatePropertyChain(
192 StorageImpl *storage,
193 ULONG newPropertyIndex,
194 StgProperty newProperty);
196 static LONG propertyNameCmp(
197 const OLECHAR *newProperty,
198 const OLECHAR *currentProperty);
201 /***********************************************************************
202 * Declaration of miscellaneous functions...
204 static HRESULT validateSTGM(DWORD stgmValue);
206 static DWORD GetShareModeFromSTGM(DWORD stgm);
207 static DWORD GetAccessModeFromSTGM(DWORD stgm);
208 static DWORD GetCreationModeFromSTGM(DWORD stgm);
210 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
213 /****************************************************************************
214 * IEnumSTATSTGImpl definitions.
216 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
217 * This class allows iterating through the content of a storage and to find
218 * specific items inside it.
220 struct IEnumSTATSTGImpl
222 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
223 * since we want to cast this in an IEnumSTATSTG pointer */
225 LONG ref; /* Reference count */
226 StorageImpl* parentStorage; /* Reference to the parent storage */
227 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
230 * The current implementation of the IEnumSTATSTGImpl class uses a stack
231 * to walk the property sets to get the content of a storage. This stack
232 * is implemented by the following 3 data members
234 ULONG stackSize;
235 ULONG stackMaxSize;
236 ULONG* stackToVisit;
238 #define ENUMSTATSGT_SIZE_INCREMENT 10
242 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
243 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
244 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
245 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
246 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
247 StgProperty* buffer);
248 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
249 StgProperty *currentProperty, ULONG *propertyId);
251 /************************************************************************
252 ** Block Functions
255 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
257 if (index == 0xffffffff)
258 index = 0;
259 else
260 index ++;
262 return index * BIG_BLOCK_SIZE;
265 /************************************************************************
266 ** Storage32BaseImpl implementation
268 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
269 ULARGE_INTEGER offset,
270 void* buffer,
271 ULONG size,
272 ULONG* bytesRead)
274 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
277 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
278 ULARGE_INTEGER offset,
279 const void* buffer,
280 const ULONG size,
281 ULONG* bytesWritten)
283 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
286 /************************************************************************
287 * Storage32BaseImpl_QueryInterface (IUnknown)
289 * This method implements the common QueryInterface for all IStorage32
290 * implementations contained in this file.
292 * See Windows documentation for more details on IUnknown methods.
294 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
295 IStorage* iface,
296 REFIID riid,
297 void** ppvObject)
299 StorageBaseImpl *This = (StorageBaseImpl *)iface;
301 * Perform a sanity check on the parameters.
303 if ( (This==0) || (ppvObject==0) )
304 return E_INVALIDARG;
307 * Initialize the return parameter.
309 *ppvObject = 0;
312 * Compare the riid with the interface IDs implemented by this object.
314 if (IsEqualGUID(&IID_IUnknown, riid) ||
315 IsEqualGUID(&IID_IStorage, riid))
317 *ppvObject = This;
319 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
321 *ppvObject = &This->pssVtbl;
325 * Check that we obtained an interface.
327 if ((*ppvObject)==0)
328 return E_NOINTERFACE;
331 * Query Interface always increases the reference count by one when it is
332 * successful
334 IStorage_AddRef(iface);
336 return S_OK;
339 /************************************************************************
340 * Storage32BaseImpl_AddRef (IUnknown)
342 * This method implements the common AddRef for all IStorage32
343 * implementations contained in this file.
345 * See Windows documentation for more details on IUnknown methods.
347 static ULONG WINAPI StorageBaseImpl_AddRef(
348 IStorage* iface)
350 StorageBaseImpl *This = (StorageBaseImpl *)iface;
351 ULONG ref = InterlockedIncrement(&This->ref);
353 TRACE("(%p) AddRef to %d\n", This, ref);
355 return ref;
358 /************************************************************************
359 * Storage32BaseImpl_Release (IUnknown)
361 * This method implements the common Release for all IStorage32
362 * implementations contained in this file.
364 * See Windows documentation for more details on IUnknown methods.
366 static ULONG WINAPI StorageBaseImpl_Release(
367 IStorage* iface)
369 StorageBaseImpl *This = (StorageBaseImpl *)iface;
371 * Decrease the reference count on this object.
373 ULONG ref = InterlockedDecrement(&This->ref);
375 TRACE("(%p) ReleaseRef to %d\n", This, ref);
378 * If the reference count goes down to 0, perform suicide.
380 if (ref == 0)
383 * Since we are using a system of base-classes, we want to call the
384 * destructor of the appropriate derived class. To do this, we are
385 * using virtual functions to implement the destructor.
387 This->v_destructor(This);
390 return ref;
393 /************************************************************************
394 * Storage32BaseImpl_OpenStream (IStorage)
396 * This method will open the specified stream object from the current storage.
398 * See Windows documentation for more details on IStorage methods.
400 static HRESULT WINAPI StorageBaseImpl_OpenStream(
401 IStorage* iface,
402 const OLECHAR* pwcsName, /* [string][in] */
403 void* reserved1, /* [unique][in] */
404 DWORD grfMode, /* [in] */
405 DWORD reserved2, /* [in] */
406 IStream** ppstm) /* [out] */
408 StorageBaseImpl *This = (StorageBaseImpl *)iface;
409 IEnumSTATSTGImpl* propertyEnumeration;
410 StgStreamImpl* newStream;
411 StgProperty currentProperty;
412 ULONG foundPropertyIndex;
413 HRESULT res = STG_E_UNKNOWN;
415 TRACE("(%p, %s, %p, %x, %d, %p)\n",
416 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
419 * Perform a sanity check on the parameters.
421 if ( (pwcsName==NULL) || (ppstm==0) )
423 res = E_INVALIDARG;
424 goto end;
428 * Initialize the out parameter
430 *ppstm = NULL;
433 * Validate the STGM flags
435 if ( FAILED( validateSTGM(grfMode) ) ||
436 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
438 res = STG_E_INVALIDFLAG;
439 goto end;
443 * As documented.
445 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
447 res = STG_E_INVALIDFUNCTION;
448 goto end;
452 * Check that we're compatible with the parent's storage mode, but
453 * only if we are not in transacted mode
455 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
456 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
458 res = STG_E_ACCESSDENIED;
459 goto end;
464 * Create a property enumeration to search the properties
466 propertyEnumeration = IEnumSTATSTGImpl_Construct(
467 This->ancestorStorage,
468 This->rootPropertySetIndex);
471 * Search the enumeration for the property with the given name
473 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
474 propertyEnumeration,
475 pwcsName,
476 &currentProperty);
479 * Delete the property enumeration since we don't need it anymore
481 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
484 * If it was found, construct the stream object and return a pointer to it.
486 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
487 (currentProperty.propertyType==PROPTYPE_STREAM) )
489 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
491 if (newStream!=0)
493 newStream->grfMode = grfMode;
494 *ppstm = (IStream*)newStream;
497 * Since we are returning a pointer to the interface, we have to
498 * nail down the reference.
500 IStream_AddRef(*ppstm);
502 res = S_OK;
503 goto end;
506 res = E_OUTOFMEMORY;
507 goto end;
510 res = STG_E_FILENOTFOUND;
512 end:
513 if (res == S_OK)
514 TRACE("<-- IStream %p\n", *ppstm);
515 TRACE("<-- %08x\n", res);
516 return res;
519 /************************************************************************
520 * Storage32BaseImpl_OpenStorage (IStorage)
522 * This method will open a new storage object from the current storage.
524 * See Windows documentation for more details on IStorage methods.
526 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
527 IStorage* iface,
528 const OLECHAR* pwcsName, /* [string][unique][in] */
529 IStorage* pstgPriority, /* [unique][in] */
530 DWORD grfMode, /* [in] */
531 SNB snbExclude, /* [unique][in] */
532 DWORD reserved, /* [in] */
533 IStorage** ppstg) /* [out] */
535 StorageBaseImpl *This = (StorageBaseImpl *)iface;
536 StorageInternalImpl* newStorage;
537 IEnumSTATSTGImpl* propertyEnumeration;
538 StgProperty currentProperty;
539 ULONG foundPropertyIndex;
540 HRESULT res = STG_E_UNKNOWN;
542 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
543 iface, debugstr_w(pwcsName), pstgPriority,
544 grfMode, snbExclude, reserved, ppstg);
547 * Perform a sanity check on the parameters.
549 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
551 res = E_INVALIDARG;
552 goto end;
555 /* as documented */
556 if (snbExclude != NULL)
558 res = STG_E_INVALIDPARAMETER;
559 goto end;
563 * Validate the STGM flags
565 if ( FAILED( validateSTGM(grfMode) ))
567 res = STG_E_INVALIDFLAG;
568 goto end;
572 * As documented.
574 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
575 (grfMode & STGM_DELETEONRELEASE) ||
576 (grfMode & STGM_PRIORITY) )
578 res = STG_E_INVALIDFUNCTION;
579 goto end;
583 * Check that we're compatible with the parent's storage mode,
584 * but only if we are not transacted
586 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
587 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
589 res = STG_E_ACCESSDENIED;
590 goto end;
595 * Initialize the out parameter
597 *ppstg = NULL;
600 * Create a property enumeration to search the properties
602 propertyEnumeration = IEnumSTATSTGImpl_Construct(
603 This->ancestorStorage,
604 This->rootPropertySetIndex);
607 * Search the enumeration for the property with the given name
609 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
610 propertyEnumeration,
611 pwcsName,
612 &currentProperty);
615 * Delete the property enumeration since we don't need it anymore
617 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
620 * If it was found, construct the stream object and return a pointer to it.
622 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
623 (currentProperty.propertyType==PROPTYPE_STORAGE) )
626 * Construct a new Storage object
628 newStorage = StorageInternalImpl_Construct(
629 This->ancestorStorage,
630 grfMode,
631 foundPropertyIndex);
633 if (newStorage != 0)
635 *ppstg = (IStorage*)newStorage;
638 * Since we are returning a pointer to the interface,
639 * we have to nail down the reference.
641 StorageBaseImpl_AddRef(*ppstg);
643 res = S_OK;
644 goto end;
647 res = STG_E_INSUFFICIENTMEMORY;
648 goto end;
651 res = STG_E_FILENOTFOUND;
653 end:
654 TRACE("<-- %08x\n", res);
655 return res;
658 /************************************************************************
659 * Storage32BaseImpl_EnumElements (IStorage)
661 * This method will create an enumerator object that can be used to
662 * retrieve information about all the properties in the storage object.
664 * See Windows documentation for more details on IStorage methods.
666 static HRESULT WINAPI StorageBaseImpl_EnumElements(
667 IStorage* iface,
668 DWORD reserved1, /* [in] */
669 void* reserved2, /* [size_is][unique][in] */
670 DWORD reserved3, /* [in] */
671 IEnumSTATSTG** ppenum) /* [out] */
673 StorageBaseImpl *This = (StorageBaseImpl *)iface;
674 IEnumSTATSTGImpl* newEnum;
676 TRACE("(%p, %d, %p, %d, %p)\n",
677 iface, reserved1, reserved2, reserved3, ppenum);
680 * Perform a sanity check on the parameters.
682 if ( (This==0) || (ppenum==0))
683 return E_INVALIDARG;
686 * Construct the enumerator.
688 newEnum = IEnumSTATSTGImpl_Construct(
689 This->ancestorStorage,
690 This->rootPropertySetIndex);
692 if (newEnum!=0)
694 *ppenum = (IEnumSTATSTG*)newEnum;
697 * Don't forget to nail down a reference to the new object before
698 * returning it.
700 IEnumSTATSTG_AddRef(*ppenum);
702 return S_OK;
705 return E_OUTOFMEMORY;
708 /************************************************************************
709 * Storage32BaseImpl_Stat (IStorage)
711 * This method will retrieve information about this storage object.
713 * See Windows documentation for more details on IStorage methods.
715 static HRESULT WINAPI StorageBaseImpl_Stat(
716 IStorage* iface,
717 STATSTG* pstatstg, /* [out] */
718 DWORD grfStatFlag) /* [in] */
720 StorageBaseImpl *This = (StorageBaseImpl *)iface;
721 StgProperty curProperty;
722 BOOL readSuccessful;
723 HRESULT res = STG_E_UNKNOWN;
725 TRACE("(%p, %p, %x)\n",
726 iface, pstatstg, grfStatFlag);
729 * Perform a sanity check on the parameters.
731 if ( (This==0) || (pstatstg==0))
733 res = E_INVALIDARG;
734 goto end;
738 * Read the information from the property.
740 readSuccessful = StorageImpl_ReadProperty(
741 This->ancestorStorage,
742 This->rootPropertySetIndex,
743 &curProperty);
745 if (readSuccessful)
747 StorageUtl_CopyPropertyToSTATSTG(
748 pstatstg,
749 &curProperty,
750 grfStatFlag);
752 pstatstg->grfMode = This->openFlags;
753 pstatstg->grfStateBits = This->stateBits;
755 res = S_OK;
756 goto end;
759 res = E_FAIL;
761 end:
762 if (res == S_OK)
764 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
766 TRACE("<-- %08x\n", res);
767 return res;
770 /************************************************************************
771 * Storage32BaseImpl_RenameElement (IStorage)
773 * This method will rename the specified element.
775 * See Windows documentation for more details on IStorage methods.
777 * Implementation notes: The method used to rename consists of creating a clone
778 * of the deleted StgProperty object setting it with the new name and to
779 * perform a DestroyElement of the old StgProperty.
781 static HRESULT WINAPI StorageBaseImpl_RenameElement(
782 IStorage* iface,
783 const OLECHAR* pwcsOldName, /* [in] */
784 const OLECHAR* pwcsNewName) /* [in] */
786 StorageBaseImpl *This = (StorageBaseImpl *)iface;
787 IEnumSTATSTGImpl* propertyEnumeration;
788 StgProperty currentProperty;
789 ULONG foundPropertyIndex;
791 TRACE("(%p, %s, %s)\n",
792 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
795 * Create a property enumeration to search the properties
797 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
798 This->rootPropertySetIndex);
801 * Search the enumeration for the new property name
803 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
804 pwcsNewName,
805 &currentProperty);
807 if (foundPropertyIndex != PROPERTY_NULL)
810 * There is already a property with the new name
812 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
813 return STG_E_FILEALREADYEXISTS;
816 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
819 * Search the enumeration for the old property name
821 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
822 pwcsOldName,
823 &currentProperty);
826 * Delete the property enumeration since we don't need it anymore
828 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
830 if (foundPropertyIndex != PROPERTY_NULL)
832 StgProperty renamedProperty;
833 ULONG renamedPropertyIndex;
836 * Setup a new property for the renamed property
838 renamedProperty.sizeOfNameString =
839 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
841 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
842 return STG_E_INVALIDNAME;
844 strcpyW(renamedProperty.name, pwcsNewName);
846 renamedProperty.propertyType = currentProperty.propertyType;
847 renamedProperty.startingBlock = currentProperty.startingBlock;
848 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
849 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
851 renamedProperty.previousProperty = PROPERTY_NULL;
852 renamedProperty.nextProperty = PROPERTY_NULL;
855 * Bring the dirProperty link in case it is a storage and in which
856 * case the renamed storage elements don't require to be reorganized.
858 renamedProperty.dirProperty = currentProperty.dirProperty;
860 /* call CoFileTime to get the current time
861 renamedProperty.timeStampS1
862 renamedProperty.timeStampD1
863 renamedProperty.timeStampS2
864 renamedProperty.timeStampD2
865 renamedProperty.propertyUniqueID
869 * Obtain a free property in the property chain
871 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
874 * Save the new property into the new property spot
876 StorageImpl_WriteProperty(
877 This->ancestorStorage,
878 renamedPropertyIndex,
879 &renamedProperty);
882 * Find a spot in the property chain for our newly created property.
884 updatePropertyChain(
885 (StorageImpl*)This,
886 renamedPropertyIndex,
887 renamedProperty);
890 * At this point the renamed property has been inserted in the tree,
891 * now, before Destroying the old property we must zero its dirProperty
892 * otherwise the DestroyProperty below will zap it all and we do not want
893 * this to happen.
894 * Also, we fake that the old property is a storage so the DestroyProperty
895 * will not do a SetSize(0) on the stream data.
897 * This means that we need to tweak the StgProperty if it is a stream or a
898 * non empty storage.
900 StorageImpl_ReadProperty(This->ancestorStorage,
901 foundPropertyIndex,
902 &currentProperty);
904 currentProperty.dirProperty = PROPERTY_NULL;
905 currentProperty.propertyType = PROPTYPE_STORAGE;
906 StorageImpl_WriteProperty(
907 This->ancestorStorage,
908 foundPropertyIndex,
909 &currentProperty);
912 * Invoke Destroy to get rid of the ole property and automatically redo
913 * the linking of its previous and next members...
915 IStorage_DestroyElement(iface, pwcsOldName);
918 else
921 * There is no property with the old name
923 return STG_E_FILENOTFOUND;
926 return S_OK;
929 /************************************************************************
930 * Storage32BaseImpl_CreateStream (IStorage)
932 * This method will create a stream object within this storage
934 * See Windows documentation for more details on IStorage methods.
936 static HRESULT WINAPI StorageBaseImpl_CreateStream(
937 IStorage* iface,
938 const OLECHAR* pwcsName, /* [string][in] */
939 DWORD grfMode, /* [in] */
940 DWORD reserved1, /* [in] */
941 DWORD reserved2, /* [in] */
942 IStream** ppstm) /* [out] */
944 StorageBaseImpl *This = (StorageBaseImpl *)iface;
945 IEnumSTATSTGImpl* propertyEnumeration;
946 StgStreamImpl* newStream;
947 StgProperty currentProperty, newStreamProperty;
948 ULONG foundPropertyIndex, newPropertyIndex;
950 TRACE("(%p, %s, %x, %d, %d, %p)\n",
951 iface, debugstr_w(pwcsName), grfMode,
952 reserved1, reserved2, ppstm);
955 * Validate parameters
957 if (ppstm == 0)
958 return STG_E_INVALIDPOINTER;
960 if (pwcsName == 0)
961 return STG_E_INVALIDNAME;
963 if (reserved1 || reserved2)
964 return STG_E_INVALIDPARAMETER;
967 * Validate the STGM flags
969 if ( FAILED( validateSTGM(grfMode) ))
970 return STG_E_INVALIDFLAG;
972 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
973 return STG_E_INVALIDFLAG;
976 * As documented.
978 if ((grfMode & STGM_DELETEONRELEASE) ||
979 (grfMode & STGM_TRANSACTED))
980 return STG_E_INVALIDFUNCTION;
983 * Check that we're compatible with the parent's storage mode
984 * if not in transacted mode
986 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
987 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
988 return STG_E_ACCESSDENIED;
992 * Initialize the out parameter
994 *ppstm = 0;
997 * Create a property enumeration to search the properties
999 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1000 This->rootPropertySetIndex);
1002 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1003 pwcsName,
1004 &currentProperty);
1006 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1008 if (foundPropertyIndex != PROPERTY_NULL)
1011 * An element with this name already exists
1013 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1015 StgStreamImpl *strm;
1017 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
1019 if (strm->ownerProperty == foundPropertyIndex)
1021 TRACE("Stream deleted %p\n", strm);
1022 strm->parentStorage = NULL;
1023 list_remove(&strm->StrmListEntry);
1026 IStorage_DestroyElement(iface, pwcsName);
1028 else
1029 return STG_E_FILEALREADYEXISTS;
1031 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1033 WARN("read-only storage\n");
1034 return STG_E_ACCESSDENIED;
1038 * memset the empty property
1040 memset(&newStreamProperty, 0, sizeof(StgProperty));
1042 newStreamProperty.sizeOfNameString =
1043 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1045 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1046 return STG_E_INVALIDNAME;
1048 strcpyW(newStreamProperty.name, pwcsName);
1050 newStreamProperty.propertyType = PROPTYPE_STREAM;
1051 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1052 newStreamProperty.size.u.LowPart = 0;
1053 newStreamProperty.size.u.HighPart = 0;
1055 newStreamProperty.previousProperty = PROPERTY_NULL;
1056 newStreamProperty.nextProperty = PROPERTY_NULL;
1057 newStreamProperty.dirProperty = PROPERTY_NULL;
1059 /* call CoFileTime to get the current time
1060 newStreamProperty.timeStampS1
1061 newStreamProperty.timeStampD1
1062 newStreamProperty.timeStampS2
1063 newStreamProperty.timeStampD2
1066 /* newStreamProperty.propertyUniqueID */
1069 * Get a free property or create a new one
1071 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1074 * Save the new property into the new property spot
1076 StorageImpl_WriteProperty(
1077 This->ancestorStorage,
1078 newPropertyIndex,
1079 &newStreamProperty);
1082 * Find a spot in the property chain for our newly created property.
1084 updatePropertyChain(
1085 (StorageImpl*)This,
1086 newPropertyIndex,
1087 newStreamProperty);
1090 * Open the stream to return it.
1092 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1094 if (newStream != 0)
1096 *ppstm = (IStream*)newStream;
1099 * Since we are returning a pointer to the interface, we have to nail down
1100 * the reference.
1102 IStream_AddRef(*ppstm);
1104 else
1106 return STG_E_INSUFFICIENTMEMORY;
1109 return S_OK;
1112 /************************************************************************
1113 * Storage32BaseImpl_SetClass (IStorage)
1115 * This method will write the specified CLSID in the property of this
1116 * storage.
1118 * See Windows documentation for more details on IStorage methods.
1120 static HRESULT WINAPI StorageBaseImpl_SetClass(
1121 IStorage* iface,
1122 REFCLSID clsid) /* [in] */
1124 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1125 HRESULT hRes = E_FAIL;
1126 StgProperty curProperty;
1127 BOOL success;
1129 TRACE("(%p, %p)\n", iface, clsid);
1131 success = StorageImpl_ReadProperty(This->ancestorStorage,
1132 This->rootPropertySetIndex,
1133 &curProperty);
1134 if (success)
1136 curProperty.propertyUniqueID = *clsid;
1138 success = StorageImpl_WriteProperty(This->ancestorStorage,
1139 This->rootPropertySetIndex,
1140 &curProperty);
1141 if (success)
1142 hRes = S_OK;
1145 return hRes;
1148 /************************************************************************
1149 ** Storage32Impl implementation
1152 /************************************************************************
1153 * Storage32Impl_CreateStorage (IStorage)
1155 * This method will create the storage object within the provided storage.
1157 * See Windows documentation for more details on IStorage methods.
1159 static HRESULT WINAPI StorageImpl_CreateStorage(
1160 IStorage* iface,
1161 const OLECHAR *pwcsName, /* [string][in] */
1162 DWORD grfMode, /* [in] */
1163 DWORD reserved1, /* [in] */
1164 DWORD reserved2, /* [in] */
1165 IStorage **ppstg) /* [out] */
1167 StorageImpl* const This=(StorageImpl*)iface;
1169 IEnumSTATSTGImpl *propertyEnumeration;
1170 StgProperty currentProperty;
1171 StgProperty newProperty;
1172 ULONG foundPropertyIndex;
1173 ULONG newPropertyIndex;
1174 HRESULT hr;
1176 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1177 iface, debugstr_w(pwcsName), grfMode,
1178 reserved1, reserved2, ppstg);
1181 * Validate parameters
1183 if (ppstg == 0)
1184 return STG_E_INVALIDPOINTER;
1186 if (pwcsName == 0)
1187 return STG_E_INVALIDNAME;
1190 * Initialize the out parameter
1192 *ppstg = NULL;
1195 * Validate the STGM flags
1197 if ( FAILED( validateSTGM(grfMode) ) ||
1198 (grfMode & STGM_DELETEONRELEASE) )
1200 WARN("bad grfMode: 0x%x\n", grfMode);
1201 return STG_E_INVALIDFLAG;
1205 * Check that we're compatible with the parent's storage mode
1207 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1209 WARN("access denied\n");
1210 return STG_E_ACCESSDENIED;
1214 * Create a property enumeration and search the properties
1216 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1217 This->base.rootPropertySetIndex);
1219 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1220 pwcsName,
1221 &currentProperty);
1222 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1224 if (foundPropertyIndex != PROPERTY_NULL)
1227 * An element with this name already exists
1229 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1230 IStorage_DestroyElement(iface, pwcsName);
1231 else
1233 WARN("file already exists\n");
1234 return STG_E_FILEALREADYEXISTS;
1237 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1239 WARN("read-only storage\n");
1240 return STG_E_ACCESSDENIED;
1244 * memset the empty property
1246 memset(&newProperty, 0, sizeof(StgProperty));
1248 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1250 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1252 FIXME("name too long\n");
1253 return STG_E_INVALIDNAME;
1256 strcpyW(newProperty.name, pwcsName);
1258 newProperty.propertyType = PROPTYPE_STORAGE;
1259 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1260 newProperty.size.u.LowPart = 0;
1261 newProperty.size.u.HighPart = 0;
1263 newProperty.previousProperty = PROPERTY_NULL;
1264 newProperty.nextProperty = PROPERTY_NULL;
1265 newProperty.dirProperty = PROPERTY_NULL;
1267 /* call CoFileTime to get the current time
1268 newProperty.timeStampS1
1269 newProperty.timeStampD1
1270 newProperty.timeStampS2
1271 newProperty.timeStampD2
1274 /* newStorageProperty.propertyUniqueID */
1277 * Obtain a free property in the property chain
1279 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1282 * Save the new property into the new property spot
1284 StorageImpl_WriteProperty(
1285 This->base.ancestorStorage,
1286 newPropertyIndex,
1287 &newProperty);
1290 * Find a spot in the property chain for our newly created property.
1292 updatePropertyChain(
1293 This,
1294 newPropertyIndex,
1295 newProperty);
1298 * Open it to get a pointer to return.
1300 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1302 if( (hr != S_OK) || (*ppstg == NULL))
1304 return hr;
1308 return S_OK;
1312 /***************************************************************************
1314 * Internal Method
1316 * Get a free property or create a new one.
1318 static ULONG getFreeProperty(
1319 StorageImpl *storage)
1321 ULONG currentPropertyIndex = 0;
1322 ULONG newPropertyIndex = PROPERTY_NULL;
1323 BOOL readSuccessful = TRUE;
1324 StgProperty currentProperty;
1329 * Start by reading the root property
1331 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1332 currentPropertyIndex,
1333 &currentProperty);
1334 if (readSuccessful)
1336 if (currentProperty.sizeOfNameString == 0)
1339 * The property existis and is available, we found it.
1341 newPropertyIndex = currentPropertyIndex;
1344 else
1347 * We exhausted the property list, we will create more space below
1349 newPropertyIndex = currentPropertyIndex;
1351 currentPropertyIndex++;
1353 } while (newPropertyIndex == PROPERTY_NULL);
1356 * grow the property chain
1358 if (! readSuccessful)
1360 StgProperty emptyProperty;
1361 ULARGE_INTEGER newSize;
1362 ULONG propertyIndex;
1363 ULONG lastProperty = 0;
1364 ULONG blockCount = 0;
1367 * obtain the new count of property blocks
1369 blockCount = BlockChainStream_GetCount(
1370 storage->base.ancestorStorage->rootBlockChain)+1;
1373 * initialize the size used by the property stream
1375 newSize.u.HighPart = 0;
1376 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1379 * add a property block to the property chain
1381 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1384 * memset the empty property in order to initialize the unused newly
1385 * created property
1387 memset(&emptyProperty, 0, sizeof(StgProperty));
1390 * initialize them
1392 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1394 for(
1395 propertyIndex = newPropertyIndex;
1396 propertyIndex < lastProperty;
1397 propertyIndex++)
1399 StorageImpl_WriteProperty(
1400 storage->base.ancestorStorage,
1401 propertyIndex,
1402 &emptyProperty);
1406 return newPropertyIndex;
1409 /****************************************************************************
1411 * Internal Method
1413 * Case insensitive comparison of StgProperty.name by first considering
1414 * their size.
1416 * Returns <0 when newProperty < currentProperty
1417 * >0 when newProperty > currentProperty
1418 * 0 when newProperty == currentProperty
1420 static LONG propertyNameCmp(
1421 const OLECHAR *newProperty,
1422 const OLECHAR *currentProperty)
1424 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1426 if (diff == 0)
1429 * We compare the string themselves only when they are of the same length
1431 diff = lstrcmpiW( newProperty, currentProperty);
1434 return diff;
1437 /****************************************************************************
1439 * Internal Method
1441 * Properly link this new element in the property chain.
1443 static void updatePropertyChain(
1444 StorageImpl *storage,
1445 ULONG newPropertyIndex,
1446 StgProperty newProperty)
1448 StgProperty currentProperty;
1451 * Read the root property
1453 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1454 storage->base.rootPropertySetIndex,
1455 &currentProperty);
1457 if (currentProperty.dirProperty != PROPERTY_NULL)
1460 * The root storage contains some element, therefore, start the research
1461 * for the appropriate location.
1463 BOOL found = 0;
1464 ULONG current, next, previous, currentPropertyId;
1467 * Keep the StgProperty sequence number of the storage first property
1469 currentPropertyId = currentProperty.dirProperty;
1472 * Read
1474 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1475 currentProperty.dirProperty,
1476 &currentProperty);
1478 previous = currentProperty.previousProperty;
1479 next = currentProperty.nextProperty;
1480 current = currentPropertyId;
1482 while (found == 0)
1484 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1486 if (diff < 0)
1488 if (previous != PROPERTY_NULL)
1490 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1491 previous,
1492 &currentProperty);
1493 current = previous;
1495 else
1497 currentProperty.previousProperty = newPropertyIndex;
1498 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1499 current,
1500 &currentProperty);
1501 found = 1;
1504 else if (diff > 0)
1506 if (next != PROPERTY_NULL)
1508 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1509 next,
1510 &currentProperty);
1511 current = next;
1513 else
1515 currentProperty.nextProperty = newPropertyIndex;
1516 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1517 current,
1518 &currentProperty);
1519 found = 1;
1522 else
1525 * Trying to insert an item with the same name in the
1526 * subtree structure.
1528 assert(FALSE);
1531 previous = currentProperty.previousProperty;
1532 next = currentProperty.nextProperty;
1535 else
1538 * The root storage is empty, link the new property to its dir property
1540 currentProperty.dirProperty = newPropertyIndex;
1541 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1542 storage->base.rootPropertySetIndex,
1543 &currentProperty);
1548 /*************************************************************************
1549 * CopyTo (IStorage)
1551 static HRESULT WINAPI StorageImpl_CopyTo(
1552 IStorage* iface,
1553 DWORD ciidExclude, /* [in] */
1554 const IID* rgiidExclude, /* [size_is][unique][in] */
1555 SNB snbExclude, /* [unique][in] */
1556 IStorage* pstgDest) /* [unique][in] */
1558 IEnumSTATSTG *elements = 0;
1559 STATSTG curElement, strStat;
1560 HRESULT hr;
1561 IStorage *pstgTmp, *pstgChild;
1562 IStream *pstrTmp, *pstrChild;
1564 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1565 FIXME("Exclude option not implemented\n");
1567 TRACE("(%p, %d, %p, %p, %p)\n",
1568 iface, ciidExclude, rgiidExclude,
1569 snbExclude, pstgDest);
1572 * Perform a sanity check
1574 if ( pstgDest == 0 )
1575 return STG_E_INVALIDPOINTER;
1578 * Enumerate the elements
1580 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1582 if ( hr != S_OK )
1583 return hr;
1586 * set the class ID
1588 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1589 IStorage_SetClass( pstgDest, &curElement.clsid );
1594 * Obtain the next element
1596 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1598 if ( hr == S_FALSE )
1600 hr = S_OK; /* done, every element has been copied */
1601 break;
1604 if (curElement.type == STGTY_STORAGE)
1607 * open child source storage
1609 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1610 STGM_READ|STGM_SHARE_EXCLUSIVE,
1611 NULL, 0, &pstgChild );
1613 if (hr != S_OK)
1614 break;
1617 * Check if destination storage is not a child of the source
1618 * storage, which will cause an infinite loop
1620 if (pstgChild == pstgDest)
1622 IEnumSTATSTG_Release(elements);
1624 return STG_E_ACCESSDENIED;
1628 * create a new storage in destination storage
1630 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1631 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1632 0, 0,
1633 &pstgTmp );
1635 * if it already exist, don't create a new one use this one
1637 if (hr == STG_E_FILEALREADYEXISTS)
1639 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1640 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1641 NULL, 0, &pstgTmp );
1644 if (hr != S_OK)
1645 break;
1649 * do the copy recursively
1651 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1652 snbExclude, pstgTmp );
1654 IStorage_Release( pstgTmp );
1655 IStorage_Release( pstgChild );
1657 else if (curElement.type == STGTY_STREAM)
1660 * create a new stream in destination storage. If the stream already
1661 * exist, it will be deleted and a new one will be created.
1663 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1664 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1665 0, 0, &pstrTmp );
1667 if (hr != S_OK)
1668 break;
1671 * open child stream storage
1673 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1674 STGM_READ|STGM_SHARE_EXCLUSIVE,
1675 0, &pstrChild );
1677 if (hr != S_OK)
1678 break;
1681 * Get the size of the source stream
1683 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1686 * Set the size of the destination stream.
1688 IStream_SetSize(pstrTmp, strStat.cbSize);
1691 * do the copy
1693 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1694 NULL, NULL );
1696 IStream_Release( pstrTmp );
1697 IStream_Release( pstrChild );
1699 else
1701 WARN("unknown element type: %d\n", curElement.type);
1704 } while (hr == S_OK);
1707 * Clean-up
1709 IEnumSTATSTG_Release(elements);
1711 return hr;
1714 /*************************************************************************
1715 * MoveElementTo (IStorage)
1717 static HRESULT WINAPI StorageImpl_MoveElementTo(
1718 IStorage* iface,
1719 const OLECHAR *pwcsName, /* [string][in] */
1720 IStorage *pstgDest, /* [unique][in] */
1721 const OLECHAR *pwcsNewName,/* [string][in] */
1722 DWORD grfFlags) /* [in] */
1724 FIXME("(%p %s %p %s %u): stub\n", iface,
1725 debugstr_w(pwcsName), pstgDest,
1726 debugstr_w(pwcsNewName), grfFlags);
1727 return E_NOTIMPL;
1730 /*************************************************************************
1731 * Commit (IStorage)
1733 * Ensures that any changes made to a storage object open in transacted mode
1734 * are reflected in the parent storage
1736 * NOTES
1737 * Wine doesn't implement transacted mode, which seems to be a basic
1738 * optimization, so we can ignore this stub for now.
1740 static HRESULT WINAPI StorageImpl_Commit(
1741 IStorage* iface,
1742 DWORD grfCommitFlags)/* [in] */
1744 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1745 return S_OK;
1748 /*************************************************************************
1749 * Revert (IStorage)
1751 * Discard all changes that have been made since the last commit operation
1753 static HRESULT WINAPI StorageImpl_Revert(
1754 IStorage* iface)
1756 FIXME("(%p): stub\n", iface);
1757 return E_NOTIMPL;
1760 /*************************************************************************
1761 * DestroyElement (IStorage)
1763 * Strategy: This implementation is built this way for simplicity not for speed.
1764 * I always delete the topmost element of the enumeration and adjust
1765 * the deleted element pointer all the time. This takes longer to
1766 * do but allow to reinvoke DestroyElement whenever we encounter a
1767 * storage object. The optimisation resides in the usage of another
1768 * enumeration strategy that would give all the leaves of a storage
1769 * first. (postfix order)
1771 static HRESULT WINAPI StorageImpl_DestroyElement(
1772 IStorage* iface,
1773 const OLECHAR *pwcsName)/* [string][in] */
1775 StorageImpl* const This=(StorageImpl*)iface;
1777 IEnumSTATSTGImpl* propertyEnumeration;
1778 HRESULT hr = S_OK;
1779 BOOL res;
1780 StgProperty propertyToDelete;
1781 StgProperty parentProperty;
1782 ULONG foundPropertyIndexToDelete;
1783 ULONG typeOfRelation;
1784 ULONG parentPropertyId = 0;
1786 TRACE("(%p, %s)\n",
1787 iface, debugstr_w(pwcsName));
1790 * Perform a sanity check on the parameters.
1792 if (pwcsName==NULL)
1793 return STG_E_INVALIDPOINTER;
1796 * Create a property enumeration to search the property with the given name
1798 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1799 This->base.ancestorStorage,
1800 This->base.rootPropertySetIndex);
1802 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1803 propertyEnumeration,
1804 pwcsName,
1805 &propertyToDelete);
1807 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1809 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1811 return STG_E_FILENOTFOUND;
1815 * Find the parent property of the property to delete (the one that
1816 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1817 * the parent is This. Otherwise, the parent is one of its sibling...
1821 * First, read This's StgProperty..
1823 res = StorageImpl_ReadProperty(
1824 This->base.ancestorStorage,
1825 This->base.rootPropertySetIndex,
1826 &parentProperty);
1828 assert(res);
1831 * Second, check to see if by any chance the actual storage (This) is not
1832 * the parent of the property to delete... We never know...
1834 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1837 * Set data as it would have been done in the else part...
1839 typeOfRelation = PROPERTY_RELATION_DIR;
1840 parentPropertyId = This->base.rootPropertySetIndex;
1842 else
1845 * Create a property enumeration to search the parent properties, and
1846 * delete it once done.
1848 IEnumSTATSTGImpl* propertyEnumeration2;
1850 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1851 This->base.ancestorStorage,
1852 This->base.rootPropertySetIndex);
1854 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1855 propertyEnumeration2,
1856 foundPropertyIndexToDelete,
1857 &parentProperty,
1858 &parentPropertyId);
1860 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1863 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1865 hr = deleteStorageProperty(
1866 This,
1867 foundPropertyIndexToDelete,
1868 propertyToDelete);
1870 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1872 hr = deleteStreamProperty(
1873 This,
1874 foundPropertyIndexToDelete,
1875 propertyToDelete);
1878 if (hr!=S_OK)
1879 return hr;
1882 * Adjust the property chain
1884 hr = adjustPropertyChain(
1885 This,
1886 propertyToDelete,
1887 parentProperty,
1888 parentPropertyId,
1889 typeOfRelation);
1891 return hr;
1895 /************************************************************************
1896 * StorageImpl_Stat (IStorage)
1898 * This method will retrieve information about this storage object.
1900 * See Windows documentation for more details on IStorage methods.
1902 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1903 STATSTG* pstatstg, /* [out] */
1904 DWORD grfStatFlag) /* [in] */
1906 StorageImpl* const This = (StorageImpl*)iface;
1907 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1909 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1911 CoTaskMemFree(pstatstg->pwcsName);
1912 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1913 strcpyW(pstatstg->pwcsName, This->pwcsName);
1916 return result;
1919 /******************************************************************************
1920 * Internal stream list handlers
1923 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1925 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1926 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1929 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1931 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1932 list_remove(&(strm->StrmListEntry));
1935 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1937 struct list *cur, *cur2;
1938 StgStreamImpl *strm=NULL;
1940 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1941 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1942 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1943 strm->parentStorage = NULL;
1944 list_remove(cur);
1949 /*********************************************************************
1951 * Internal Method
1953 * Perform the deletion of a complete storage node
1956 static HRESULT deleteStorageProperty(
1957 StorageImpl *parentStorage,
1958 ULONG indexOfPropertyToDelete,
1959 StgProperty propertyToDelete)
1961 IEnumSTATSTG *elements = 0;
1962 IStorage *childStorage = 0;
1963 STATSTG currentElement;
1964 HRESULT hr;
1965 HRESULT destroyHr = S_OK;
1968 * Open the storage and enumerate it
1970 hr = StorageBaseImpl_OpenStorage(
1971 (IStorage*)parentStorage,
1972 propertyToDelete.name,
1974 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1977 &childStorage);
1979 if (hr != S_OK)
1981 return hr;
1985 * Enumerate the elements
1987 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1992 * Obtain the next element
1994 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1995 if (hr==S_OK)
1997 destroyHr = StorageImpl_DestroyElement(childStorage, currentElement.pwcsName);
1999 CoTaskMemFree(currentElement.pwcsName);
2003 * We need to Reset the enumeration every time because we delete elements
2004 * and the enumeration could be invalid
2006 IEnumSTATSTG_Reset(elements);
2008 } while ((hr == S_OK) && (destroyHr == S_OK));
2011 * Invalidate the property by zeroing its name member.
2013 propertyToDelete.sizeOfNameString = 0;
2015 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2016 indexOfPropertyToDelete,
2017 &propertyToDelete);
2019 IStorage_Release(childStorage);
2020 IEnumSTATSTG_Release(elements);
2022 return destroyHr;
2025 /*********************************************************************
2027 * Internal Method
2029 * Perform the deletion of a stream node
2032 static HRESULT deleteStreamProperty(
2033 StorageImpl *parentStorage,
2034 ULONG indexOfPropertyToDelete,
2035 StgProperty propertyToDelete)
2037 IStream *pis;
2038 HRESULT hr;
2039 ULARGE_INTEGER size;
2041 size.u.HighPart = 0;
2042 size.u.LowPart = 0;
2044 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
2045 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
2047 if (hr!=S_OK)
2049 return(hr);
2053 * Zap the stream
2055 hr = IStream_SetSize(pis, size);
2057 if(hr != S_OK)
2059 return hr;
2063 * Release the stream object.
2065 IStream_Release(pis);
2068 * Invalidate the property by zeroing its name member.
2070 propertyToDelete.sizeOfNameString = 0;
2073 * Here we should re-read the property so we get the updated pointer
2074 * but since we are here to zap it, I don't do it...
2076 StorageImpl_WriteProperty(
2077 parentStorage->base.ancestorStorage,
2078 indexOfPropertyToDelete,
2079 &propertyToDelete);
2081 return S_OK;
2084 /*********************************************************************
2086 * Internal Method
2088 * Finds a placeholder for the StgProperty within the Storage
2091 static HRESULT findPlaceholder(
2092 StorageImpl *storage,
2093 ULONG propertyIndexToStore,
2094 ULONG storePropertyIndex,
2095 INT typeOfRelation)
2097 StgProperty storeProperty;
2098 BOOL res = TRUE;
2101 * Read the storage property
2103 res = StorageImpl_ReadProperty(
2104 storage->base.ancestorStorage,
2105 storePropertyIndex,
2106 &storeProperty);
2108 if(! res)
2110 return E_FAIL;
2113 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2115 if (storeProperty.previousProperty != PROPERTY_NULL)
2117 return findPlaceholder(
2118 storage,
2119 propertyIndexToStore,
2120 storeProperty.previousProperty,
2121 typeOfRelation);
2123 else
2125 storeProperty.previousProperty = propertyIndexToStore;
2128 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2130 if (storeProperty.nextProperty != PROPERTY_NULL)
2132 return findPlaceholder(
2133 storage,
2134 propertyIndexToStore,
2135 storeProperty.nextProperty,
2136 typeOfRelation);
2138 else
2140 storeProperty.nextProperty = propertyIndexToStore;
2143 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2145 if (storeProperty.dirProperty != PROPERTY_NULL)
2147 return findPlaceholder(
2148 storage,
2149 propertyIndexToStore,
2150 storeProperty.dirProperty,
2151 typeOfRelation);
2153 else
2155 storeProperty.dirProperty = propertyIndexToStore;
2159 res = StorageImpl_WriteProperty(
2160 storage->base.ancestorStorage,
2161 storePropertyIndex,
2162 &storeProperty);
2164 if(!res)
2166 return E_FAIL;
2169 return S_OK;
2172 /*************************************************************************
2174 * Internal Method
2176 * This method takes the previous and the next property link of a property
2177 * to be deleted and find them a place in the Storage.
2179 static HRESULT adjustPropertyChain(
2180 StorageImpl *This,
2181 StgProperty propertyToDelete,
2182 StgProperty parentProperty,
2183 ULONG parentPropertyId,
2184 INT typeOfRelation)
2186 ULONG newLinkProperty = PROPERTY_NULL;
2187 BOOL needToFindAPlaceholder = FALSE;
2188 ULONG storeNode = PROPERTY_NULL;
2189 ULONG toStoreNode = PROPERTY_NULL;
2190 INT relationType = 0;
2191 HRESULT hr = S_OK;
2192 BOOL res = TRUE;
2194 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2196 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2199 * Set the parent previous to the property to delete previous
2201 newLinkProperty = propertyToDelete.previousProperty;
2203 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2206 * We also need to find a storage for the other link, setup variables
2207 * to do this at the end...
2209 needToFindAPlaceholder = TRUE;
2210 storeNode = propertyToDelete.previousProperty;
2211 toStoreNode = propertyToDelete.nextProperty;
2212 relationType = PROPERTY_RELATION_NEXT;
2215 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2218 * Set the parent previous to the property to delete next
2220 newLinkProperty = propertyToDelete.nextProperty;
2224 * Link it for real...
2226 parentProperty.previousProperty = newLinkProperty;
2229 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2231 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2234 * Set the parent next to the property to delete next previous
2236 newLinkProperty = propertyToDelete.previousProperty;
2238 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2241 * We also need to find a storage for the other link, setup variables
2242 * to do this at the end...
2244 needToFindAPlaceholder = TRUE;
2245 storeNode = propertyToDelete.previousProperty;
2246 toStoreNode = propertyToDelete.nextProperty;
2247 relationType = PROPERTY_RELATION_NEXT;
2250 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2253 * Set the parent next to the property to delete next
2255 newLinkProperty = propertyToDelete.nextProperty;
2259 * Link it for real...
2261 parentProperty.nextProperty = newLinkProperty;
2263 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2265 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2268 * Set the parent dir to the property to delete previous
2270 newLinkProperty = propertyToDelete.previousProperty;
2272 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2275 * We also need to find a storage for the other link, setup variables
2276 * to do this at the end...
2278 needToFindAPlaceholder = TRUE;
2279 storeNode = propertyToDelete.previousProperty;
2280 toStoreNode = propertyToDelete.nextProperty;
2281 relationType = PROPERTY_RELATION_NEXT;
2284 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2287 * Set the parent dir to the property to delete next
2289 newLinkProperty = propertyToDelete.nextProperty;
2293 * Link it for real...
2295 parentProperty.dirProperty = newLinkProperty;
2299 * Write back the parent property
2301 res = StorageImpl_WriteProperty(
2302 This->base.ancestorStorage,
2303 parentPropertyId,
2304 &parentProperty);
2305 if(! res)
2307 return E_FAIL;
2311 * If a placeholder is required for the other link, then, find one and
2312 * get out of here...
2314 if (needToFindAPlaceholder)
2316 hr = findPlaceholder(
2317 This,
2318 toStoreNode,
2319 storeNode,
2320 relationType);
2323 return hr;
2327 /******************************************************************************
2328 * SetElementTimes (IStorage)
2330 static HRESULT WINAPI StorageImpl_SetElementTimes(
2331 IStorage* iface,
2332 const OLECHAR *pwcsName,/* [string][in] */
2333 const FILETIME *pctime, /* [in] */
2334 const FILETIME *patime, /* [in] */
2335 const FILETIME *pmtime) /* [in] */
2337 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2338 return S_OK;
2341 /******************************************************************************
2342 * SetStateBits (IStorage)
2344 static HRESULT WINAPI StorageImpl_SetStateBits(
2345 IStorage* iface,
2346 DWORD grfStateBits,/* [in] */
2347 DWORD grfMask) /* [in] */
2349 StorageImpl* const This = (StorageImpl*)iface;
2350 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2351 return S_OK;
2355 * Virtual function table for the IStorage32Impl class.
2357 static const IStorageVtbl Storage32Impl_Vtbl =
2359 StorageBaseImpl_QueryInterface,
2360 StorageBaseImpl_AddRef,
2361 StorageBaseImpl_Release,
2362 StorageBaseImpl_CreateStream,
2363 StorageBaseImpl_OpenStream,
2364 StorageImpl_CreateStorage,
2365 StorageBaseImpl_OpenStorage,
2366 StorageImpl_CopyTo,
2367 StorageImpl_MoveElementTo,
2368 StorageImpl_Commit,
2369 StorageImpl_Revert,
2370 StorageBaseImpl_EnumElements,
2371 StorageImpl_DestroyElement,
2372 StorageBaseImpl_RenameElement,
2373 StorageImpl_SetElementTimes,
2374 StorageBaseImpl_SetClass,
2375 StorageImpl_SetStateBits,
2376 StorageImpl_Stat
2379 static HRESULT StorageImpl_Construct(
2380 StorageImpl* This,
2381 HANDLE hFile,
2382 LPCOLESTR pwcsName,
2383 ILockBytes* pLkbyt,
2384 DWORD openFlags,
2385 BOOL fileBased,
2386 BOOL fileCreate)
2388 HRESULT hr = S_OK;
2389 StgProperty currentProperty;
2390 BOOL readSuccessful;
2391 ULONG currentPropertyIndex;
2393 if ( FAILED( validateSTGM(openFlags) ))
2394 return STG_E_INVALIDFLAG;
2396 memset(This, 0, sizeof(StorageImpl));
2399 * Initialize stream list
2402 list_init(&This->base.strmHead);
2405 * Initialize the virtual function table.
2407 This->base.lpVtbl = &Storage32Impl_Vtbl;
2408 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2409 This->base.v_destructor = StorageImpl_Destroy;
2410 This->base.openFlags = (openFlags & ~STGM_CREATE);
2413 * This is the top-level storage so initialize the ancestor pointer
2414 * to this.
2416 This->base.ancestorStorage = This;
2419 * Initialize the physical support of the storage.
2421 This->hFile = hFile;
2424 * Store copy of file path.
2426 if(pwcsName) {
2427 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2428 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2429 if (!This->pwcsName)
2430 return STG_E_INSUFFICIENTMEMORY;
2431 strcpyW(This->pwcsName, pwcsName);
2435 * Initialize the big block cache.
2437 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2438 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2439 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2440 pLkbyt,
2441 openFlags,
2442 This->bigBlockSize,
2443 fileBased);
2445 if (This->bigBlockFile == 0)
2446 return E_FAIL;
2448 if (fileCreate)
2450 ULARGE_INTEGER size;
2451 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2454 * Initialize all header variables:
2455 * - The big block depot consists of one block and it is at block 0
2456 * - The properties start at block 1
2457 * - There is no small block depot
2459 memset( This->bigBlockDepotStart,
2460 BLOCK_UNUSED,
2461 sizeof(This->bigBlockDepotStart));
2463 This->bigBlockDepotCount = 1;
2464 This->bigBlockDepotStart[0] = 0;
2465 This->rootStartBlock = 1;
2466 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2467 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2468 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2469 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2470 This->extBigBlockDepotCount = 0;
2472 StorageImpl_SaveFileHeader(This);
2475 * Add one block for the big block depot and one block for the properties
2477 size.u.HighPart = 0;
2478 size.u.LowPart = This->bigBlockSize * 3;
2479 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2482 * Initialize the big block depot
2484 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2485 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2486 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2487 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2489 else
2492 * Load the header for the file.
2494 hr = StorageImpl_LoadFileHeader(This);
2496 if (FAILED(hr))
2498 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2500 return hr;
2505 * There is no block depot cached yet.
2507 This->indexBlockDepotCached = 0xFFFFFFFF;
2510 * Start searching for free blocks with block 0.
2512 This->prevFreeBlock = 0;
2515 * Create the block chain abstractions.
2517 if(!(This->rootBlockChain =
2518 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2519 return STG_E_READFAULT;
2521 if(!(This->smallBlockDepotChain =
2522 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2523 PROPERTY_NULL)))
2524 return STG_E_READFAULT;
2527 * Write the root property (memory only)
2529 if (fileCreate)
2531 StgProperty rootProp;
2533 * Initialize the property chain
2535 memset(&rootProp, 0, sizeof(rootProp));
2536 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2537 sizeof(rootProp.name)/sizeof(WCHAR) );
2538 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2539 rootProp.propertyType = PROPTYPE_ROOT;
2540 rootProp.previousProperty = PROPERTY_NULL;
2541 rootProp.nextProperty = PROPERTY_NULL;
2542 rootProp.dirProperty = PROPERTY_NULL;
2543 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2544 rootProp.size.u.HighPart = 0;
2545 rootProp.size.u.LowPart = 0;
2547 StorageImpl_WriteProperty(This, 0, &rootProp);
2551 * Find the ID of the root in the property sets.
2553 currentPropertyIndex = 0;
2557 readSuccessful = StorageImpl_ReadProperty(
2558 This,
2559 currentPropertyIndex,
2560 &currentProperty);
2562 if (readSuccessful)
2564 if ( (currentProperty.sizeOfNameString != 0 ) &&
2565 (currentProperty.propertyType == PROPTYPE_ROOT) )
2567 This->base.rootPropertySetIndex = currentPropertyIndex;
2571 currentPropertyIndex++;
2573 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2575 if (!readSuccessful)
2577 /* TODO CLEANUP */
2578 return STG_E_READFAULT;
2582 * Create the block chain abstraction for the small block root chain.
2584 if(!(This->smallBlockRootChain =
2585 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2586 return STG_E_READFAULT;
2588 return hr;
2591 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2593 StorageImpl *This = (StorageImpl*) iface;
2594 TRACE("(%p)\n", This);
2596 StorageBaseImpl_DeleteAll(&This->base);
2598 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2600 BlockChainStream_Destroy(This->smallBlockRootChain);
2601 BlockChainStream_Destroy(This->rootBlockChain);
2602 BlockChainStream_Destroy(This->smallBlockDepotChain);
2604 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2605 HeapFree(GetProcessHeap(), 0, This);
2608 /******************************************************************************
2609 * Storage32Impl_GetNextFreeBigBlock
2611 * Returns the index of the next free big block.
2612 * If the big block depot is filled, this method will enlarge it.
2615 static ULONG StorageImpl_GetNextFreeBigBlock(
2616 StorageImpl* This)
2618 ULONG depotBlockIndexPos;
2619 BYTE depotBuffer[BIG_BLOCK_SIZE];
2620 BOOL success;
2621 ULONG depotBlockOffset;
2622 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2623 ULONG nextBlockIndex = BLOCK_SPECIAL;
2624 int depotIndex = 0;
2625 ULONG freeBlock = BLOCK_UNUSED;
2627 depotIndex = This->prevFreeBlock / blocksPerDepot;
2628 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2631 * Scan the entire big block depot until we find a block marked free
2633 while (nextBlockIndex != BLOCK_UNUSED)
2635 if (depotIndex < COUNT_BBDEPOTINHEADER)
2637 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2640 * Grow the primary depot.
2642 if (depotBlockIndexPos == BLOCK_UNUSED)
2644 depotBlockIndexPos = depotIndex*blocksPerDepot;
2647 * Add a block depot.
2649 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2650 This->bigBlockDepotCount++;
2651 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2654 * Flag it as a block depot.
2656 StorageImpl_SetNextBlockInChain(This,
2657 depotBlockIndexPos,
2658 BLOCK_SPECIAL);
2660 /* Save new header information.
2662 StorageImpl_SaveFileHeader(This);
2665 else
2667 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2669 if (depotBlockIndexPos == BLOCK_UNUSED)
2672 * Grow the extended depot.
2674 ULONG extIndex = BLOCK_UNUSED;
2675 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2676 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2678 if (extBlockOffset == 0)
2680 /* We need an extended block.
2682 extIndex = Storage32Impl_AddExtBlockDepot(This);
2683 This->extBigBlockDepotCount++;
2684 depotBlockIndexPos = extIndex + 1;
2686 else
2687 depotBlockIndexPos = depotIndex * blocksPerDepot;
2690 * Add a block depot and mark it in the extended block.
2692 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2693 This->bigBlockDepotCount++;
2694 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2696 /* Flag the block depot.
2698 StorageImpl_SetNextBlockInChain(This,
2699 depotBlockIndexPos,
2700 BLOCK_SPECIAL);
2702 /* If necessary, flag the extended depot block.
2704 if (extIndex != BLOCK_UNUSED)
2705 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2707 /* Save header information.
2709 StorageImpl_SaveFileHeader(This);
2713 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2715 if (success)
2717 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2718 ( nextBlockIndex != BLOCK_UNUSED))
2720 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2722 if (nextBlockIndex == BLOCK_UNUSED)
2724 freeBlock = (depotIndex * blocksPerDepot) +
2725 (depotBlockOffset/sizeof(ULONG));
2728 depotBlockOffset += sizeof(ULONG);
2732 depotIndex++;
2733 depotBlockOffset = 0;
2737 * make sure that the block physically exists before using it
2739 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2741 This->prevFreeBlock = freeBlock;
2743 return freeBlock;
2746 /******************************************************************************
2747 * Storage32Impl_AddBlockDepot
2749 * This will create a depot block, essentially it is a block initialized
2750 * to BLOCK_UNUSEDs.
2752 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2754 BYTE blockBuffer[BIG_BLOCK_SIZE];
2757 * Initialize blocks as free
2759 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2760 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2763 /******************************************************************************
2764 * Storage32Impl_GetExtDepotBlock
2766 * Returns the index of the block that corresponds to the specified depot
2767 * index. This method is only for depot indexes equal or greater than
2768 * COUNT_BBDEPOTINHEADER.
2770 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2772 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2773 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2774 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2775 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2776 ULONG blockIndex = BLOCK_UNUSED;
2777 ULONG extBlockIndex = This->extBigBlockDepotStart;
2779 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2781 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2782 return BLOCK_UNUSED;
2784 while (extBlockCount > 0)
2786 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2787 extBlockCount--;
2790 if (extBlockIndex != BLOCK_UNUSED)
2791 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2792 extBlockOffset * sizeof(ULONG), &blockIndex);
2794 return blockIndex;
2797 /******************************************************************************
2798 * Storage32Impl_SetExtDepotBlock
2800 * Associates the specified block index to the specified depot index.
2801 * This method is only for depot indexes equal or greater than
2802 * COUNT_BBDEPOTINHEADER.
2804 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2806 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2807 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2808 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2809 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2810 ULONG extBlockIndex = This->extBigBlockDepotStart;
2812 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2814 while (extBlockCount > 0)
2816 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2817 extBlockCount--;
2820 if (extBlockIndex != BLOCK_UNUSED)
2822 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2823 extBlockOffset * sizeof(ULONG),
2824 blockIndex);
2828 /******************************************************************************
2829 * Storage32Impl_AddExtBlockDepot
2831 * Creates an extended depot block.
2833 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2835 ULONG numExtBlocks = This->extBigBlockDepotCount;
2836 ULONG nextExtBlock = This->extBigBlockDepotStart;
2837 BYTE depotBuffer[BIG_BLOCK_SIZE];
2838 ULONG index = BLOCK_UNUSED;
2839 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2840 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2841 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2843 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2844 blocksPerDepotBlock;
2846 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2849 * The first extended block.
2851 This->extBigBlockDepotStart = index;
2853 else
2855 unsigned int i;
2857 * Follow the chain to the last one.
2859 for (i = 0; i < (numExtBlocks - 1); i++)
2861 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2865 * Add the new extended block to the chain.
2867 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2868 index);
2872 * Initialize this block.
2874 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2875 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2877 return index;
2880 /******************************************************************************
2881 * Storage32Impl_FreeBigBlock
2883 * This method will flag the specified block as free in the big block depot.
2885 static void StorageImpl_FreeBigBlock(
2886 StorageImpl* This,
2887 ULONG blockIndex)
2889 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2891 if (blockIndex < This->prevFreeBlock)
2892 This->prevFreeBlock = blockIndex;
2895 /************************************************************************
2896 * Storage32Impl_GetNextBlockInChain
2898 * This method will retrieve the block index of the next big block in
2899 * in the chain.
2901 * Params: This - Pointer to the Storage object.
2902 * blockIndex - Index of the block to retrieve the chain
2903 * for.
2904 * nextBlockIndex - receives the return value.
2906 * Returns: This method returns the index of the next block in the chain.
2907 * It will return the constants:
2908 * BLOCK_SPECIAL - If the block given was not part of a
2909 * chain.
2910 * BLOCK_END_OF_CHAIN - If the block given was the last in
2911 * a chain.
2912 * BLOCK_UNUSED - If the block given was not past of a chain
2913 * and is available.
2914 * BLOCK_EXTBBDEPOT - This block is part of the extended
2915 * big block depot.
2917 * See Windows documentation for more details on IStorage methods.
2919 static HRESULT StorageImpl_GetNextBlockInChain(
2920 StorageImpl* This,
2921 ULONG blockIndex,
2922 ULONG* nextBlockIndex)
2924 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2925 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2926 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2927 BYTE depotBuffer[BIG_BLOCK_SIZE];
2928 BOOL success;
2929 ULONG depotBlockIndexPos;
2930 int index;
2932 *nextBlockIndex = BLOCK_SPECIAL;
2934 if(depotBlockCount >= This->bigBlockDepotCount)
2936 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2937 This->bigBlockDepotCount);
2938 return STG_E_READFAULT;
2942 * Cache the currently accessed depot block.
2944 if (depotBlockCount != This->indexBlockDepotCached)
2946 This->indexBlockDepotCached = depotBlockCount;
2948 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2950 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2952 else
2955 * We have to look in the extended depot.
2957 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2960 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2962 if (!success)
2963 return STG_E_READFAULT;
2965 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2967 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2968 This->blockDepotCached[index] = *nextBlockIndex;
2972 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2974 return S_OK;
2977 /******************************************************************************
2978 * Storage32Impl_GetNextExtendedBlock
2980 * Given an extended block this method will return the next extended block.
2982 * NOTES:
2983 * The last ULONG of an extended block is the block index of the next
2984 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2985 * depot.
2987 * Return values:
2988 * - The index of the next extended block
2989 * - BLOCK_UNUSED: there is no next extended block.
2990 * - Any other return values denotes failure.
2992 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2994 ULONG nextBlockIndex = BLOCK_SPECIAL;
2995 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2997 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2998 &nextBlockIndex);
3000 return nextBlockIndex;
3003 /******************************************************************************
3004 * Storage32Impl_SetNextBlockInChain
3006 * This method will write the index of the specified block's next block
3007 * in the big block depot.
3009 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3010 * do the following
3012 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3013 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3014 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3017 static void StorageImpl_SetNextBlockInChain(
3018 StorageImpl* This,
3019 ULONG blockIndex,
3020 ULONG nextBlock)
3022 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3023 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3024 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3025 ULONG depotBlockIndexPos;
3027 assert(depotBlockCount < This->bigBlockDepotCount);
3028 assert(blockIndex != nextBlock);
3030 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3032 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3034 else
3037 * We have to look in the extended depot.
3039 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3042 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3043 nextBlock);
3045 * Update the cached block depot, if necessary.
3047 if (depotBlockCount == This->indexBlockDepotCached)
3049 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3053 /******************************************************************************
3054 * Storage32Impl_LoadFileHeader
3056 * This method will read in the file header, i.e. big block index -1.
3058 static HRESULT StorageImpl_LoadFileHeader(
3059 StorageImpl* This)
3061 HRESULT hr = STG_E_FILENOTFOUND;
3062 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3063 BOOL success;
3064 int index;
3066 TRACE("\n");
3068 * Get a pointer to the big block of data containing the header.
3070 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3073 * Extract the information from the header.
3075 if (success)
3078 * Check for the "magic number" signature and return an error if it is not
3079 * found.
3081 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3083 return STG_E_OLDFORMAT;
3086 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3088 return STG_E_INVALIDHEADER;
3091 StorageUtl_ReadWord(
3092 headerBigBlock,
3093 OFFSET_BIGBLOCKSIZEBITS,
3094 &This->bigBlockSizeBits);
3096 StorageUtl_ReadWord(
3097 headerBigBlock,
3098 OFFSET_SMALLBLOCKSIZEBITS,
3099 &This->smallBlockSizeBits);
3101 StorageUtl_ReadDWord(
3102 headerBigBlock,
3103 OFFSET_BBDEPOTCOUNT,
3104 &This->bigBlockDepotCount);
3106 StorageUtl_ReadDWord(
3107 headerBigBlock,
3108 OFFSET_ROOTSTARTBLOCK,
3109 &This->rootStartBlock);
3111 StorageUtl_ReadDWord(
3112 headerBigBlock,
3113 OFFSET_SBDEPOTSTART,
3114 &This->smallBlockDepotStart);
3116 StorageUtl_ReadDWord(
3117 headerBigBlock,
3118 OFFSET_EXTBBDEPOTSTART,
3119 &This->extBigBlockDepotStart);
3121 StorageUtl_ReadDWord(
3122 headerBigBlock,
3123 OFFSET_EXTBBDEPOTCOUNT,
3124 &This->extBigBlockDepotCount);
3126 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3128 StorageUtl_ReadDWord(
3129 headerBigBlock,
3130 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3131 &(This->bigBlockDepotStart[index]));
3135 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3137 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3138 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3141 * Right now, the code is making some assumptions about the size of the
3142 * blocks, just make sure they are what we're expecting.
3144 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3145 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3147 WARN("Broken OLE storage file\n");
3148 hr = STG_E_INVALIDHEADER;
3150 else
3151 hr = S_OK;
3154 return hr;
3157 /******************************************************************************
3158 * Storage32Impl_SaveFileHeader
3160 * This method will save to the file the header, i.e. big block -1.
3162 static void StorageImpl_SaveFileHeader(
3163 StorageImpl* This)
3165 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3166 int index;
3167 BOOL success;
3170 * Get a pointer to the big block of data containing the header.
3172 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3175 * If the block read failed, the file is probably new.
3177 if (!success)
3180 * Initialize for all unknown fields.
3182 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3185 * Initialize the magic number.
3187 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3190 * And a bunch of things we don't know what they mean
3192 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3193 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3194 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3195 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3199 * Write the information to the header.
3201 StorageUtl_WriteWord(
3202 headerBigBlock,
3203 OFFSET_BIGBLOCKSIZEBITS,
3204 This->bigBlockSizeBits);
3206 StorageUtl_WriteWord(
3207 headerBigBlock,
3208 OFFSET_SMALLBLOCKSIZEBITS,
3209 This->smallBlockSizeBits);
3211 StorageUtl_WriteDWord(
3212 headerBigBlock,
3213 OFFSET_BBDEPOTCOUNT,
3214 This->bigBlockDepotCount);
3216 StorageUtl_WriteDWord(
3217 headerBigBlock,
3218 OFFSET_ROOTSTARTBLOCK,
3219 This->rootStartBlock);
3221 StorageUtl_WriteDWord(
3222 headerBigBlock,
3223 OFFSET_SBDEPOTSTART,
3224 This->smallBlockDepotStart);
3226 StorageUtl_WriteDWord(
3227 headerBigBlock,
3228 OFFSET_SBDEPOTCOUNT,
3229 This->smallBlockDepotChain ?
3230 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3232 StorageUtl_WriteDWord(
3233 headerBigBlock,
3234 OFFSET_EXTBBDEPOTSTART,
3235 This->extBigBlockDepotStart);
3237 StorageUtl_WriteDWord(
3238 headerBigBlock,
3239 OFFSET_EXTBBDEPOTCOUNT,
3240 This->extBigBlockDepotCount);
3242 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3244 StorageUtl_WriteDWord(
3245 headerBigBlock,
3246 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3247 (This->bigBlockDepotStart[index]));
3251 * Write the big block back to the file.
3253 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3256 /******************************************************************************
3257 * Storage32Impl_ReadProperty
3259 * This method will read the specified property from the property chain.
3261 BOOL StorageImpl_ReadProperty(
3262 StorageImpl* This,
3263 ULONG index,
3264 StgProperty* buffer)
3266 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3267 ULARGE_INTEGER offsetInPropSet;
3268 HRESULT readRes;
3269 ULONG bytesRead;
3271 offsetInPropSet.u.HighPart = 0;
3272 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3274 readRes = BlockChainStream_ReadAt(
3275 This->rootBlockChain,
3276 offsetInPropSet,
3277 PROPSET_BLOCK_SIZE,
3278 currentProperty,
3279 &bytesRead);
3281 if (SUCCEEDED(readRes))
3283 /* replace the name of root entry (often "Root Entry") by the file name */
3284 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3285 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3287 memset(buffer->name, 0, sizeof(buffer->name));
3288 memcpy(
3289 buffer->name,
3290 propName,
3291 PROPERTY_NAME_BUFFER_LEN );
3292 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3294 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3296 StorageUtl_ReadWord(
3297 currentProperty,
3298 OFFSET_PS_NAMELENGTH,
3299 &buffer->sizeOfNameString);
3301 StorageUtl_ReadDWord(
3302 currentProperty,
3303 OFFSET_PS_PREVIOUSPROP,
3304 &buffer->previousProperty);
3306 StorageUtl_ReadDWord(
3307 currentProperty,
3308 OFFSET_PS_NEXTPROP,
3309 &buffer->nextProperty);
3311 StorageUtl_ReadDWord(
3312 currentProperty,
3313 OFFSET_PS_DIRPROP,
3314 &buffer->dirProperty);
3316 StorageUtl_ReadGUID(
3317 currentProperty,
3318 OFFSET_PS_GUID,
3319 &buffer->propertyUniqueID);
3321 StorageUtl_ReadDWord(
3322 currentProperty,
3323 OFFSET_PS_TSS1,
3324 &buffer->timeStampS1);
3326 StorageUtl_ReadDWord(
3327 currentProperty,
3328 OFFSET_PS_TSD1,
3329 &buffer->timeStampD1);
3331 StorageUtl_ReadDWord(
3332 currentProperty,
3333 OFFSET_PS_TSS2,
3334 &buffer->timeStampS2);
3336 StorageUtl_ReadDWord(
3337 currentProperty,
3338 OFFSET_PS_TSD2,
3339 &buffer->timeStampD2);
3341 StorageUtl_ReadDWord(
3342 currentProperty,
3343 OFFSET_PS_STARTBLOCK,
3344 &buffer->startingBlock);
3346 StorageUtl_ReadDWord(
3347 currentProperty,
3348 OFFSET_PS_SIZE,
3349 &buffer->size.u.LowPart);
3351 buffer->size.u.HighPart = 0;
3354 return SUCCEEDED(readRes) ? TRUE : FALSE;
3357 /*********************************************************************
3358 * Write the specified property into the property chain
3360 BOOL StorageImpl_WriteProperty(
3361 StorageImpl* This,
3362 ULONG index,
3363 const StgProperty* buffer)
3365 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3366 ULARGE_INTEGER offsetInPropSet;
3367 HRESULT writeRes;
3368 ULONG bytesWritten;
3370 offsetInPropSet.u.HighPart = 0;
3371 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3373 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3375 memcpy(
3376 currentProperty + OFFSET_PS_NAME,
3377 buffer->name,
3378 PROPERTY_NAME_BUFFER_LEN );
3380 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3382 StorageUtl_WriteWord(
3383 currentProperty,
3384 OFFSET_PS_NAMELENGTH,
3385 buffer->sizeOfNameString);
3387 StorageUtl_WriteDWord(
3388 currentProperty,
3389 OFFSET_PS_PREVIOUSPROP,
3390 buffer->previousProperty);
3392 StorageUtl_WriteDWord(
3393 currentProperty,
3394 OFFSET_PS_NEXTPROP,
3395 buffer->nextProperty);
3397 StorageUtl_WriteDWord(
3398 currentProperty,
3399 OFFSET_PS_DIRPROP,
3400 buffer->dirProperty);
3402 StorageUtl_WriteGUID(
3403 currentProperty,
3404 OFFSET_PS_GUID,
3405 &buffer->propertyUniqueID);
3407 StorageUtl_WriteDWord(
3408 currentProperty,
3409 OFFSET_PS_TSS1,
3410 buffer->timeStampS1);
3412 StorageUtl_WriteDWord(
3413 currentProperty,
3414 OFFSET_PS_TSD1,
3415 buffer->timeStampD1);
3417 StorageUtl_WriteDWord(
3418 currentProperty,
3419 OFFSET_PS_TSS2,
3420 buffer->timeStampS2);
3422 StorageUtl_WriteDWord(
3423 currentProperty,
3424 OFFSET_PS_TSD2,
3425 buffer->timeStampD2);
3427 StorageUtl_WriteDWord(
3428 currentProperty,
3429 OFFSET_PS_STARTBLOCK,
3430 buffer->startingBlock);
3432 StorageUtl_WriteDWord(
3433 currentProperty,
3434 OFFSET_PS_SIZE,
3435 buffer->size.u.LowPart);
3437 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3438 offsetInPropSet,
3439 PROPSET_BLOCK_SIZE,
3440 currentProperty,
3441 &bytesWritten);
3442 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3445 static BOOL StorageImpl_ReadBigBlock(
3446 StorageImpl* This,
3447 ULONG blockIndex,
3448 void* buffer)
3450 ULARGE_INTEGER ulOffset;
3451 DWORD read;
3453 ulOffset.u.HighPart = 0;
3454 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3456 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3457 return (read == This->bigBlockSize);
3460 static BOOL StorageImpl_ReadDWordFromBigBlock(
3461 StorageImpl* This,
3462 ULONG blockIndex,
3463 ULONG offset,
3464 DWORD* value)
3466 ULARGE_INTEGER ulOffset;
3467 DWORD read;
3468 DWORD tmp;
3470 ulOffset.u.HighPart = 0;
3471 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3472 ulOffset.u.LowPart += offset;
3474 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3475 *value = lendian32toh(tmp);
3476 return (read == sizeof(DWORD));
3479 static BOOL StorageImpl_WriteBigBlock(
3480 StorageImpl* This,
3481 ULONG blockIndex,
3482 const void* buffer)
3484 ULARGE_INTEGER ulOffset;
3485 DWORD wrote;
3487 ulOffset.u.HighPart = 0;
3488 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3490 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3491 return (wrote == This->bigBlockSize);
3494 static BOOL StorageImpl_WriteDWordToBigBlock(
3495 StorageImpl* This,
3496 ULONG blockIndex,
3497 ULONG offset,
3498 DWORD value)
3500 ULARGE_INTEGER ulOffset;
3501 DWORD wrote;
3503 ulOffset.u.HighPart = 0;
3504 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3505 ulOffset.u.LowPart += offset;
3507 value = htole32(value);
3508 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3509 return (wrote == sizeof(DWORD));
3512 /******************************************************************************
3513 * Storage32Impl_SmallBlocksToBigBlocks
3515 * This method will convert a small block chain to a big block chain.
3516 * The small block chain will be destroyed.
3518 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3519 StorageImpl* This,
3520 SmallBlockChainStream** ppsbChain)
3522 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3523 ULARGE_INTEGER size, offset;
3524 ULONG cbRead, cbWritten;
3525 ULARGE_INTEGER cbTotalRead;
3526 ULONG propertyIndex;
3527 HRESULT resWrite = S_OK;
3528 HRESULT resRead;
3529 StgProperty chainProperty;
3530 BYTE *buffer;
3531 BlockChainStream *bbTempChain = NULL;
3532 BlockChainStream *bigBlockChain = NULL;
3535 * Create a temporary big block chain that doesn't have
3536 * an associated property. This temporary chain will be
3537 * used to copy data from small blocks to big blocks.
3539 bbTempChain = BlockChainStream_Construct(This,
3540 &bbHeadOfChain,
3541 PROPERTY_NULL);
3542 if(!bbTempChain) return NULL;
3544 * Grow the big block chain.
3546 size = SmallBlockChainStream_GetSize(*ppsbChain);
3547 BlockChainStream_SetSize(bbTempChain, size);
3550 * Copy the contents of the small block chain to the big block chain
3551 * by small block size increments.
3553 offset.u.LowPart = 0;
3554 offset.u.HighPart = 0;
3555 cbTotalRead.QuadPart = 0;
3557 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3560 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3561 offset,
3562 This->smallBlockSize,
3563 buffer,
3564 &cbRead);
3565 if (FAILED(resRead))
3566 break;
3568 if (cbRead > 0)
3570 cbTotalRead.QuadPart += cbRead;
3572 resWrite = BlockChainStream_WriteAt(bbTempChain,
3573 offset,
3574 cbRead,
3575 buffer,
3576 &cbWritten);
3578 if (FAILED(resWrite))
3579 break;
3581 offset.u.LowPart += This->smallBlockSize;
3583 } while (cbTotalRead.QuadPart < size.QuadPart);
3584 HeapFree(GetProcessHeap(),0,buffer);
3586 if (FAILED(resRead) || FAILED(resWrite))
3588 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3589 BlockChainStream_Destroy(bbTempChain);
3590 return NULL;
3594 * Destroy the small block chain.
3596 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3597 size.u.HighPart = 0;
3598 size.u.LowPart = 0;
3599 SmallBlockChainStream_SetSize(*ppsbChain, size);
3600 SmallBlockChainStream_Destroy(*ppsbChain);
3601 *ppsbChain = 0;
3604 * Change the property information. This chain is now a big block chain
3605 * and it doesn't reside in the small blocks chain anymore.
3607 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3609 chainProperty.startingBlock = bbHeadOfChain;
3611 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3614 * Destroy the temporary propertyless big block chain.
3615 * Create a new big block chain associated with this property.
3617 BlockChainStream_Destroy(bbTempChain);
3618 bigBlockChain = BlockChainStream_Construct(This,
3619 NULL,
3620 propertyIndex);
3622 return bigBlockChain;
3625 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3627 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3629 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3630 HeapFree(GetProcessHeap(), 0, This);
3633 /******************************************************************************
3635 ** Storage32InternalImpl_Commit
3637 ** The non-root storages cannot be opened in transacted mode thus this function
3638 ** does nothing.
3640 static HRESULT WINAPI StorageInternalImpl_Commit(
3641 IStorage* iface,
3642 DWORD grfCommitFlags) /* [in] */
3644 return S_OK;
3647 /******************************************************************************
3649 ** Storage32InternalImpl_Revert
3651 ** The non-root storages cannot be opened in transacted mode thus this function
3652 ** does nothing.
3654 static HRESULT WINAPI StorageInternalImpl_Revert(
3655 IStorage* iface)
3657 return S_OK;
3660 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3662 IStorage_Release((IStorage*)This->parentStorage);
3663 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3664 HeapFree(GetProcessHeap(), 0, This);
3667 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3668 IEnumSTATSTG* iface,
3669 REFIID riid,
3670 void** ppvObject)
3672 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3675 * Perform a sanity check on the parameters.
3677 if (ppvObject==0)
3678 return E_INVALIDARG;
3681 * Initialize the return parameter.
3683 *ppvObject = 0;
3686 * Compare the riid with the interface IDs implemented by this object.
3688 if (IsEqualGUID(&IID_IUnknown, riid) ||
3689 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3691 *ppvObject = This;
3692 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3693 return S_OK;
3696 return E_NOINTERFACE;
3699 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3700 IEnumSTATSTG* iface)
3702 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3703 return InterlockedIncrement(&This->ref);
3706 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3707 IEnumSTATSTG* iface)
3709 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3711 ULONG newRef;
3713 newRef = InterlockedDecrement(&This->ref);
3716 * If the reference count goes down to 0, perform suicide.
3718 if (newRef==0)
3720 IEnumSTATSTGImpl_Destroy(This);
3723 return newRef;
3726 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3727 IEnumSTATSTG* iface,
3728 ULONG celt,
3729 STATSTG* rgelt,
3730 ULONG* pceltFetched)
3732 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3734 StgProperty currentProperty;
3735 STATSTG* currentReturnStruct = rgelt;
3736 ULONG objectFetched = 0;
3737 ULONG currentSearchNode;
3740 * Perform a sanity check on the parameters.
3742 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3743 return E_INVALIDARG;
3746 * To avoid the special case, get another pointer to a ULONG value if
3747 * the caller didn't supply one.
3749 if (pceltFetched==0)
3750 pceltFetched = &objectFetched;
3753 * Start the iteration, we will iterate until we hit the end of the
3754 * linked list or until we hit the number of items to iterate through
3756 *pceltFetched = 0;
3759 * Start with the node at the top of the stack.
3761 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3763 while ( ( *pceltFetched < celt) &&
3764 ( currentSearchNode!=PROPERTY_NULL) )
3767 * Remove the top node from the stack
3769 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3772 * Read the property from the storage.
3774 StorageImpl_ReadProperty(This->parentStorage,
3775 currentSearchNode,
3776 &currentProperty);
3779 * Copy the information to the return buffer.
3781 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3782 &currentProperty,
3783 STATFLAG_DEFAULT);
3786 * Step to the next item in the iteration
3788 (*pceltFetched)++;
3789 currentReturnStruct++;
3792 * Push the next search node in the search stack.
3794 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3797 * continue the iteration.
3799 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3802 if (*pceltFetched == celt)
3803 return S_OK;
3805 return S_FALSE;
3809 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3810 IEnumSTATSTG* iface,
3811 ULONG celt)
3813 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3815 StgProperty currentProperty;
3816 ULONG objectFetched = 0;
3817 ULONG currentSearchNode;
3820 * Start with the node at the top of the stack.
3822 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3824 while ( (objectFetched < celt) &&
3825 (currentSearchNode!=PROPERTY_NULL) )
3828 * Remove the top node from the stack
3830 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3833 * Read the property from the storage.
3835 StorageImpl_ReadProperty(This->parentStorage,
3836 currentSearchNode,
3837 &currentProperty);
3840 * Step to the next item in the iteration
3842 objectFetched++;
3845 * Push the next search node in the search stack.
3847 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3850 * continue the iteration.
3852 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3855 if (objectFetched == celt)
3856 return S_OK;
3858 return S_FALSE;
3861 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3862 IEnumSTATSTG* iface)
3864 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3866 StgProperty rootProperty;
3867 BOOL readSuccessful;
3870 * Re-initialize the search stack to an empty stack
3872 This->stackSize = 0;
3875 * Read the root property from the storage.
3877 readSuccessful = StorageImpl_ReadProperty(
3878 This->parentStorage,
3879 This->firstPropertyNode,
3880 &rootProperty);
3882 if (readSuccessful)
3884 assert(rootProperty.sizeOfNameString!=0);
3887 * Push the search node in the search stack.
3889 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3892 return S_OK;
3895 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3896 IEnumSTATSTG* iface,
3897 IEnumSTATSTG** ppenum)
3899 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3901 IEnumSTATSTGImpl* newClone;
3904 * Perform a sanity check on the parameters.
3906 if (ppenum==0)
3907 return E_INVALIDARG;
3909 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3910 This->firstPropertyNode);
3914 * The new clone enumeration must point to the same current node as
3915 * the ole one.
3917 newClone->stackSize = This->stackSize ;
3918 newClone->stackMaxSize = This->stackMaxSize ;
3919 newClone->stackToVisit =
3920 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3922 memcpy(
3923 newClone->stackToVisit,
3924 This->stackToVisit,
3925 sizeof(ULONG) * newClone->stackSize);
3927 *ppenum = (IEnumSTATSTG*)newClone;
3930 * Don't forget to nail down a reference to the clone before
3931 * returning it.
3933 IEnumSTATSTGImpl_AddRef(*ppenum);
3935 return S_OK;
3938 static INT IEnumSTATSTGImpl_FindParentProperty(
3939 IEnumSTATSTGImpl *This,
3940 ULONG childProperty,
3941 StgProperty *currentProperty,
3942 ULONG *thisNodeId)
3944 ULONG currentSearchNode;
3945 ULONG foundNode;
3948 * To avoid the special case, get another pointer to a ULONG value if
3949 * the caller didn't supply one.
3951 if (thisNodeId==0)
3952 thisNodeId = &foundNode;
3955 * Start with the node at the top of the stack.
3957 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3960 while (currentSearchNode!=PROPERTY_NULL)
3963 * Store the current node in the returned parameters
3965 *thisNodeId = currentSearchNode;
3968 * Remove the top node from the stack
3970 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3973 * Read the property from the storage.
3975 StorageImpl_ReadProperty(
3976 This->parentStorage,
3977 currentSearchNode,
3978 currentProperty);
3980 if (currentProperty->previousProperty == childProperty)
3981 return PROPERTY_RELATION_PREVIOUS;
3983 else if (currentProperty->nextProperty == childProperty)
3984 return PROPERTY_RELATION_NEXT;
3986 else if (currentProperty->dirProperty == childProperty)
3987 return PROPERTY_RELATION_DIR;
3990 * Push the next search node in the search stack.
3992 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3995 * continue the iteration.
3997 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4000 return PROPERTY_NULL;
4003 static ULONG IEnumSTATSTGImpl_FindProperty(
4004 IEnumSTATSTGImpl* This,
4005 const OLECHAR* lpszPropName,
4006 StgProperty* currentProperty)
4008 ULONG currentSearchNode;
4011 * Start with the node at the top of the stack.
4013 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4015 while (currentSearchNode!=PROPERTY_NULL)
4018 * Remove the top node from the stack
4020 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4023 * Read the property from the storage.
4025 StorageImpl_ReadProperty(This->parentStorage,
4026 currentSearchNode,
4027 currentProperty);
4029 if (propertyNameCmp(currentProperty->name, lpszPropName) == 0)
4030 return currentSearchNode;
4033 * Push the next search node in the search stack.
4035 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4038 * continue the iteration.
4040 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4043 return PROPERTY_NULL;
4046 static void IEnumSTATSTGImpl_PushSearchNode(
4047 IEnumSTATSTGImpl* This,
4048 ULONG nodeToPush)
4050 StgProperty rootProperty;
4051 BOOL readSuccessful;
4054 * First, make sure we're not trying to push an unexisting node.
4056 if (nodeToPush==PROPERTY_NULL)
4057 return;
4060 * First push the node to the stack
4062 if (This->stackSize == This->stackMaxSize)
4064 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4066 This->stackToVisit = HeapReAlloc(
4067 GetProcessHeap(),
4069 This->stackToVisit,
4070 sizeof(ULONG) * This->stackMaxSize);
4073 This->stackToVisit[This->stackSize] = nodeToPush;
4074 This->stackSize++;
4077 * Read the root property from the storage.
4079 readSuccessful = StorageImpl_ReadProperty(
4080 This->parentStorage,
4081 nodeToPush,
4082 &rootProperty);
4084 if (readSuccessful)
4086 assert(rootProperty.sizeOfNameString!=0);
4089 * Push the previous search node in the search stack.
4091 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4095 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4096 IEnumSTATSTGImpl* This,
4097 BOOL remove)
4099 ULONG topNode;
4101 if (This->stackSize == 0)
4102 return PROPERTY_NULL;
4104 topNode = This->stackToVisit[This->stackSize-1];
4106 if (remove)
4107 This->stackSize--;
4109 return topNode;
4113 * Virtual function table for the IEnumSTATSTGImpl class.
4115 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4117 IEnumSTATSTGImpl_QueryInterface,
4118 IEnumSTATSTGImpl_AddRef,
4119 IEnumSTATSTGImpl_Release,
4120 IEnumSTATSTGImpl_Next,
4121 IEnumSTATSTGImpl_Skip,
4122 IEnumSTATSTGImpl_Reset,
4123 IEnumSTATSTGImpl_Clone
4126 /******************************************************************************
4127 ** IEnumSTATSTGImpl implementation
4130 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4131 StorageImpl* parentStorage,
4132 ULONG firstPropertyNode)
4134 IEnumSTATSTGImpl* newEnumeration;
4136 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4138 if (newEnumeration!=0)
4141 * Set-up the virtual function table and reference count.
4143 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4144 newEnumeration->ref = 0;
4147 * We want to nail-down the reference to the storage in case the
4148 * enumeration out-lives the storage in the client application.
4150 newEnumeration->parentStorage = parentStorage;
4151 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4153 newEnumeration->firstPropertyNode = firstPropertyNode;
4156 * Initialize the search stack
4158 newEnumeration->stackSize = 0;
4159 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4160 newEnumeration->stackToVisit =
4161 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4164 * Make sure the current node of the iterator is the first one.
4166 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4169 return newEnumeration;
4173 * Virtual function table for the Storage32InternalImpl class.
4175 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4177 StorageBaseImpl_QueryInterface,
4178 StorageBaseImpl_AddRef,
4179 StorageBaseImpl_Release,
4180 StorageBaseImpl_CreateStream,
4181 StorageBaseImpl_OpenStream,
4182 StorageImpl_CreateStorage,
4183 StorageBaseImpl_OpenStorage,
4184 StorageImpl_CopyTo,
4185 StorageImpl_MoveElementTo,
4186 StorageInternalImpl_Commit,
4187 StorageInternalImpl_Revert,
4188 StorageBaseImpl_EnumElements,
4189 StorageImpl_DestroyElement,
4190 StorageBaseImpl_RenameElement,
4191 StorageImpl_SetElementTimes,
4192 StorageBaseImpl_SetClass,
4193 StorageImpl_SetStateBits,
4194 StorageBaseImpl_Stat
4197 /******************************************************************************
4198 ** Storage32InternalImpl implementation
4201 static StorageInternalImpl* StorageInternalImpl_Construct(
4202 StorageImpl* ancestorStorage,
4203 DWORD openFlags,
4204 ULONG rootPropertyIndex)
4206 StorageInternalImpl* newStorage;
4209 * Allocate space for the new storage object
4211 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4213 if (newStorage!=0)
4216 * Initialize the stream list
4218 list_init(&newStorage->base.strmHead);
4221 * Initialize the virtual function table.
4223 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4224 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
4225 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4228 * Keep the ancestor storage pointer and nail a reference to it.
4230 newStorage->base.ancestorStorage = ancestorStorage;
4231 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4234 * Keep the index of the root property set for this storage,
4236 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4238 return newStorage;
4241 return 0;
4244 /******************************************************************************
4245 ** StorageUtl implementation
4248 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4250 WORD tmp;
4252 memcpy(&tmp, buffer+offset, sizeof(WORD));
4253 *value = lendian16toh(tmp);
4256 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4258 value = htole16(value);
4259 memcpy(buffer+offset, &value, sizeof(WORD));
4262 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4264 DWORD tmp;
4266 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4267 *value = lendian32toh(tmp);
4270 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4272 value = htole32(value);
4273 memcpy(buffer+offset, &value, sizeof(DWORD));
4276 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4277 ULARGE_INTEGER* value)
4279 #ifdef WORDS_BIGENDIAN
4280 ULARGE_INTEGER tmp;
4282 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4283 value->u.LowPart = htole32(tmp.u.HighPart);
4284 value->u.HighPart = htole32(tmp.u.LowPart);
4285 #else
4286 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4287 #endif
4290 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4291 const ULARGE_INTEGER *value)
4293 #ifdef WORDS_BIGENDIAN
4294 ULARGE_INTEGER tmp;
4296 tmp.u.LowPart = htole32(value->u.HighPart);
4297 tmp.u.HighPart = htole32(value->u.LowPart);
4298 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4299 #else
4300 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4301 #endif
4304 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4306 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4307 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4308 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4310 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4313 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4315 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4316 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4317 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4319 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4322 void StorageUtl_CopyPropertyToSTATSTG(
4323 STATSTG* destination,
4324 const StgProperty* source,
4325 int statFlags)
4328 * The copy of the string occurs only when the flag is not set
4330 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4331 (source->name == NULL) ||
4332 (source->name[0] == 0) )
4334 destination->pwcsName = 0;
4336 else
4338 destination->pwcsName =
4339 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4341 strcpyW(destination->pwcsName, source->name);
4344 switch (source->propertyType)
4346 case PROPTYPE_STORAGE:
4347 case PROPTYPE_ROOT:
4348 destination->type = STGTY_STORAGE;
4349 break;
4350 case PROPTYPE_STREAM:
4351 destination->type = STGTY_STREAM;
4352 break;
4353 default:
4354 destination->type = STGTY_STREAM;
4355 break;
4358 destination->cbSize = source->size;
4360 currentReturnStruct->mtime = {0}; TODO
4361 currentReturnStruct->ctime = {0};
4362 currentReturnStruct->atime = {0};
4364 destination->grfMode = 0;
4365 destination->grfLocksSupported = 0;
4366 destination->clsid = source->propertyUniqueID;
4367 destination->grfStateBits = 0;
4368 destination->reserved = 0;
4371 /******************************************************************************
4372 ** BlockChainStream implementation
4375 BlockChainStream* BlockChainStream_Construct(
4376 StorageImpl* parentStorage,
4377 ULONG* headOfStreamPlaceHolder,
4378 ULONG propertyIndex)
4380 BlockChainStream* newStream;
4381 ULONG blockIndex;
4383 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4385 newStream->parentStorage = parentStorage;
4386 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4387 newStream->ownerPropertyIndex = propertyIndex;
4388 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4389 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4390 newStream->numBlocks = 0;
4392 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4394 while (blockIndex != BLOCK_END_OF_CHAIN)
4396 newStream->numBlocks++;
4397 newStream->tailIndex = blockIndex;
4399 if(FAILED(StorageImpl_GetNextBlockInChain(
4400 parentStorage,
4401 blockIndex,
4402 &blockIndex)))
4404 HeapFree(GetProcessHeap(), 0, newStream);
4405 return NULL;
4409 return newStream;
4412 void BlockChainStream_Destroy(BlockChainStream* This)
4414 HeapFree(GetProcessHeap(), 0, This);
4417 /******************************************************************************
4418 * BlockChainStream_GetHeadOfChain
4420 * Returns the head of this stream chain.
4421 * Some special chains don't have properties, their heads are kept in
4422 * This->headOfStreamPlaceHolder.
4425 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4427 StgProperty chainProperty;
4428 BOOL readSuccessful;
4430 if (This->headOfStreamPlaceHolder != 0)
4431 return *(This->headOfStreamPlaceHolder);
4433 if (This->ownerPropertyIndex != PROPERTY_NULL)
4435 readSuccessful = StorageImpl_ReadProperty(
4436 This->parentStorage,
4437 This->ownerPropertyIndex,
4438 &chainProperty);
4440 if (readSuccessful)
4442 return chainProperty.startingBlock;
4446 return BLOCK_END_OF_CHAIN;
4449 /******************************************************************************
4450 * BlockChainStream_GetCount
4452 * Returns the number of blocks that comprises this chain.
4453 * This is not the size of the stream as the last block may not be full!
4456 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4458 ULONG blockIndex;
4459 ULONG count = 0;
4461 blockIndex = BlockChainStream_GetHeadOfChain(This);
4463 while (blockIndex != BLOCK_END_OF_CHAIN)
4465 count++;
4467 if(FAILED(StorageImpl_GetNextBlockInChain(
4468 This->parentStorage,
4469 blockIndex,
4470 &blockIndex)))
4471 return 0;
4474 return count;
4477 /******************************************************************************
4478 * BlockChainStream_ReadAt
4480 * Reads a specified number of bytes from this chain at the specified offset.
4481 * bytesRead may be NULL.
4482 * Failure will be returned if the specified number of bytes has not been read.
4484 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4485 ULARGE_INTEGER offset,
4486 ULONG size,
4487 void* buffer,
4488 ULONG* bytesRead)
4490 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4491 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4492 ULONG bytesToReadInBuffer;
4493 ULONG blockIndex;
4494 BYTE* bufferWalker;
4496 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4499 * Find the first block in the stream that contains part of the buffer.
4501 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4502 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4503 (blockNoInSequence < This->lastBlockNoInSequence) )
4505 blockIndex = BlockChainStream_GetHeadOfChain(This);
4506 This->lastBlockNoInSequence = blockNoInSequence;
4508 else
4510 ULONG temp = blockNoInSequence;
4512 blockIndex = This->lastBlockNoInSequenceIndex;
4513 blockNoInSequence -= This->lastBlockNoInSequence;
4514 This->lastBlockNoInSequence = temp;
4517 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4519 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4520 return STG_E_DOCFILECORRUPT;
4521 blockNoInSequence--;
4524 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4525 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4527 This->lastBlockNoInSequenceIndex = blockIndex;
4530 * Start reading the buffer.
4532 *bytesRead = 0;
4533 bufferWalker = buffer;
4535 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4537 ULARGE_INTEGER ulOffset;
4538 DWORD bytesReadAt;
4540 * Calculate how many bytes we can copy from this big block.
4542 bytesToReadInBuffer =
4543 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4545 TRACE("block %i\n",blockIndex);
4546 ulOffset.u.HighPart = 0;
4547 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4548 offsetInBlock;
4550 StorageImpl_ReadAt(This->parentStorage,
4551 ulOffset,
4552 bufferWalker,
4553 bytesToReadInBuffer,
4554 &bytesReadAt);
4556 * Step to the next big block.
4558 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4559 return STG_E_DOCFILECORRUPT;
4561 bufferWalker += bytesReadAt;
4562 size -= bytesReadAt;
4563 *bytesRead += bytesReadAt;
4564 offsetInBlock = 0; /* There is no offset on the next block */
4566 if (bytesToReadInBuffer != bytesReadAt)
4567 break;
4570 return (size == 0) ? S_OK : STG_E_READFAULT;
4573 /******************************************************************************
4574 * BlockChainStream_WriteAt
4576 * Writes the specified number of bytes to this chain at the specified offset.
4577 * bytesWritten may be NULL.
4578 * Will fail if not all specified number of bytes have been written.
4580 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4581 ULARGE_INTEGER offset,
4582 ULONG size,
4583 const void* buffer,
4584 ULONG* bytesWritten)
4586 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4587 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4588 ULONG bytesToWrite;
4589 ULONG blockIndex;
4590 const BYTE* bufferWalker;
4593 * Find the first block in the stream that contains part of the buffer.
4595 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4596 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4597 (blockNoInSequence < This->lastBlockNoInSequence) )
4599 blockIndex = BlockChainStream_GetHeadOfChain(This);
4600 This->lastBlockNoInSequence = blockNoInSequence;
4602 else
4604 ULONG temp = blockNoInSequence;
4606 blockIndex = This->lastBlockNoInSequenceIndex;
4607 blockNoInSequence -= This->lastBlockNoInSequence;
4608 This->lastBlockNoInSequence = temp;
4611 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4613 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4614 &blockIndex)))
4615 return STG_E_DOCFILECORRUPT;
4616 blockNoInSequence--;
4619 This->lastBlockNoInSequenceIndex = blockIndex;
4621 /* BlockChainStream_SetSize should have already been called to ensure we have
4622 * enough blocks in the chain to write into */
4623 if (blockIndex == BLOCK_END_OF_CHAIN)
4625 ERR("not enough blocks in chain to write data\n");
4626 return STG_E_DOCFILECORRUPT;
4629 *bytesWritten = 0;
4630 bufferWalker = buffer;
4632 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4634 ULARGE_INTEGER ulOffset;
4635 DWORD bytesWrittenAt;
4637 * Calculate how many bytes we can copy from this big block.
4639 bytesToWrite =
4640 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4642 TRACE("block %i\n",blockIndex);
4643 ulOffset.u.HighPart = 0;
4644 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4645 offsetInBlock;
4647 StorageImpl_WriteAt(This->parentStorage,
4648 ulOffset,
4649 bufferWalker,
4650 bytesToWrite,
4651 &bytesWrittenAt);
4654 * Step to the next big block.
4656 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4657 &blockIndex)))
4658 return STG_E_DOCFILECORRUPT;
4660 bufferWalker += bytesWrittenAt;
4661 size -= bytesWrittenAt;
4662 *bytesWritten += bytesWrittenAt;
4663 offsetInBlock = 0; /* There is no offset on the next block */
4665 if (bytesWrittenAt != bytesToWrite)
4666 break;
4669 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4672 /******************************************************************************
4673 * BlockChainStream_Shrink
4675 * Shrinks this chain in the big block depot.
4677 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4678 ULARGE_INTEGER newSize)
4680 ULONG blockIndex, extraBlock;
4681 ULONG numBlocks;
4682 ULONG count = 1;
4685 * Reset the last accessed block cache.
4687 This->lastBlockNoInSequence = 0xFFFFFFFF;
4688 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4691 * Figure out how many blocks are needed to contain the new size
4693 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4695 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4696 numBlocks++;
4698 blockIndex = BlockChainStream_GetHeadOfChain(This);
4701 * Go to the new end of chain
4703 while (count < numBlocks)
4705 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4706 &blockIndex)))
4707 return FALSE;
4708 count++;
4711 /* Get the next block before marking the new end */
4712 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4713 &extraBlock)))
4714 return FALSE;
4716 /* Mark the new end of chain */
4717 StorageImpl_SetNextBlockInChain(
4718 This->parentStorage,
4719 blockIndex,
4720 BLOCK_END_OF_CHAIN);
4722 This->tailIndex = blockIndex;
4723 This->numBlocks = numBlocks;
4726 * Mark the extra blocks as free
4728 while (extraBlock != BLOCK_END_OF_CHAIN)
4730 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4731 &blockIndex)))
4732 return FALSE;
4733 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4734 extraBlock = blockIndex;
4737 return TRUE;
4740 /******************************************************************************
4741 * BlockChainStream_Enlarge
4743 * Grows this chain in the big block depot.
4745 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4746 ULARGE_INTEGER newSize)
4748 ULONG blockIndex, currentBlock;
4749 ULONG newNumBlocks;
4750 ULONG oldNumBlocks = 0;
4752 blockIndex = BlockChainStream_GetHeadOfChain(This);
4755 * Empty chain. Create the head.
4757 if (blockIndex == BLOCK_END_OF_CHAIN)
4759 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4760 StorageImpl_SetNextBlockInChain(This->parentStorage,
4761 blockIndex,
4762 BLOCK_END_OF_CHAIN);
4764 if (This->headOfStreamPlaceHolder != 0)
4766 *(This->headOfStreamPlaceHolder) = blockIndex;
4768 else
4770 StgProperty chainProp;
4771 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4773 StorageImpl_ReadProperty(
4774 This->parentStorage,
4775 This->ownerPropertyIndex,
4776 &chainProp);
4778 chainProp.startingBlock = blockIndex;
4780 StorageImpl_WriteProperty(
4781 This->parentStorage,
4782 This->ownerPropertyIndex,
4783 &chainProp);
4786 This->tailIndex = blockIndex;
4787 This->numBlocks = 1;
4791 * Figure out how many blocks are needed to contain this stream
4793 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4795 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4796 newNumBlocks++;
4799 * Go to the current end of chain
4801 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4803 currentBlock = blockIndex;
4805 while (blockIndex != BLOCK_END_OF_CHAIN)
4807 This->numBlocks++;
4808 currentBlock = blockIndex;
4810 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4811 &blockIndex)))
4812 return FALSE;
4815 This->tailIndex = currentBlock;
4818 currentBlock = This->tailIndex;
4819 oldNumBlocks = This->numBlocks;
4822 * Add new blocks to the chain
4824 if (oldNumBlocks < newNumBlocks)
4826 while (oldNumBlocks < newNumBlocks)
4828 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4830 StorageImpl_SetNextBlockInChain(
4831 This->parentStorage,
4832 currentBlock,
4833 blockIndex);
4835 StorageImpl_SetNextBlockInChain(
4836 This->parentStorage,
4837 blockIndex,
4838 BLOCK_END_OF_CHAIN);
4840 currentBlock = blockIndex;
4841 oldNumBlocks++;
4844 This->tailIndex = blockIndex;
4845 This->numBlocks = newNumBlocks;
4848 return TRUE;
4851 /******************************************************************************
4852 * BlockChainStream_SetSize
4854 * Sets the size of this stream. The big block depot will be updated.
4855 * The file will grow if we grow the chain.
4857 * TODO: Free the actual blocks in the file when we shrink the chain.
4858 * Currently, the blocks are still in the file. So the file size
4859 * doesn't shrink even if we shrink streams.
4861 BOOL BlockChainStream_SetSize(
4862 BlockChainStream* This,
4863 ULARGE_INTEGER newSize)
4865 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4867 if (newSize.u.LowPart == size.u.LowPart)
4868 return TRUE;
4870 if (newSize.u.LowPart < size.u.LowPart)
4872 BlockChainStream_Shrink(This, newSize);
4874 else
4876 BlockChainStream_Enlarge(This, newSize);
4879 return TRUE;
4882 /******************************************************************************
4883 * BlockChainStream_GetSize
4885 * Returns the size of this chain.
4886 * Will return the block count if this chain doesn't have a property.
4888 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4890 StgProperty chainProperty;
4892 if(This->headOfStreamPlaceHolder == NULL)
4895 * This chain is a data stream read the property and return
4896 * the appropriate size
4898 StorageImpl_ReadProperty(
4899 This->parentStorage,
4900 This->ownerPropertyIndex,
4901 &chainProperty);
4903 return chainProperty.size;
4905 else
4908 * this chain is a chain that does not have a property, figure out the
4909 * size by making the product number of used blocks times the
4910 * size of them
4912 ULARGE_INTEGER result;
4913 result.u.HighPart = 0;
4915 result.u.LowPart =
4916 BlockChainStream_GetCount(This) *
4917 This->parentStorage->bigBlockSize;
4919 return result;
4923 /******************************************************************************
4924 ** SmallBlockChainStream implementation
4927 SmallBlockChainStream* SmallBlockChainStream_Construct(
4928 StorageImpl* parentStorage,
4929 ULONG propertyIndex)
4931 SmallBlockChainStream* newStream;
4933 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4935 newStream->parentStorage = parentStorage;
4936 newStream->ownerPropertyIndex = propertyIndex;
4938 return newStream;
4941 void SmallBlockChainStream_Destroy(
4942 SmallBlockChainStream* This)
4944 HeapFree(GetProcessHeap(), 0, This);
4947 /******************************************************************************
4948 * SmallBlockChainStream_GetHeadOfChain
4950 * Returns the head of this chain of small blocks.
4952 static ULONG SmallBlockChainStream_GetHeadOfChain(
4953 SmallBlockChainStream* This)
4955 StgProperty chainProperty;
4956 BOOL readSuccessful;
4958 if (This->ownerPropertyIndex)
4960 readSuccessful = StorageImpl_ReadProperty(
4961 This->parentStorage,
4962 This->ownerPropertyIndex,
4963 &chainProperty);
4965 if (readSuccessful)
4967 return chainProperty.startingBlock;
4972 return BLOCK_END_OF_CHAIN;
4975 /******************************************************************************
4976 * SmallBlockChainStream_GetNextBlockInChain
4978 * Returns the index of the next small block in this chain.
4980 * Return Values:
4981 * - BLOCK_END_OF_CHAIN: end of this chain
4982 * - BLOCK_UNUSED: small block 'blockIndex' is free
4984 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4985 SmallBlockChainStream* This,
4986 ULONG blockIndex,
4987 ULONG* nextBlockInChain)
4989 ULARGE_INTEGER offsetOfBlockInDepot;
4990 DWORD buffer;
4991 ULONG bytesRead;
4992 HRESULT res;
4994 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4996 offsetOfBlockInDepot.u.HighPart = 0;
4997 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5000 * Read those bytes in the buffer from the small block file.
5002 res = BlockChainStream_ReadAt(
5003 This->parentStorage->smallBlockDepotChain,
5004 offsetOfBlockInDepot,
5005 sizeof(DWORD),
5006 &buffer,
5007 &bytesRead);
5009 if (SUCCEEDED(res))
5011 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5012 return S_OK;
5015 return res;
5018 /******************************************************************************
5019 * SmallBlockChainStream_SetNextBlockInChain
5021 * Writes the index of the next block of the specified block in the small
5022 * block depot.
5023 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5024 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5026 static void SmallBlockChainStream_SetNextBlockInChain(
5027 SmallBlockChainStream* This,
5028 ULONG blockIndex,
5029 ULONG nextBlock)
5031 ULARGE_INTEGER offsetOfBlockInDepot;
5032 DWORD buffer;
5033 ULONG bytesWritten;
5035 offsetOfBlockInDepot.u.HighPart = 0;
5036 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5038 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5041 * Read those bytes in the buffer from the small block file.
5043 BlockChainStream_WriteAt(
5044 This->parentStorage->smallBlockDepotChain,
5045 offsetOfBlockInDepot,
5046 sizeof(DWORD),
5047 &buffer,
5048 &bytesWritten);
5051 /******************************************************************************
5052 * SmallBlockChainStream_FreeBlock
5054 * Flag small block 'blockIndex' as free in the small block depot.
5056 static void SmallBlockChainStream_FreeBlock(
5057 SmallBlockChainStream* This,
5058 ULONG blockIndex)
5060 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5063 /******************************************************************************
5064 * SmallBlockChainStream_GetNextFreeBlock
5066 * Returns the index of a free small block. The small block depot will be
5067 * enlarged if necessary. The small block chain will also be enlarged if
5068 * necessary.
5070 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5071 SmallBlockChainStream* This)
5073 ULARGE_INTEGER offsetOfBlockInDepot;
5074 DWORD buffer;
5075 ULONG bytesRead;
5076 ULONG blockIndex = 0;
5077 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5078 HRESULT res = S_OK;
5079 ULONG smallBlocksPerBigBlock;
5081 offsetOfBlockInDepot.u.HighPart = 0;
5084 * Scan the small block depot for a free block
5086 while (nextBlockIndex != BLOCK_UNUSED)
5088 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5090 res = BlockChainStream_ReadAt(
5091 This->parentStorage->smallBlockDepotChain,
5092 offsetOfBlockInDepot,
5093 sizeof(DWORD),
5094 &buffer,
5095 &bytesRead);
5098 * If we run out of space for the small block depot, enlarge it
5100 if (SUCCEEDED(res))
5102 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5104 if (nextBlockIndex != BLOCK_UNUSED)
5105 blockIndex++;
5107 else
5109 ULONG count =
5110 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5112 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5113 ULONG nextBlock, newsbdIndex;
5114 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5116 nextBlock = sbdIndex;
5117 while (nextBlock != BLOCK_END_OF_CHAIN)
5119 sbdIndex = nextBlock;
5120 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5123 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5124 if (sbdIndex != BLOCK_END_OF_CHAIN)
5125 StorageImpl_SetNextBlockInChain(
5126 This->parentStorage,
5127 sbdIndex,
5128 newsbdIndex);
5130 StorageImpl_SetNextBlockInChain(
5131 This->parentStorage,
5132 newsbdIndex,
5133 BLOCK_END_OF_CHAIN);
5136 * Initialize all the small blocks to free
5138 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5139 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5141 if (count == 0)
5144 * We have just created the small block depot.
5146 StgProperty rootProp;
5147 ULONG sbStartIndex;
5150 * Save it in the header
5152 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5153 StorageImpl_SaveFileHeader(This->parentStorage);
5156 * And allocate the first big block that will contain small blocks
5158 sbStartIndex =
5159 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5161 StorageImpl_SetNextBlockInChain(
5162 This->parentStorage,
5163 sbStartIndex,
5164 BLOCK_END_OF_CHAIN);
5166 StorageImpl_ReadProperty(
5167 This->parentStorage,
5168 This->parentStorage->base.rootPropertySetIndex,
5169 &rootProp);
5171 rootProp.startingBlock = sbStartIndex;
5172 rootProp.size.u.HighPart = 0;
5173 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5175 StorageImpl_WriteProperty(
5176 This->parentStorage,
5177 This->parentStorage->base.rootPropertySetIndex,
5178 &rootProp);
5180 else
5181 StorageImpl_SaveFileHeader(This->parentStorage);
5185 smallBlocksPerBigBlock =
5186 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5189 * Verify if we have to allocate big blocks to contain small blocks
5191 if (blockIndex % smallBlocksPerBigBlock == 0)
5193 StgProperty rootProp;
5194 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5196 StorageImpl_ReadProperty(
5197 This->parentStorage,
5198 This->parentStorage->base.rootPropertySetIndex,
5199 &rootProp);
5201 if (rootProp.size.u.LowPart <
5202 (blocksRequired * This->parentStorage->bigBlockSize))
5204 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5206 BlockChainStream_SetSize(
5207 This->parentStorage->smallBlockRootChain,
5208 rootProp.size);
5210 StorageImpl_WriteProperty(
5211 This->parentStorage,
5212 This->parentStorage->base.rootPropertySetIndex,
5213 &rootProp);
5217 return blockIndex;
5220 /******************************************************************************
5221 * SmallBlockChainStream_ReadAt
5223 * Reads a specified number of bytes from this chain at the specified offset.
5224 * bytesRead may be NULL.
5225 * Failure will be returned if the specified number of bytes has not been read.
5227 HRESULT SmallBlockChainStream_ReadAt(
5228 SmallBlockChainStream* This,
5229 ULARGE_INTEGER offset,
5230 ULONG size,
5231 void* buffer,
5232 ULONG* bytesRead)
5234 HRESULT rc = S_OK;
5235 ULARGE_INTEGER offsetInBigBlockFile;
5236 ULONG blockNoInSequence =
5237 offset.u.LowPart / This->parentStorage->smallBlockSize;
5239 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5240 ULONG bytesToReadInBuffer;
5241 ULONG blockIndex;
5242 ULONG bytesReadFromBigBlockFile;
5243 BYTE* bufferWalker;
5246 * This should never happen on a small block file.
5248 assert(offset.u.HighPart==0);
5251 * Find the first block in the stream that contains part of the buffer.
5253 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5255 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5257 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5258 if(FAILED(rc))
5259 return rc;
5260 blockNoInSequence--;
5264 * Start reading the buffer.
5266 *bytesRead = 0;
5267 bufferWalker = buffer;
5269 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5272 * Calculate how many bytes we can copy from this small block.
5274 bytesToReadInBuffer =
5275 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5278 * Calculate the offset of the small block in the small block file.
5280 offsetInBigBlockFile.u.HighPart = 0;
5281 offsetInBigBlockFile.u.LowPart =
5282 blockIndex * This->parentStorage->smallBlockSize;
5284 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5287 * Read those bytes in the buffer from the small block file.
5288 * The small block has already been identified so it shouldn't fail
5289 * unless the file is corrupt.
5291 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5292 offsetInBigBlockFile,
5293 bytesToReadInBuffer,
5294 bufferWalker,
5295 &bytesReadFromBigBlockFile);
5297 if (FAILED(rc))
5298 return rc;
5301 * Step to the next big block.
5303 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5304 if(FAILED(rc))
5305 return STG_E_DOCFILECORRUPT;
5307 bufferWalker += bytesReadFromBigBlockFile;
5308 size -= bytesReadFromBigBlockFile;
5309 *bytesRead += bytesReadFromBigBlockFile;
5310 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5313 return (size == 0) ? S_OK : STG_E_READFAULT;
5316 /******************************************************************************
5317 * SmallBlockChainStream_WriteAt
5319 * Writes the specified number of bytes to this chain at the specified offset.
5320 * bytesWritten may be NULL.
5321 * Will fail if not all specified number of bytes have been written.
5323 HRESULT SmallBlockChainStream_WriteAt(
5324 SmallBlockChainStream* This,
5325 ULARGE_INTEGER offset,
5326 ULONG size,
5327 const void* buffer,
5328 ULONG* bytesWritten)
5330 ULARGE_INTEGER offsetInBigBlockFile;
5331 ULONG blockNoInSequence =
5332 offset.u.LowPart / This->parentStorage->smallBlockSize;
5334 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5335 ULONG bytesToWriteInBuffer;
5336 ULONG blockIndex;
5337 ULONG bytesWrittenToBigBlockFile;
5338 const BYTE* bufferWalker;
5339 HRESULT res;
5342 * This should never happen on a small block file.
5344 assert(offset.u.HighPart==0);
5347 * Find the first block in the stream that contains part of the buffer.
5349 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5351 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5353 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5354 return STG_E_DOCFILECORRUPT;
5355 blockNoInSequence--;
5359 * Start writing the buffer.
5361 * Here, I'm casting away the constness on the buffer variable
5362 * This is OK since we don't intend to modify that buffer.
5364 *bytesWritten = 0;
5365 bufferWalker = buffer;
5366 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5369 * Calculate how many bytes we can copy to this small block.
5371 bytesToWriteInBuffer =
5372 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5375 * Calculate the offset of the small block in the small block file.
5377 offsetInBigBlockFile.u.HighPart = 0;
5378 offsetInBigBlockFile.u.LowPart =
5379 blockIndex * This->parentStorage->smallBlockSize;
5381 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5384 * Write those bytes in the buffer to the small block file.
5386 res = BlockChainStream_WriteAt(
5387 This->parentStorage->smallBlockRootChain,
5388 offsetInBigBlockFile,
5389 bytesToWriteInBuffer,
5390 bufferWalker,
5391 &bytesWrittenToBigBlockFile);
5392 if (FAILED(res))
5393 return res;
5396 * Step to the next big block.
5398 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5399 &blockIndex)))
5400 return FALSE;
5401 bufferWalker += bytesWrittenToBigBlockFile;
5402 size -= bytesWrittenToBigBlockFile;
5403 *bytesWritten += bytesWrittenToBigBlockFile;
5404 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5407 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5410 /******************************************************************************
5411 * SmallBlockChainStream_Shrink
5413 * Shrinks this chain in the small block depot.
5415 static BOOL SmallBlockChainStream_Shrink(
5416 SmallBlockChainStream* This,
5417 ULARGE_INTEGER newSize)
5419 ULONG blockIndex, extraBlock;
5420 ULONG numBlocks;
5421 ULONG count = 0;
5423 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5425 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5426 numBlocks++;
5428 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5431 * Go to the new end of chain
5433 while (count < numBlocks)
5435 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5436 &blockIndex)))
5437 return FALSE;
5438 count++;
5442 * If the count is 0, we have a special case, the head of the chain was
5443 * just freed.
5445 if (count == 0)
5447 StgProperty chainProp;
5449 StorageImpl_ReadProperty(This->parentStorage,
5450 This->ownerPropertyIndex,
5451 &chainProp);
5453 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5455 StorageImpl_WriteProperty(This->parentStorage,
5456 This->ownerPropertyIndex,
5457 &chainProp);
5460 * We start freeing the chain at the head block.
5462 extraBlock = blockIndex;
5464 else
5466 /* Get the next block before marking the new end */
5467 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5468 &extraBlock)))
5469 return FALSE;
5471 /* Mark the new end of chain */
5472 SmallBlockChainStream_SetNextBlockInChain(
5473 This,
5474 blockIndex,
5475 BLOCK_END_OF_CHAIN);
5479 * Mark the extra blocks as free
5481 while (extraBlock != BLOCK_END_OF_CHAIN)
5483 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5484 &blockIndex)))
5485 return FALSE;
5486 SmallBlockChainStream_FreeBlock(This, extraBlock);
5487 extraBlock = blockIndex;
5490 return TRUE;
5493 /******************************************************************************
5494 * SmallBlockChainStream_Enlarge
5496 * Grows this chain in the small block depot.
5498 static BOOL SmallBlockChainStream_Enlarge(
5499 SmallBlockChainStream* This,
5500 ULARGE_INTEGER newSize)
5502 ULONG blockIndex, currentBlock;
5503 ULONG newNumBlocks;
5504 ULONG oldNumBlocks = 0;
5506 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5509 * Empty chain
5511 if (blockIndex == BLOCK_END_OF_CHAIN)
5514 StgProperty chainProp;
5516 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5517 &chainProp);
5519 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5521 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5522 &chainProp);
5524 blockIndex = chainProp.startingBlock;
5525 SmallBlockChainStream_SetNextBlockInChain(
5526 This,
5527 blockIndex,
5528 BLOCK_END_OF_CHAIN);
5531 currentBlock = blockIndex;
5534 * Figure out how many blocks are needed to contain this stream
5536 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5538 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5539 newNumBlocks++;
5542 * Go to the current end of chain
5544 while (blockIndex != BLOCK_END_OF_CHAIN)
5546 oldNumBlocks++;
5547 currentBlock = blockIndex;
5548 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5549 return FALSE;
5553 * Add new blocks to the chain
5555 while (oldNumBlocks < newNumBlocks)
5557 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5558 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5560 SmallBlockChainStream_SetNextBlockInChain(
5561 This,
5562 blockIndex,
5563 BLOCK_END_OF_CHAIN);
5565 currentBlock = blockIndex;
5566 oldNumBlocks++;
5569 return TRUE;
5572 /******************************************************************************
5573 * SmallBlockChainStream_SetSize
5575 * Sets the size of this stream.
5576 * The file will grow if we grow the chain.
5578 * TODO: Free the actual blocks in the file when we shrink the chain.
5579 * Currently, the blocks are still in the file. So the file size
5580 * doesn't shrink even if we shrink streams.
5582 BOOL SmallBlockChainStream_SetSize(
5583 SmallBlockChainStream* This,
5584 ULARGE_INTEGER newSize)
5586 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5588 if (newSize.u.LowPart == size.u.LowPart)
5589 return TRUE;
5591 if (newSize.u.LowPart < size.u.LowPart)
5593 SmallBlockChainStream_Shrink(This, newSize);
5595 else
5597 SmallBlockChainStream_Enlarge(This, newSize);
5600 return TRUE;
5603 /******************************************************************************
5604 * SmallBlockChainStream_GetSize
5606 * Returns the size of this chain.
5608 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5610 StgProperty chainProperty;
5612 StorageImpl_ReadProperty(
5613 This->parentStorage,
5614 This->ownerPropertyIndex,
5615 &chainProperty);
5617 return chainProperty.size;
5620 /******************************************************************************
5621 * StgCreateDocfile [OLE32.@]
5622 * Creates a new compound file storage object
5624 * PARAMS
5625 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5626 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5627 * reserved [ ?] unused?, usually 0
5628 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5630 * RETURNS
5631 * S_OK if the file was successfully created
5632 * some STG_E_ value if error
5633 * NOTES
5634 * if pwcsName is NULL, create file with new unique name
5635 * the function can returns
5636 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5637 * (unrealized now)
5639 HRESULT WINAPI StgCreateDocfile(
5640 LPCOLESTR pwcsName,
5641 DWORD grfMode,
5642 DWORD reserved,
5643 IStorage **ppstgOpen)
5645 StorageImpl* newStorage = 0;
5646 HANDLE hFile = INVALID_HANDLE_VALUE;
5647 HRESULT hr = STG_E_INVALIDFLAG;
5648 DWORD shareMode;
5649 DWORD accessMode;
5650 DWORD creationMode;
5651 DWORD fileAttributes;
5652 WCHAR tempFileName[MAX_PATH];
5654 TRACE("(%s, %x, %d, %p)\n",
5655 debugstr_w(pwcsName), grfMode,
5656 reserved, ppstgOpen);
5659 * Validate the parameters
5661 if (ppstgOpen == 0)
5662 return STG_E_INVALIDPOINTER;
5663 if (reserved != 0)
5664 return STG_E_INVALIDPARAMETER;
5666 /* if no share mode given then DENY_NONE is the default */
5667 if (STGM_SHARE_MODE(grfMode) == 0)
5668 grfMode |= STGM_SHARE_DENY_NONE;
5671 * Validate the STGM flags
5673 if ( FAILED( validateSTGM(grfMode) ))
5674 goto end;
5676 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5677 switch(STGM_ACCESS_MODE(grfMode))
5679 case STGM_WRITE:
5680 case STGM_READWRITE:
5681 break;
5682 default:
5683 goto end;
5686 /* in direct mode, can only use SHARE_EXCLUSIVE */
5687 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5688 goto end;
5690 /* but in transacted mode, any share mode is valid */
5693 * Generate a unique name.
5695 if (pwcsName == 0)
5697 WCHAR tempPath[MAX_PATH];
5698 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5700 memset(tempPath, 0, sizeof(tempPath));
5701 memset(tempFileName, 0, sizeof(tempFileName));
5703 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5704 tempPath[0] = '.';
5706 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5707 pwcsName = tempFileName;
5708 else
5710 hr = STG_E_INSUFFICIENTMEMORY;
5711 goto end;
5714 creationMode = TRUNCATE_EXISTING;
5716 else
5718 creationMode = GetCreationModeFromSTGM(grfMode);
5722 * Interpret the STGM value grfMode
5724 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5725 accessMode = GetAccessModeFromSTGM(grfMode);
5727 if (grfMode & STGM_DELETEONRELEASE)
5728 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5729 else
5730 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5732 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5733 FIXME("Storage share mode not implemented.\n");
5735 if (grfMode & STGM_TRANSACTED)
5736 FIXME("Transacted mode not implemented.\n");
5739 * Initialize the "out" parameter.
5741 *ppstgOpen = 0;
5743 hFile = CreateFileW(pwcsName,
5744 accessMode,
5745 shareMode,
5746 NULL,
5747 creationMode,
5748 fileAttributes,
5751 if (hFile == INVALID_HANDLE_VALUE)
5753 if(GetLastError() == ERROR_FILE_EXISTS)
5754 hr = STG_E_FILEALREADYEXISTS;
5755 else
5756 hr = E_FAIL;
5757 goto end;
5761 * Allocate and initialize the new IStorage32object.
5763 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5765 if (newStorage == 0)
5767 hr = STG_E_INSUFFICIENTMEMORY;
5768 goto end;
5771 hr = StorageImpl_Construct(
5772 newStorage,
5773 hFile,
5774 pwcsName,
5775 NULL,
5776 grfMode,
5777 TRUE,
5778 TRUE);
5780 if (FAILED(hr))
5782 HeapFree(GetProcessHeap(), 0, newStorage);
5783 goto end;
5787 * Get an "out" pointer for the caller.
5789 hr = StorageBaseImpl_QueryInterface(
5790 (IStorage*)newStorage,
5791 (REFIID)&IID_IStorage,
5792 (void**)ppstgOpen);
5793 end:
5794 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5796 return hr;
5799 /******************************************************************************
5800 * StgCreateStorageEx [OLE32.@]
5802 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5804 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5805 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5807 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5809 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5810 return STG_E_INVALIDPARAMETER;
5813 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5815 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5816 return STG_E_INVALIDPARAMETER;
5819 if (stgfmt == STGFMT_FILE)
5821 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5822 return STG_E_INVALIDPARAMETER;
5825 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5827 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5828 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5831 ERR("Invalid stgfmt argument\n");
5832 return STG_E_INVALIDPARAMETER;
5835 /******************************************************************************
5836 * StgCreatePropSetStg [OLE32.@]
5838 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5839 IPropertySetStorage **ppPropSetStg)
5841 HRESULT hr;
5843 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5844 if (reserved)
5845 hr = STG_E_INVALIDPARAMETER;
5846 else
5847 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5848 (void**)ppPropSetStg);
5849 return hr;
5852 /******************************************************************************
5853 * StgOpenStorageEx [OLE32.@]
5855 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5857 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5858 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5860 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5862 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5863 return STG_E_INVALIDPARAMETER;
5866 switch (stgfmt)
5868 case STGFMT_FILE:
5869 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5870 return STG_E_INVALIDPARAMETER;
5872 case STGFMT_STORAGE:
5873 break;
5875 case STGFMT_DOCFILE:
5876 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5878 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5879 return STG_E_INVALIDPARAMETER;
5881 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5882 break;
5884 case STGFMT_ANY:
5885 WARN("STGFMT_ANY assuming storage\n");
5886 break;
5888 default:
5889 return STG_E_INVALIDPARAMETER;
5892 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5896 /******************************************************************************
5897 * StgOpenStorage [OLE32.@]
5899 HRESULT WINAPI StgOpenStorage(
5900 const OLECHAR *pwcsName,
5901 IStorage *pstgPriority,
5902 DWORD grfMode,
5903 SNB snbExclude,
5904 DWORD reserved,
5905 IStorage **ppstgOpen)
5907 StorageImpl* newStorage = 0;
5908 HRESULT hr = S_OK;
5909 HANDLE hFile = 0;
5910 DWORD shareMode;
5911 DWORD accessMode;
5912 WCHAR fullname[MAX_PATH];
5914 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5915 debugstr_w(pwcsName), pstgPriority, grfMode,
5916 snbExclude, reserved, ppstgOpen);
5919 * Perform sanity checks
5921 if (pwcsName == 0)
5923 hr = STG_E_INVALIDNAME;
5924 goto end;
5927 if (ppstgOpen == 0)
5929 hr = STG_E_INVALIDPOINTER;
5930 goto end;
5933 if (reserved)
5935 hr = STG_E_INVALIDPARAMETER;
5936 goto end;
5939 if (grfMode & STGM_PRIORITY)
5941 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5942 return STG_E_INVALIDFLAG;
5943 if (grfMode & STGM_DELETEONRELEASE)
5944 return STG_E_INVALIDFUNCTION;
5945 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5946 return STG_E_INVALIDFLAG;
5947 grfMode &= ~0xf0; /* remove the existing sharing mode */
5948 grfMode |= STGM_SHARE_DENY_NONE;
5950 /* STGM_PRIORITY stops other IStorage objects on the same file from
5951 * committing until the STGM_PRIORITY IStorage is closed. it also
5952 * stops non-transacted mode StgOpenStorage calls with write access from
5953 * succeeding. obviously, both of these cannot be achieved through just
5954 * file share flags */
5955 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5959 * Validate the sharing mode
5961 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5962 switch(STGM_SHARE_MODE(grfMode))
5964 case STGM_SHARE_EXCLUSIVE:
5965 case STGM_SHARE_DENY_WRITE:
5966 break;
5967 default:
5968 hr = STG_E_INVALIDFLAG;
5969 goto end;
5973 * Validate the STGM flags
5975 if ( FAILED( validateSTGM(grfMode) ) ||
5976 (grfMode&STGM_CREATE))
5978 hr = STG_E_INVALIDFLAG;
5979 goto end;
5982 /* shared reading requires transacted mode */
5983 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5984 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5985 !(grfMode&STGM_TRANSACTED) )
5987 hr = STG_E_INVALIDFLAG;
5988 goto end;
5992 * Interpret the STGM value grfMode
5994 shareMode = GetShareModeFromSTGM(grfMode);
5995 accessMode = GetAccessModeFromSTGM(grfMode);
5998 * Initialize the "out" parameter.
6000 *ppstgOpen = 0;
6002 hFile = CreateFileW( pwcsName,
6003 accessMode,
6004 shareMode,
6005 NULL,
6006 OPEN_EXISTING,
6007 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6010 if (hFile==INVALID_HANDLE_VALUE)
6012 DWORD last_error = GetLastError();
6014 hr = E_FAIL;
6016 switch (last_error)
6018 case ERROR_FILE_NOT_FOUND:
6019 hr = STG_E_FILENOTFOUND;
6020 break;
6022 case ERROR_PATH_NOT_FOUND:
6023 hr = STG_E_PATHNOTFOUND;
6024 break;
6026 case ERROR_ACCESS_DENIED:
6027 case ERROR_WRITE_PROTECT:
6028 hr = STG_E_ACCESSDENIED;
6029 break;
6031 case ERROR_SHARING_VIOLATION:
6032 hr = STG_E_SHAREVIOLATION;
6033 break;
6035 default:
6036 hr = E_FAIL;
6039 goto end;
6043 * Refuse to open the file if it's too small to be a structured storage file
6044 * FIXME: verify the file when reading instead of here
6046 if (GetFileSize(hFile, NULL) < 0x100)
6048 CloseHandle(hFile);
6049 hr = STG_E_FILEALREADYEXISTS;
6050 goto end;
6054 * Allocate and initialize the new IStorage32object.
6056 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6058 if (newStorage == 0)
6060 hr = STG_E_INSUFFICIENTMEMORY;
6061 goto end;
6064 /* Initialize the storage */
6065 hr = StorageImpl_Construct(
6066 newStorage,
6067 hFile,
6068 pwcsName,
6069 NULL,
6070 grfMode,
6071 TRUE,
6072 FALSE );
6074 if (FAILED(hr))
6076 HeapFree(GetProcessHeap(), 0, newStorage);
6078 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6080 if(hr == STG_E_INVALIDHEADER)
6081 hr = STG_E_FILEALREADYEXISTS;
6082 goto end;
6085 /* prepare the file name string given in lieu of the root property name */
6086 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6087 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6088 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6091 * Get an "out" pointer for the caller.
6093 hr = StorageBaseImpl_QueryInterface(
6094 (IStorage*)newStorage,
6095 (REFIID)&IID_IStorage,
6096 (void**)ppstgOpen);
6098 end:
6099 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6100 return hr;
6103 /******************************************************************************
6104 * StgCreateDocfileOnILockBytes [OLE32.@]
6106 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6107 ILockBytes *plkbyt,
6108 DWORD grfMode,
6109 DWORD reserved,
6110 IStorage** ppstgOpen)
6112 StorageImpl* newStorage = 0;
6113 HRESULT hr = S_OK;
6116 * Validate the parameters
6118 if ((ppstgOpen == 0) || (plkbyt == 0))
6119 return STG_E_INVALIDPOINTER;
6122 * Allocate and initialize the new IStorage object.
6124 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6126 if (newStorage == 0)
6127 return STG_E_INSUFFICIENTMEMORY;
6129 hr = StorageImpl_Construct(
6130 newStorage,
6133 plkbyt,
6134 grfMode,
6135 FALSE,
6136 TRUE);
6138 if (FAILED(hr))
6140 HeapFree(GetProcessHeap(), 0, newStorage);
6141 return hr;
6145 * Get an "out" pointer for the caller.
6147 hr = StorageBaseImpl_QueryInterface(
6148 (IStorage*)newStorage,
6149 (REFIID)&IID_IStorage,
6150 (void**)ppstgOpen);
6152 return hr;
6155 /******************************************************************************
6156 * StgOpenStorageOnILockBytes [OLE32.@]
6158 HRESULT WINAPI StgOpenStorageOnILockBytes(
6159 ILockBytes *plkbyt,
6160 IStorage *pstgPriority,
6161 DWORD grfMode,
6162 SNB snbExclude,
6163 DWORD reserved,
6164 IStorage **ppstgOpen)
6166 StorageImpl* newStorage = 0;
6167 HRESULT hr = S_OK;
6170 * Perform a sanity check
6172 if ((plkbyt == 0) || (ppstgOpen == 0))
6173 return STG_E_INVALIDPOINTER;
6176 * Validate the STGM flags
6178 if ( FAILED( validateSTGM(grfMode) ))
6179 return STG_E_INVALIDFLAG;
6182 * Initialize the "out" parameter.
6184 *ppstgOpen = 0;
6187 * Allocate and initialize the new IStorage object.
6189 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6191 if (newStorage == 0)
6192 return STG_E_INSUFFICIENTMEMORY;
6194 hr = StorageImpl_Construct(
6195 newStorage,
6198 plkbyt,
6199 grfMode,
6200 FALSE,
6201 FALSE);
6203 if (FAILED(hr))
6205 HeapFree(GetProcessHeap(), 0, newStorage);
6206 return hr;
6210 * Get an "out" pointer for the caller.
6212 hr = StorageBaseImpl_QueryInterface(
6213 (IStorage*)newStorage,
6214 (REFIID)&IID_IStorage,
6215 (void**)ppstgOpen);
6217 return hr;
6220 /******************************************************************************
6221 * StgSetTimes [ole32.@]
6222 * StgSetTimes [OLE32.@]
6226 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6227 FILETIME const *patime, FILETIME const *pmtime)
6229 IStorage *stg = NULL;
6230 HRESULT r;
6232 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6234 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6235 0, 0, &stg);
6236 if( SUCCEEDED(r) )
6238 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6239 IStorage_Release(stg);
6242 return r;
6245 /******************************************************************************
6246 * StgIsStorageILockBytes [OLE32.@]
6248 * Determines if the ILockBytes contains a storage object.
6250 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6252 BYTE sig[8];
6253 ULARGE_INTEGER offset;
6255 offset.u.HighPart = 0;
6256 offset.u.LowPart = 0;
6258 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6260 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6261 return S_OK;
6263 return S_FALSE;
6266 /******************************************************************************
6267 * WriteClassStg [OLE32.@]
6269 * This method will store the specified CLSID in the specified storage object
6271 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6273 HRESULT hRes;
6275 if(!pStg)
6276 return E_INVALIDARG;
6278 if(!rclsid)
6279 return STG_E_INVALIDPOINTER;
6281 hRes = IStorage_SetClass(pStg, rclsid);
6283 return hRes;
6286 /***********************************************************************
6287 * ReadClassStg (OLE32.@)
6289 * This method reads the CLSID previously written to a storage object with
6290 * the WriteClassStg.
6292 * PARAMS
6293 * pstg [I] IStorage pointer
6294 * pclsid [O] Pointer to where the CLSID is written
6296 * RETURNS
6297 * Success: S_OK.
6298 * Failure: HRESULT code.
6300 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6302 STATSTG pstatstg;
6303 HRESULT hRes;
6305 TRACE("(%p, %p)\n", pstg, pclsid);
6307 if(!pstg || !pclsid)
6308 return E_INVALIDARG;
6311 * read a STATSTG structure (contains the clsid) from the storage
6313 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6315 if(SUCCEEDED(hRes))
6316 *pclsid=pstatstg.clsid;
6318 return hRes;
6321 /***********************************************************************
6322 * OleLoadFromStream (OLE32.@)
6324 * This function loads an object from stream
6326 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6328 CLSID clsid;
6329 HRESULT res;
6330 LPPERSISTSTREAM xstm;
6332 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6334 res=ReadClassStm(pStm,&clsid);
6335 if (FAILED(res))
6336 return res;
6337 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6338 if (FAILED(res))
6339 return res;
6340 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6341 if (FAILED(res)) {
6342 IUnknown_Release((IUnknown*)*ppvObj);
6343 return res;
6345 res=IPersistStream_Load(xstm,pStm);
6346 IPersistStream_Release(xstm);
6347 /* FIXME: all refcounts ok at this point? I think they should be:
6348 * pStm : unchanged
6349 * ppvObj : 1
6350 * xstm : 0 (released)
6352 return res;
6355 /***********************************************************************
6356 * OleSaveToStream (OLE32.@)
6358 * This function saves an object with the IPersistStream interface on it
6359 * to the specified stream.
6361 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6364 CLSID clsid;
6365 HRESULT res;
6367 TRACE("(%p,%p)\n",pPStm,pStm);
6369 res=IPersistStream_GetClassID(pPStm,&clsid);
6371 if (SUCCEEDED(res)){
6373 res=WriteClassStm(pStm,&clsid);
6375 if (SUCCEEDED(res))
6377 res=IPersistStream_Save(pPStm,pStm,TRUE);
6380 TRACE("Finished Save\n");
6381 return res;
6384 /****************************************************************************
6385 * This method validate a STGM parameter that can contain the values below
6387 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6388 * The stgm values contained in 0xffff0000 are bitmasks.
6390 * STGM_DIRECT 0x00000000
6391 * STGM_TRANSACTED 0x00010000
6392 * STGM_SIMPLE 0x08000000
6394 * STGM_READ 0x00000000
6395 * STGM_WRITE 0x00000001
6396 * STGM_READWRITE 0x00000002
6398 * STGM_SHARE_DENY_NONE 0x00000040
6399 * STGM_SHARE_DENY_READ 0x00000030
6400 * STGM_SHARE_DENY_WRITE 0x00000020
6401 * STGM_SHARE_EXCLUSIVE 0x00000010
6403 * STGM_PRIORITY 0x00040000
6404 * STGM_DELETEONRELEASE 0x04000000
6406 * STGM_CREATE 0x00001000
6407 * STGM_CONVERT 0x00020000
6408 * STGM_FAILIFTHERE 0x00000000
6410 * STGM_NOSCRATCH 0x00100000
6411 * STGM_NOSNAPSHOT 0x00200000
6413 static HRESULT validateSTGM(DWORD stgm)
6415 DWORD access = STGM_ACCESS_MODE(stgm);
6416 DWORD share = STGM_SHARE_MODE(stgm);
6417 DWORD create = STGM_CREATE_MODE(stgm);
6419 if (stgm&~STGM_KNOWN_FLAGS)
6421 ERR("unknown flags %08x\n", stgm);
6422 return E_FAIL;
6425 switch (access)
6427 case STGM_READ:
6428 case STGM_WRITE:
6429 case STGM_READWRITE:
6430 break;
6431 default:
6432 return E_FAIL;
6435 switch (share)
6437 case STGM_SHARE_DENY_NONE:
6438 case STGM_SHARE_DENY_READ:
6439 case STGM_SHARE_DENY_WRITE:
6440 case STGM_SHARE_EXCLUSIVE:
6441 break;
6442 default:
6443 return E_FAIL;
6446 switch (create)
6448 case STGM_CREATE:
6449 case STGM_FAILIFTHERE:
6450 break;
6451 default:
6452 return E_FAIL;
6456 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6458 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6459 return E_FAIL;
6462 * STGM_CREATE | STGM_CONVERT
6463 * if both are false, STGM_FAILIFTHERE is set to TRUE
6465 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6466 return E_FAIL;
6469 * STGM_NOSCRATCH requires STGM_TRANSACTED
6471 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6472 return E_FAIL;
6475 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6476 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6478 if ( (stgm & STGM_NOSNAPSHOT) &&
6479 (!(stgm & STGM_TRANSACTED) ||
6480 share == STGM_SHARE_EXCLUSIVE ||
6481 share == STGM_SHARE_DENY_WRITE) )
6482 return E_FAIL;
6484 return S_OK;
6487 /****************************************************************************
6488 * GetShareModeFromSTGM
6490 * This method will return a share mode flag from a STGM value.
6491 * The STGM value is assumed valid.
6493 static DWORD GetShareModeFromSTGM(DWORD stgm)
6495 switch (STGM_SHARE_MODE(stgm))
6497 case STGM_SHARE_DENY_NONE:
6498 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6499 case STGM_SHARE_DENY_READ:
6500 return FILE_SHARE_WRITE;
6501 case STGM_SHARE_DENY_WRITE:
6502 return FILE_SHARE_READ;
6503 case STGM_SHARE_EXCLUSIVE:
6504 return 0;
6506 ERR("Invalid share mode!\n");
6507 assert(0);
6508 return 0;
6511 /****************************************************************************
6512 * GetAccessModeFromSTGM
6514 * This method will return an access mode flag from a STGM value.
6515 * The STGM value is assumed valid.
6517 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6519 switch (STGM_ACCESS_MODE(stgm))
6521 case STGM_READ:
6522 return GENERIC_READ;
6523 case STGM_WRITE:
6524 case STGM_READWRITE:
6525 return GENERIC_READ | GENERIC_WRITE;
6527 ERR("Invalid access mode!\n");
6528 assert(0);
6529 return 0;
6532 /****************************************************************************
6533 * GetCreationModeFromSTGM
6535 * This method will return a creation mode flag from a STGM value.
6536 * The STGM value is assumed valid.
6538 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6540 switch(STGM_CREATE_MODE(stgm))
6542 case STGM_CREATE:
6543 return CREATE_ALWAYS;
6544 case STGM_CONVERT:
6545 FIXME("STGM_CONVERT not implemented!\n");
6546 return CREATE_NEW;
6547 case STGM_FAILIFTHERE:
6548 return CREATE_NEW;
6550 ERR("Invalid create mode!\n");
6551 assert(0);
6552 return 0;
6556 /*************************************************************************
6557 * OLECONVERT_LoadOLE10 [Internal]
6559 * Loads the OLE10 STREAM to memory
6561 * PARAMS
6562 * pOleStream [I] The OLESTREAM
6563 * pData [I] Data Structure for the OLESTREAM Data
6565 * RETURNS
6566 * Success: S_OK
6567 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6568 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6570 * NOTES
6571 * This function is used by OleConvertOLESTREAMToIStorage only.
6573 * Memory allocated for pData must be freed by the caller
6575 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6577 DWORD dwSize;
6578 HRESULT hRes = S_OK;
6579 int nTryCnt=0;
6580 int max_try = 6;
6582 pData->pData = NULL;
6583 pData->pstrOleObjFileName = NULL;
6585 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6587 /* Get the OleID */
6588 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6589 if(dwSize != sizeof(pData->dwOleID))
6591 hRes = CONVERT10_E_OLESTREAM_GET;
6593 else if(pData->dwOleID != OLESTREAM_ID)
6595 hRes = CONVERT10_E_OLESTREAM_FMT;
6597 else
6599 hRes = S_OK;
6600 break;
6604 if(hRes == S_OK)
6606 /* Get the TypeID... more info needed for this field */
6607 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6608 if(dwSize != sizeof(pData->dwTypeID))
6610 hRes = CONVERT10_E_OLESTREAM_GET;
6613 if(hRes == S_OK)
6615 if(pData->dwTypeID != 0)
6617 /* Get the length of the OleTypeName */
6618 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6619 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6621 hRes = CONVERT10_E_OLESTREAM_GET;
6624 if(hRes == S_OK)
6626 if(pData->dwOleTypeNameLength > 0)
6628 /* Get the OleTypeName */
6629 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6630 if(dwSize != pData->dwOleTypeNameLength)
6632 hRes = CONVERT10_E_OLESTREAM_GET;
6636 if(bStrem1)
6638 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6639 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6641 hRes = CONVERT10_E_OLESTREAM_GET;
6643 if(hRes == S_OK)
6645 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6646 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6647 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6648 if(pData->pstrOleObjFileName)
6650 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6651 if(dwSize != pData->dwOleObjFileNameLength)
6653 hRes = CONVERT10_E_OLESTREAM_GET;
6656 else
6657 hRes = CONVERT10_E_OLESTREAM_GET;
6660 else
6662 /* Get the Width of the Metafile */
6663 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6664 if(dwSize != sizeof(pData->dwMetaFileWidth))
6666 hRes = CONVERT10_E_OLESTREAM_GET;
6668 if(hRes == S_OK)
6670 /* Get the Height of the Metafile */
6671 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6672 if(dwSize != sizeof(pData->dwMetaFileHeight))
6674 hRes = CONVERT10_E_OLESTREAM_GET;
6678 if(hRes == S_OK)
6680 /* Get the Length of the Data */
6681 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6682 if(dwSize != sizeof(pData->dwDataLength))
6684 hRes = CONVERT10_E_OLESTREAM_GET;
6688 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6690 if(!bStrem1) /* if it is a second OLE stream data */
6692 pData->dwDataLength -= 8;
6693 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6694 if(dwSize != sizeof(pData->strUnknown))
6696 hRes = CONVERT10_E_OLESTREAM_GET;
6700 if(hRes == S_OK)
6702 if(pData->dwDataLength > 0)
6704 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6706 /* Get Data (ex. IStorage, Metafile, or BMP) */
6707 if(pData->pData)
6709 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6710 if(dwSize != pData->dwDataLength)
6712 hRes = CONVERT10_E_OLESTREAM_GET;
6715 else
6717 hRes = CONVERT10_E_OLESTREAM_GET;
6723 return hRes;
6726 /*************************************************************************
6727 * OLECONVERT_SaveOLE10 [Internal]
6729 * Saves the OLE10 STREAM From memory
6731 * PARAMS
6732 * pData [I] Data Structure for the OLESTREAM Data
6733 * pOleStream [I] The OLESTREAM to save
6735 * RETURNS
6736 * Success: S_OK
6737 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6739 * NOTES
6740 * This function is used by OleConvertIStorageToOLESTREAM only.
6743 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6745 DWORD dwSize;
6746 HRESULT hRes = S_OK;
6749 /* Set the OleID */
6750 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6751 if(dwSize != sizeof(pData->dwOleID))
6753 hRes = CONVERT10_E_OLESTREAM_PUT;
6756 if(hRes == S_OK)
6758 /* Set the TypeID */
6759 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6760 if(dwSize != sizeof(pData->dwTypeID))
6762 hRes = CONVERT10_E_OLESTREAM_PUT;
6766 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6768 /* Set the Length of the OleTypeName */
6769 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6770 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6772 hRes = CONVERT10_E_OLESTREAM_PUT;
6775 if(hRes == S_OK)
6777 if(pData->dwOleTypeNameLength > 0)
6779 /* Set the OleTypeName */
6780 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6781 if(dwSize != pData->dwOleTypeNameLength)
6783 hRes = CONVERT10_E_OLESTREAM_PUT;
6788 if(hRes == S_OK)
6790 /* Set the width of the Metafile */
6791 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6792 if(dwSize != sizeof(pData->dwMetaFileWidth))
6794 hRes = CONVERT10_E_OLESTREAM_PUT;
6798 if(hRes == S_OK)
6800 /* Set the height of the Metafile */
6801 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6802 if(dwSize != sizeof(pData->dwMetaFileHeight))
6804 hRes = CONVERT10_E_OLESTREAM_PUT;
6808 if(hRes == S_OK)
6810 /* Set the length of the Data */
6811 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6812 if(dwSize != sizeof(pData->dwDataLength))
6814 hRes = CONVERT10_E_OLESTREAM_PUT;
6818 if(hRes == S_OK)
6820 if(pData->dwDataLength > 0)
6822 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6823 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6824 if(dwSize != pData->dwDataLength)
6826 hRes = CONVERT10_E_OLESTREAM_PUT;
6831 return hRes;
6834 /*************************************************************************
6835 * OLECONVERT_GetOLE20FromOLE10[Internal]
6837 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6838 * opens it, and copies the content to the dest IStorage for
6839 * OleConvertOLESTREAMToIStorage
6842 * PARAMS
6843 * pDestStorage [I] The IStorage to copy the data to
6844 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6845 * nBufferLength [I] The size of the buffer
6847 * RETURNS
6848 * Nothing
6850 * NOTES
6854 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6856 HRESULT hRes;
6857 HANDLE hFile;
6858 IStorage *pTempStorage;
6859 DWORD dwNumOfBytesWritten;
6860 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6861 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6863 /* Create a temp File */
6864 GetTempPathW(MAX_PATH, wstrTempDir);
6865 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6866 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6868 if(hFile != INVALID_HANDLE_VALUE)
6870 /* Write IStorage Data to File */
6871 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6872 CloseHandle(hFile);
6874 /* Open and copy temp storage to the Dest Storage */
6875 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6876 if(hRes == S_OK)
6878 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6879 StorageBaseImpl_Release(pTempStorage);
6881 DeleteFileW(wstrTempFile);
6886 /*************************************************************************
6887 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6889 * Saves the OLE10 STREAM From memory
6891 * PARAMS
6892 * pStorage [I] The Src IStorage to copy
6893 * pData [I] The Dest Memory to write to.
6895 * RETURNS
6896 * The size in bytes allocated for pData
6898 * NOTES
6899 * Memory allocated for pData must be freed by the caller
6901 * Used by OleConvertIStorageToOLESTREAM only.
6904 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6906 HANDLE hFile;
6907 HRESULT hRes;
6908 DWORD nDataLength = 0;
6909 IStorage *pTempStorage;
6910 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6911 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6913 *pData = NULL;
6915 /* Create temp Storage */
6916 GetTempPathW(MAX_PATH, wstrTempDir);
6917 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6918 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6920 if(hRes == S_OK)
6922 /* Copy Src Storage to the Temp Storage */
6923 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6924 StorageBaseImpl_Release(pTempStorage);
6926 /* Open Temp Storage as a file and copy to memory */
6927 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6928 if(hFile != INVALID_HANDLE_VALUE)
6930 nDataLength = GetFileSize(hFile, NULL);
6931 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6932 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6933 CloseHandle(hFile);
6935 DeleteFileW(wstrTempFile);
6937 return nDataLength;
6940 /*************************************************************************
6941 * OLECONVERT_CreateOleStream [Internal]
6943 * Creates the "\001OLE" stream in the IStorage if necessary.
6945 * PARAMS
6946 * pStorage [I] Dest storage to create the stream in
6948 * RETURNS
6949 * Nothing
6951 * NOTES
6952 * This function is used by OleConvertOLESTREAMToIStorage only.
6954 * This stream is still unknown, MS Word seems to have extra data
6955 * but since the data is stored in the OLESTREAM there should be
6956 * no need to recreate the stream. If the stream is manually
6957 * deleted it will create it with this default data.
6960 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6962 HRESULT hRes;
6963 IStream *pStream;
6964 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6965 BYTE pOleStreamHeader [] =
6967 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6968 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6969 0x00, 0x00, 0x00, 0x00
6972 /* Create stream if not present */
6973 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6974 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6976 if(hRes == S_OK)
6978 /* Write default Data */
6979 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6980 IStream_Release(pStream);
6984 /* write a string to a stream, preceded by its length */
6985 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6987 HRESULT r;
6988 LPSTR str;
6989 DWORD len = 0;
6991 if( string )
6992 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6993 r = IStream_Write( stm, &len, sizeof(len), NULL);
6994 if( FAILED( r ) )
6995 return r;
6996 if(len == 0)
6997 return r;
6998 str = CoTaskMemAlloc( len );
6999 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7000 r = IStream_Write( stm, str, len, NULL);
7001 CoTaskMemFree( str );
7002 return r;
7005 /* read a string preceded by its length from a stream */
7006 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7008 HRESULT r;
7009 DWORD len, count = 0;
7010 LPSTR str;
7011 LPWSTR wstr;
7013 r = IStream_Read( stm, &len, sizeof(len), &count );
7014 if( FAILED( r ) )
7015 return r;
7016 if( count != sizeof(len) )
7017 return E_OUTOFMEMORY;
7019 TRACE("%d bytes\n",len);
7021 str = CoTaskMemAlloc( len );
7022 if( !str )
7023 return E_OUTOFMEMORY;
7024 count = 0;
7025 r = IStream_Read( stm, str, len, &count );
7026 if( FAILED( r ) )
7027 return r;
7028 if( count != len )
7030 CoTaskMemFree( str );
7031 return E_OUTOFMEMORY;
7034 TRACE("Read string %s\n",debugstr_an(str,len));
7036 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7037 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7038 if( wstr )
7039 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7040 CoTaskMemFree( str );
7042 *string = wstr;
7044 return r;
7048 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7049 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7051 IStream *pstm;
7052 HRESULT r = S_OK;
7053 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7055 static const BYTE unknown1[12] =
7056 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7057 0xFF, 0xFF, 0xFF, 0xFF};
7058 static const BYTE unknown2[16] =
7059 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7060 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7062 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7063 debugstr_w(lpszUserType), debugstr_w(szClipName),
7064 debugstr_w(szProgIDName));
7066 /* Create a CompObj stream if it doesn't exist */
7067 r = IStorage_CreateStream(pstg, szwStreamName,
7068 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7069 if( FAILED (r) )
7070 return r;
7072 /* Write CompObj Structure to stream */
7073 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7075 if( SUCCEEDED( r ) )
7076 r = WriteClassStm( pstm, clsid );
7078 if( SUCCEEDED( r ) )
7079 r = STREAM_WriteString( pstm, lpszUserType );
7080 if( SUCCEEDED( r ) )
7081 r = STREAM_WriteString( pstm, szClipName );
7082 if( SUCCEEDED( r ) )
7083 r = STREAM_WriteString( pstm, szProgIDName );
7084 if( SUCCEEDED( r ) )
7085 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7087 IStream_Release( pstm );
7089 return r;
7092 /***********************************************************************
7093 * WriteFmtUserTypeStg (OLE32.@)
7095 HRESULT WINAPI WriteFmtUserTypeStg(
7096 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7098 HRESULT r;
7099 WCHAR szwClipName[0x40];
7100 CLSID clsid = CLSID_NULL;
7101 LPWSTR wstrProgID = NULL;
7102 DWORD n;
7104 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7106 /* get the clipboard format name */
7107 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
7108 szwClipName[n]=0;
7110 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7112 /* FIXME: There's room to save a CLSID and its ProgID, but
7113 the CLSID is not looked up in the registry and in all the
7114 tests I wrote it was CLSID_NULL. Where does it come from?
7117 /* get the real program ID. This may fail, but that's fine */
7118 ProgIDFromCLSID(&clsid, &wstrProgID);
7120 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7122 r = STORAGE_WriteCompObj( pstg, &clsid,
7123 lpszUserType, szwClipName, wstrProgID );
7125 CoTaskMemFree(wstrProgID);
7127 return r;
7131 /******************************************************************************
7132 * ReadFmtUserTypeStg [OLE32.@]
7134 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7136 HRESULT r;
7137 IStream *stm = 0;
7138 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7139 unsigned char unknown1[12];
7140 unsigned char unknown2[16];
7141 DWORD count;
7142 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7143 CLSID clsid;
7145 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7147 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7148 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7149 if( FAILED ( r ) )
7151 WARN("Failed to open stream r = %08x\n", r);
7152 return r;
7155 /* read the various parts of the structure */
7156 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7157 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7158 goto end;
7159 r = ReadClassStm( stm, &clsid );
7160 if( FAILED( r ) )
7161 goto end;
7163 r = STREAM_ReadString( stm, &szCLSIDName );
7164 if( FAILED( r ) )
7165 goto end;
7167 r = STREAM_ReadString( stm, &szOleTypeName );
7168 if( FAILED( r ) )
7169 goto end;
7171 r = STREAM_ReadString( stm, &szProgIDName );
7172 if( FAILED( r ) )
7173 goto end;
7175 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7176 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7177 goto end;
7179 /* ok, success... now we just need to store what we found */
7180 if( pcf )
7181 *pcf = RegisterClipboardFormatW( szOleTypeName );
7182 CoTaskMemFree( szOleTypeName );
7184 if( lplpszUserType )
7185 *lplpszUserType = szCLSIDName;
7186 CoTaskMemFree( szProgIDName );
7188 end:
7189 IStream_Release( stm );
7191 return r;
7195 /*************************************************************************
7196 * OLECONVERT_CreateCompObjStream [Internal]
7198 * Creates a "\001CompObj" is the destination IStorage if necessary.
7200 * PARAMS
7201 * pStorage [I] The dest IStorage to create the CompObj Stream
7202 * if necessary.
7203 * strOleTypeName [I] The ProgID
7205 * RETURNS
7206 * Success: S_OK
7207 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7209 * NOTES
7210 * This function is used by OleConvertOLESTREAMToIStorage only.
7212 * The stream data is stored in the OLESTREAM and there should be
7213 * no need to recreate the stream. If the stream is manually
7214 * deleted it will attempt to create it by querying the registry.
7218 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7220 IStream *pStream;
7221 HRESULT hStorageRes, hRes = S_OK;
7222 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7223 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7224 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7226 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7227 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7229 /* Initialize the CompObj structure */
7230 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7231 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
7232 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
7235 /* Create a CompObj stream if it doesn't exist */
7236 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7237 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7238 if(hStorageRes == S_OK)
7240 /* copy the OleTypeName to the compobj struct */
7241 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7242 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7244 /* copy the OleTypeName to the compobj struct */
7245 /* Note: in the test made, these were Identical */
7246 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7247 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7249 /* Get the CLSID */
7250 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7251 bufferW, OLESTREAM_MAX_STR_LEN );
7252 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7254 if(hRes == S_OK)
7256 HKEY hKey;
7257 LONG hErr;
7258 /* Get the CLSID Default Name from the Registry */
7259 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7260 if(hErr == ERROR_SUCCESS)
7262 char strTemp[OLESTREAM_MAX_STR_LEN];
7263 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7264 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7265 if(hErr == ERROR_SUCCESS)
7267 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7269 RegCloseKey(hKey);
7273 /* Write CompObj Structure to stream */
7274 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7276 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7278 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7279 if(IStorageCompObj.dwCLSIDNameLength > 0)
7281 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7283 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7284 if(IStorageCompObj.dwOleTypeNameLength > 0)
7286 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7288 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7289 if(IStorageCompObj.dwProgIDNameLength > 0)
7291 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7293 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7294 IStream_Release(pStream);
7296 return hRes;
7300 /*************************************************************************
7301 * OLECONVERT_CreateOlePresStream[Internal]
7303 * Creates the "\002OlePres000" Stream with the Metafile data
7305 * PARAMS
7306 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7307 * dwExtentX [I] Width of the Metafile
7308 * dwExtentY [I] Height of the Metafile
7309 * pData [I] Metafile data
7310 * dwDataLength [I] Size of the Metafile data
7312 * RETURNS
7313 * Success: S_OK
7314 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7316 * NOTES
7317 * This function is used by OleConvertOLESTREAMToIStorage only.
7320 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7322 HRESULT hRes;
7323 IStream *pStream;
7324 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7325 BYTE pOlePresStreamHeader [] =
7327 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7328 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7329 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7330 0x00, 0x00, 0x00, 0x00
7333 BYTE pOlePresStreamHeaderEmpty [] =
7335 0x00, 0x00, 0x00, 0x00,
7336 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7337 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7338 0x00, 0x00, 0x00, 0x00
7341 /* Create the OlePres000 Stream */
7342 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7343 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7345 if(hRes == S_OK)
7347 DWORD nHeaderSize;
7348 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7350 memset(&OlePres, 0, sizeof(OlePres));
7351 /* Do we have any metafile data to save */
7352 if(dwDataLength > 0)
7354 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7355 nHeaderSize = sizeof(pOlePresStreamHeader);
7357 else
7359 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7360 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7362 /* Set width and height of the metafile */
7363 OlePres.dwExtentX = dwExtentX;
7364 OlePres.dwExtentY = -dwExtentY;
7366 /* Set Data and Length */
7367 if(dwDataLength > sizeof(METAFILEPICT16))
7369 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7370 OlePres.pData = &(pData[8]);
7372 /* Save OlePres000 Data to Stream */
7373 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7374 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7375 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7376 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7377 if(OlePres.dwSize > 0)
7379 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7381 IStream_Release(pStream);
7385 /*************************************************************************
7386 * OLECONVERT_CreateOle10NativeStream [Internal]
7388 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7390 * PARAMS
7391 * pStorage [I] Dest storage to create the stream in
7392 * pData [I] Ole10 Native Data (ex. bmp)
7393 * dwDataLength [I] Size of the Ole10 Native Data
7395 * RETURNS
7396 * Nothing
7398 * NOTES
7399 * This function is used by OleConvertOLESTREAMToIStorage only.
7401 * Might need to verify the data and return appropriate error message
7404 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7406 HRESULT hRes;
7407 IStream *pStream;
7408 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7410 /* Create the Ole10Native Stream */
7411 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7412 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7414 if(hRes == S_OK)
7416 /* Write info to stream */
7417 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7418 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7419 IStream_Release(pStream);
7424 /*************************************************************************
7425 * OLECONVERT_GetOLE10ProgID [Internal]
7427 * Finds the ProgID (or OleTypeID) from the IStorage
7429 * PARAMS
7430 * pStorage [I] The Src IStorage to get the ProgID
7431 * strProgID [I] the ProgID string to get
7432 * dwSize [I] the size of the string
7434 * RETURNS
7435 * Success: S_OK
7436 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7438 * NOTES
7439 * This function is used by OleConvertIStorageToOLESTREAM only.
7443 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7445 HRESULT hRes;
7446 IStream *pStream;
7447 LARGE_INTEGER iSeekPos;
7448 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7449 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7451 /* Open the CompObj Stream */
7452 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7453 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7454 if(hRes == S_OK)
7457 /*Get the OleType from the CompObj Stream */
7458 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7459 iSeekPos.u.HighPart = 0;
7461 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7462 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7463 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7464 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7465 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7466 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7467 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7469 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7470 if(*dwSize > 0)
7472 IStream_Read(pStream, strProgID, *dwSize, NULL);
7474 IStream_Release(pStream);
7476 else
7478 STATSTG stat;
7479 LPOLESTR wstrProgID;
7481 /* Get the OleType from the registry */
7482 REFCLSID clsid = &(stat.clsid);
7483 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7484 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7485 if(hRes == S_OK)
7487 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7491 return hRes;
7494 /*************************************************************************
7495 * OLECONVERT_GetOle10PresData [Internal]
7497 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7499 * PARAMS
7500 * pStorage [I] Src IStroage
7501 * pOleStream [I] Dest OleStream Mem Struct
7503 * RETURNS
7504 * Nothing
7506 * NOTES
7507 * This function is used by OleConvertIStorageToOLESTREAM only.
7509 * Memory allocated for pData must be freed by the caller
7513 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7516 HRESULT hRes;
7517 IStream *pStream;
7518 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7520 /* Initialize Default data for OLESTREAM */
7521 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7522 pOleStreamData[0].dwTypeID = 2;
7523 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7524 pOleStreamData[1].dwTypeID = 0;
7525 pOleStreamData[0].dwMetaFileWidth = 0;
7526 pOleStreamData[0].dwMetaFileHeight = 0;
7527 pOleStreamData[0].pData = NULL;
7528 pOleStreamData[1].pData = NULL;
7530 /* Open Ole10Native Stream */
7531 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7532 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7533 if(hRes == S_OK)
7536 /* Read Size and Data */
7537 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7538 if(pOleStreamData->dwDataLength > 0)
7540 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7541 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7543 IStream_Release(pStream);
7549 /*************************************************************************
7550 * OLECONVERT_GetOle20PresData[Internal]
7552 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7554 * PARAMS
7555 * pStorage [I] Src IStroage
7556 * pOleStreamData [I] Dest OleStream Mem Struct
7558 * RETURNS
7559 * Nothing
7561 * NOTES
7562 * This function is used by OleConvertIStorageToOLESTREAM only.
7564 * Memory allocated for pData must be freed by the caller
7566 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7568 HRESULT hRes;
7569 IStream *pStream;
7570 OLECONVERT_ISTORAGE_OLEPRES olePress;
7571 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7573 /* Initialize Default data for OLESTREAM */
7574 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7575 pOleStreamData[0].dwTypeID = 2;
7576 pOleStreamData[0].dwMetaFileWidth = 0;
7577 pOleStreamData[0].dwMetaFileHeight = 0;
7578 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7579 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7580 pOleStreamData[1].dwTypeID = 0;
7581 pOleStreamData[1].dwOleTypeNameLength = 0;
7582 pOleStreamData[1].strOleTypeName[0] = 0;
7583 pOleStreamData[1].dwMetaFileWidth = 0;
7584 pOleStreamData[1].dwMetaFileHeight = 0;
7585 pOleStreamData[1].pData = NULL;
7586 pOleStreamData[1].dwDataLength = 0;
7589 /* Open OlePress000 stream */
7590 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7591 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7592 if(hRes == S_OK)
7594 LARGE_INTEGER iSeekPos;
7595 METAFILEPICT16 MetaFilePict;
7596 static const char strMetafilePictName[] = "METAFILEPICT";
7598 /* Set the TypeID for a Metafile */
7599 pOleStreamData[1].dwTypeID = 5;
7601 /* Set the OleTypeName to Metafile */
7602 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7603 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7605 iSeekPos.u.HighPart = 0;
7606 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7608 /* Get Presentation Data */
7609 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7610 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7611 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7612 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7614 /*Set width and Height */
7615 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7616 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7617 if(olePress.dwSize > 0)
7619 /* Set Length */
7620 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7622 /* Set MetaFilePict struct */
7623 MetaFilePict.mm = 8;
7624 MetaFilePict.xExt = olePress.dwExtentX;
7625 MetaFilePict.yExt = olePress.dwExtentY;
7626 MetaFilePict.hMF = 0;
7628 /* Get Metafile Data */
7629 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7630 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7631 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7633 IStream_Release(pStream);
7637 /*************************************************************************
7638 * OleConvertOLESTREAMToIStorage [OLE32.@]
7640 * Read info on MSDN
7642 * TODO
7643 * DVTARGETDEVICE parameter is not handled
7644 * Still unsure of some mem fields for OLE 10 Stream
7645 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7646 * and "\001OLE" streams
7649 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7650 LPOLESTREAM pOleStream,
7651 LPSTORAGE pstg,
7652 const DVTARGETDEVICE* ptd)
7654 int i;
7655 HRESULT hRes=S_OK;
7656 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7658 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7660 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7662 if(ptd != NULL)
7664 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7667 if(pstg == NULL || pOleStream == NULL)
7669 hRes = E_INVALIDARG;
7672 if(hRes == S_OK)
7674 /* Load the OLESTREAM to Memory */
7675 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7678 if(hRes == S_OK)
7680 /* Load the OLESTREAM to Memory (part 2)*/
7681 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7684 if(hRes == S_OK)
7687 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7689 /* Do we have the IStorage Data in the OLESTREAM */
7690 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7692 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7693 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7695 else
7697 /* It must be an original OLE 1.0 source */
7698 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7701 else
7703 /* It must be an original OLE 1.0 source */
7704 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7707 /* Create CompObj Stream if necessary */
7708 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7709 if(hRes == S_OK)
7711 /*Create the Ole Stream if necessary */
7712 OLECONVERT_CreateOleStream(pstg);
7717 /* Free allocated memory */
7718 for(i=0; i < 2; i++)
7720 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7721 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7722 pOleStreamData[i].pstrOleObjFileName = NULL;
7724 return hRes;
7727 /*************************************************************************
7728 * OleConvertIStorageToOLESTREAM [OLE32.@]
7730 * Read info on MSDN
7732 * Read info on MSDN
7734 * TODO
7735 * Still unsure of some mem fields for OLE 10 Stream
7736 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7737 * and "\001OLE" streams.
7740 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7741 LPSTORAGE pstg,
7742 LPOLESTREAM pOleStream)
7744 int i;
7745 HRESULT hRes = S_OK;
7746 IStream *pStream;
7747 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7748 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7750 TRACE("%p %p\n", pstg, pOleStream);
7752 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7754 if(pstg == NULL || pOleStream == NULL)
7756 hRes = E_INVALIDARG;
7758 if(hRes == S_OK)
7760 /* Get the ProgID */
7761 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7762 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7764 if(hRes == S_OK)
7766 /* Was it originally Ole10 */
7767 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7768 if(hRes == S_OK)
7770 IStream_Release(pStream);
7771 /* Get Presentation Data for Ole10Native */
7772 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7774 else
7776 /* Get Presentation Data (OLE20) */
7777 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7780 /* Save OLESTREAM */
7781 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7782 if(hRes == S_OK)
7784 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7789 /* Free allocated memory */
7790 for(i=0; i < 2; i++)
7792 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7795 return hRes;
7798 /***********************************************************************
7799 * GetConvertStg (OLE32.@)
7801 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7802 FIXME("unimplemented stub!\n");
7803 return E_FAIL;
7806 /******************************************************************************
7807 * StgIsStorageFile [OLE32.@]
7808 * Verify if the file contains a storage object
7810 * PARAMS
7811 * fn [ I] Filename
7813 * RETURNS
7814 * S_OK if file has magic bytes as a storage object
7815 * S_FALSE if file is not storage
7817 HRESULT WINAPI
7818 StgIsStorageFile(LPCOLESTR fn)
7820 HANDLE hf;
7821 BYTE magic[8];
7822 DWORD bytes_read;
7824 TRACE("%s\n", debugstr_w(fn));
7825 hf = CreateFileW(fn, GENERIC_READ,
7826 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7827 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7829 if (hf == INVALID_HANDLE_VALUE)
7830 return STG_E_FILENOTFOUND;
7832 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7834 WARN(" unable to read file\n");
7835 CloseHandle(hf);
7836 return S_FALSE;
7839 CloseHandle(hf);
7841 if (bytes_read != 8) {
7842 WARN(" too short\n");
7843 return S_FALSE;
7846 if (!memcmp(magic,STORAGE_magic,8)) {
7847 WARN(" -> YES\n");
7848 return S_OK;
7851 WARN(" -> Invalid header.\n");
7852 return S_FALSE;
7855 /***********************************************************************
7856 * WriteClassStm (OLE32.@)
7858 * Writes a CLSID to a stream.
7860 * PARAMS
7861 * pStm [I] Stream to write to.
7862 * rclsid [I] CLSID to write.
7864 * RETURNS
7865 * Success: S_OK.
7866 * Failure: HRESULT code.
7868 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7870 TRACE("(%p,%p)\n",pStm,rclsid);
7872 if (!pStm || !rclsid)
7873 return E_INVALIDARG;
7875 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7878 /***********************************************************************
7879 * ReadClassStm (OLE32.@)
7881 * Reads a CLSID from a stream.
7883 * PARAMS
7884 * pStm [I] Stream to read from.
7885 * rclsid [O] CLSID to read.
7887 * RETURNS
7888 * Success: S_OK.
7889 * Failure: HRESULT code.
7891 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7893 ULONG nbByte;
7894 HRESULT res;
7896 TRACE("(%p,%p)\n",pStm,pclsid);
7898 if (!pStm || !pclsid)
7899 return E_INVALIDARG;
7901 /* clear the output args */
7902 *pclsid = CLSID_NULL;
7904 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7906 if (FAILED(res))
7907 return res;
7909 if (nbByte != sizeof(CLSID))
7910 return STG_E_READFAULT;
7911 else
7912 return S_OK;