d3d9: Hold the lock in stateblock methods.
[wine.git] / dlls / ole32 / storage32.c
blobbda5bdc887a49d00599811ef53f74619b5b8f773
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
32 * MSDN
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
42 #define COBJMACROS
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
46 #include "windef.h"
47 #include "winbase.h"
48 #include "winnls.h"
49 #include "winuser.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
56 #include "winreg.h"
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
66 * These are signatures to detect the type of Document file.
68 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
69 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
71 static const char rootPropertyName[] = "Root Entry";
73 /****************************************************************************
74 * Storage32InternalImpl definitions.
76 * Definition of the implementation structure for the IStorage32 interface.
77 * This one implements the IStorage32 interface for storage that are
78 * inside another storage.
80 struct StorageInternalImpl
82 struct StorageBaseImpl base;
84 * There is no specific data for this class.
87 typedef struct StorageInternalImpl StorageInternalImpl;
89 /* Method definitions for the Storage32InternalImpl class. */
90 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
91 DWORD openFlags, ULONG rootTropertyIndex);
92 static void StorageImpl_Destroy(StorageBaseImpl* iface);
93 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
94 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
95 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
96 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
97 static void StorageImpl_SaveFileHeader(StorageImpl* This);
99 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
100 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
101 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
102 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
103 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
105 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
106 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
107 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
109 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
110 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
111 ULONG blockIndex, ULONG offset, DWORD value);
112 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
113 ULONG blockIndex, ULONG offset, DWORD* value);
115 /* OLESTREAM memory structure to use for Get and Put Routines */
116 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
117 typedef struct
119 DWORD dwOleID;
120 DWORD dwTypeID;
121 DWORD dwOleTypeNameLength;
122 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
123 CHAR *pstrOleObjFileName;
124 DWORD dwOleObjFileNameLength;
125 DWORD dwMetaFileWidth;
126 DWORD dwMetaFileHeight;
127 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
128 DWORD dwDataLength;
129 BYTE *pData;
130 }OLECONVERT_OLESTREAM_DATA;
132 /* CompObj Stream structure */
133 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
134 typedef struct
136 BYTE byUnknown1[12];
137 CLSID clsid;
138 DWORD dwCLSIDNameLength;
139 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
140 DWORD dwOleTypeNameLength;
141 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
142 DWORD dwProgIDNameLength;
143 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
144 BYTE byUnknown2[16];
145 }OLECONVERT_ISTORAGE_COMPOBJ;
148 /* Ole Presention Stream structure */
149 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
150 typedef struct
152 BYTE byUnknown1[28];
153 DWORD dwExtentX;
154 DWORD dwExtentY;
155 DWORD dwSize;
156 BYTE *pData;
157 }OLECONVERT_ISTORAGE_OLEPRES;
161 /***********************************************************************
162 * Forward declaration of internal functions used by the method DestroyElement
164 static HRESULT deleteStorageProperty(
165 StorageImpl *parentStorage,
166 ULONG foundPropertyIndexToDelete,
167 StgProperty propertyToDelete);
169 static HRESULT deleteStreamProperty(
170 StorageImpl *parentStorage,
171 ULONG foundPropertyIndexToDelete,
172 StgProperty propertyToDelete);
174 static HRESULT findPlaceholder(
175 StorageImpl *storage,
176 ULONG propertyIndexToStore,
177 ULONG storagePropertyIndex,
178 INT typeOfRelation);
180 static HRESULT adjustPropertyChain(
181 StorageImpl *This,
182 StgProperty propertyToDelete,
183 StgProperty parentProperty,
184 ULONG parentPropertyId,
185 INT typeOfRelation);
187 /***********************************************************************
188 * Declaration of the functions used to manipulate StgProperty
191 static ULONG getFreeProperty(
192 StorageImpl *storage);
194 static void updatePropertyChain(
195 StorageImpl *storage,
196 ULONG newPropertyIndex,
197 StgProperty newProperty);
199 static LONG propertyNameCmp(
200 const OLECHAR *newProperty,
201 const OLECHAR *currentProperty);
204 /***********************************************************************
205 * Declaration of miscellaneous functions...
207 static HRESULT validateSTGM(DWORD stgmValue);
209 static DWORD GetShareModeFromSTGM(DWORD stgm);
210 static DWORD GetAccessModeFromSTGM(DWORD stgm);
211 static DWORD GetCreationModeFromSTGM(DWORD stgm);
213 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
216 /****************************************************************************
217 * IEnumSTATSTGImpl definitions.
219 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
220 * This class allows iterating through the content of a storage and to find
221 * specific items inside it.
223 struct IEnumSTATSTGImpl
225 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
226 * since we want to cast this in an IEnumSTATSTG pointer */
228 LONG ref; /* Reference count */
229 StorageImpl* parentStorage; /* Reference to the parent storage */
230 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
233 * The current implementation of the IEnumSTATSTGImpl class uses a stack
234 * to walk the property sets to get the content of a storage. This stack
235 * is implemented by the following 3 data members
237 ULONG stackSize;
238 ULONG stackMaxSize;
239 ULONG* stackToVisit;
241 #define ENUMSTATSGT_SIZE_INCREMENT 10
245 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
246 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
247 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
248 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
249 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
250 StgProperty* buffer);
251 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
252 StgProperty *currentProperty, ULONG *propertyId);
254 /************************************************************************
255 ** Block Functions
258 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
260 if (index == 0xffffffff)
261 index = 0;
262 else
263 index ++;
265 return index * BIG_BLOCK_SIZE;
268 /************************************************************************
269 ** Storage32BaseImpl implementatiion
271 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
272 ULARGE_INTEGER offset,
273 void* buffer,
274 ULONG size,
275 ULONG* bytesRead)
277 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
280 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
281 ULARGE_INTEGER offset,
282 void* buffer,
283 const ULONG size,
284 ULONG* bytesWritten)
286 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
289 /************************************************************************
290 * Storage32BaseImpl_QueryInterface (IUnknown)
292 * This method implements the common QueryInterface for all IStorage32
293 * implementations contained in this file.
295 * See Windows documentation for more details on IUnknown methods.
297 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
298 IStorage* iface,
299 REFIID riid,
300 void** ppvObject)
302 StorageBaseImpl *This = (StorageBaseImpl *)iface;
304 * Perform a sanity check on the parameters.
306 if ( (This==0) || (ppvObject==0) )
307 return E_INVALIDARG;
310 * Initialize the return parameter.
312 *ppvObject = 0;
315 * Compare the riid with the interface IDs implemented by this object.
317 if (IsEqualGUID(&IID_IUnknown, riid) ||
318 IsEqualGUID(&IID_IStorage, riid))
320 *ppvObject = (IStorage*)This;
322 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
324 *ppvObject = (IStorage*)&This->pssVtbl;
328 * Check that we obtained an interface.
330 if ((*ppvObject)==0)
331 return E_NOINTERFACE;
334 * Query Interface always increases the reference count by one when it is
335 * successful
337 IStorage_AddRef(iface);
339 return S_OK;
342 /************************************************************************
343 * Storage32BaseImpl_AddRef (IUnknown)
345 * This method implements the common AddRef for all IStorage32
346 * implementations contained in this file.
348 * See Windows documentation for more details on IUnknown methods.
350 static ULONG WINAPI StorageBaseImpl_AddRef(
351 IStorage* iface)
353 StorageBaseImpl *This = (StorageBaseImpl *)iface;
354 ULONG ref = InterlockedIncrement(&This->ref);
356 TRACE("(%p) AddRef to %d\n", This, ref);
358 return ref;
361 /************************************************************************
362 * Storage32BaseImpl_Release (IUnknown)
364 * This method implements the common Release for all IStorage32
365 * implementations contained in this file.
367 * See Windows documentation for more details on IUnknown methods.
369 static ULONG WINAPI StorageBaseImpl_Release(
370 IStorage* iface)
372 StorageBaseImpl *This = (StorageBaseImpl *)iface;
374 * Decrease the reference count on this object.
376 ULONG ref = InterlockedDecrement(&This->ref);
378 TRACE("(%p) ReleaseRef to %d\n", This, ref);
381 * If the reference count goes down to 0, perform suicide.
383 if (ref == 0)
386 * Since we are using a system of base-classes, we want to call the
387 * destructor of the appropriate derived class. To do this, we are
388 * using virtual functions to implement the destructor.
390 This->v_destructor(This);
393 return ref;
396 /************************************************************************
397 * Storage32BaseImpl_OpenStream (IStorage)
399 * This method will open the specified stream object from the current storage.
401 * See Windows documentation for more details on IStorage methods.
403 static HRESULT WINAPI StorageBaseImpl_OpenStream(
404 IStorage* iface,
405 const OLECHAR* pwcsName, /* [string][in] */
406 void* reserved1, /* [unique][in] */
407 DWORD grfMode, /* [in] */
408 DWORD reserved2, /* [in] */
409 IStream** ppstm) /* [out] */
411 StorageBaseImpl *This = (StorageBaseImpl *)iface;
412 IEnumSTATSTGImpl* propertyEnumeration;
413 StgStreamImpl* newStream;
414 StgProperty currentProperty;
415 ULONG foundPropertyIndex;
416 HRESULT res = STG_E_UNKNOWN;
418 TRACE("(%p, %s, %p, %x, %d, %p)\n",
419 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
422 * Perform a sanity check on the parameters.
424 if ( (pwcsName==NULL) || (ppstm==0) )
426 res = E_INVALIDARG;
427 goto end;
431 * Initialize the out parameter
433 *ppstm = NULL;
436 * Validate the STGM flags
438 if ( FAILED( validateSTGM(grfMode) ) ||
439 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
441 res = STG_E_INVALIDFLAG;
442 goto end;
446 * As documented.
448 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
450 res = STG_E_INVALIDFUNCTION;
451 goto end;
455 * Check that we're compatible with the parent's storage mode, but
456 * only if we are not in transacted mode
458 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
459 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
461 res = STG_E_ACCESSDENIED;
462 goto end;
467 * Create a property enumeration to search the properties
469 propertyEnumeration = IEnumSTATSTGImpl_Construct(
470 This->ancestorStorage,
471 This->rootPropertySetIndex);
474 * Search the enumeration for the property with the given name
476 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
477 propertyEnumeration,
478 pwcsName,
479 &currentProperty);
482 * Delete the property enumeration since we don't need it anymore
484 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
487 * If it was found, construct the stream object and return a pointer to it.
489 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
490 (currentProperty.propertyType==PROPTYPE_STREAM) )
492 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
494 if (newStream!=0)
496 newStream->grfMode = grfMode;
497 *ppstm = (IStream*)newStream;
500 * Since we are returning a pointer to the interface, we have to
501 * nail down the reference.
503 IStream_AddRef(*ppstm);
505 res = S_OK;
506 goto end;
509 res = E_OUTOFMEMORY;
510 goto end;
513 res = STG_E_FILENOTFOUND;
515 end:
516 if (res == S_OK)
517 TRACE("<-- IStream %p\n", *ppstm);
518 TRACE("<-- %08x\n", res);
519 return res;
522 /************************************************************************
523 * Storage32BaseImpl_OpenStorage (IStorage)
525 * This method will open a new storage object from the current storage.
527 * See Windows documentation for more details on IStorage methods.
529 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
530 IStorage* iface,
531 const OLECHAR* pwcsName, /* [string][unique][in] */
532 IStorage* pstgPriority, /* [unique][in] */
533 DWORD grfMode, /* [in] */
534 SNB snbExclude, /* [unique][in] */
535 DWORD reserved, /* [in] */
536 IStorage** ppstg) /* [out] */
538 StorageBaseImpl *This = (StorageBaseImpl *)iface;
539 StorageInternalImpl* newStorage;
540 IEnumSTATSTGImpl* propertyEnumeration;
541 StgProperty currentProperty;
542 ULONG foundPropertyIndex;
543 HRESULT res = STG_E_UNKNOWN;
545 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
546 iface, debugstr_w(pwcsName), pstgPriority,
547 grfMode, snbExclude, reserved, ppstg);
550 * Perform a sanity check on the parameters.
552 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
554 res = E_INVALIDARG;
555 goto end;
558 /* as documented */
559 if (snbExclude != NULL)
561 res = STG_E_INVALIDPARAMETER;
562 goto end;
566 * Validate the STGM flags
568 if ( FAILED( validateSTGM(grfMode) ))
570 res = STG_E_INVALIDFLAG;
571 goto end;
575 * As documented.
577 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
578 (grfMode & STGM_DELETEONRELEASE) ||
579 (grfMode & STGM_PRIORITY) )
581 res = STG_E_INVALIDFUNCTION;
582 goto end;
586 * Check that we're compatible with the parent's storage mode,
587 * but only if we are not transacted
589 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
590 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
592 res = STG_E_ACCESSDENIED;
593 goto end;
598 * Initialize the out parameter
600 *ppstg = NULL;
603 * Create a property enumeration to search the properties
605 propertyEnumeration = IEnumSTATSTGImpl_Construct(
606 This->ancestorStorage,
607 This->rootPropertySetIndex);
610 * Search the enumeration for the property with the given name
612 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
613 propertyEnumeration,
614 pwcsName,
615 &currentProperty);
618 * Delete the property enumeration since we don't need it anymore
620 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
623 * If it was found, construct the stream object and return a pointer to it.
625 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
626 (currentProperty.propertyType==PROPTYPE_STORAGE) )
629 * Construct a new Storage object
631 newStorage = StorageInternalImpl_Construct(
632 This->ancestorStorage,
633 grfMode,
634 foundPropertyIndex);
636 if (newStorage != 0)
638 *ppstg = (IStorage*)newStorage;
641 * Since we are returning a pointer to the interface,
642 * we have to nail down the reference.
644 StorageBaseImpl_AddRef(*ppstg);
646 res = S_OK;
647 goto end;
650 res = STG_E_INSUFFICIENTMEMORY;
651 goto end;
654 res = STG_E_FILENOTFOUND;
656 end:
657 TRACE("<-- %08x\n", res);
658 return res;
661 /************************************************************************
662 * Storage32BaseImpl_EnumElements (IStorage)
664 * This method will create an enumerator object that can be used to
665 * retrieve informatino about all the properties in the storage object.
667 * See Windows documentation for more details on IStorage methods.
669 static HRESULT WINAPI StorageBaseImpl_EnumElements(
670 IStorage* iface,
671 DWORD reserved1, /* [in] */
672 void* reserved2, /* [size_is][unique][in] */
673 DWORD reserved3, /* [in] */
674 IEnumSTATSTG** ppenum) /* [out] */
676 StorageBaseImpl *This = (StorageBaseImpl *)iface;
677 IEnumSTATSTGImpl* newEnum;
679 TRACE("(%p, %d, %p, %d, %p)\n",
680 iface, reserved1, reserved2, reserved3, ppenum);
683 * Perform a sanity check on the parameters.
685 if ( (This==0) || (ppenum==0))
686 return E_INVALIDARG;
689 * Construct the enumerator.
691 newEnum = IEnumSTATSTGImpl_Construct(
692 This->ancestorStorage,
693 This->rootPropertySetIndex);
695 if (newEnum!=0)
697 *ppenum = (IEnumSTATSTG*)newEnum;
700 * Don't forget to nail down a reference to the new object before
701 * returning it.
703 IEnumSTATSTG_AddRef(*ppenum);
705 return S_OK;
708 return E_OUTOFMEMORY;
711 /************************************************************************
712 * Storage32BaseImpl_Stat (IStorage)
714 * This method will retrieve information about this storage object.
716 * See Windows documentation for more details on IStorage methods.
718 static HRESULT WINAPI StorageBaseImpl_Stat(
719 IStorage* iface,
720 STATSTG* pstatstg, /* [out] */
721 DWORD grfStatFlag) /* [in] */
723 StorageBaseImpl *This = (StorageBaseImpl *)iface;
724 StgProperty curProperty;
725 BOOL readSuccessful;
726 HRESULT res = STG_E_UNKNOWN;
728 TRACE("(%p, %p, %x)\n",
729 iface, pstatstg, grfStatFlag);
732 * Perform a sanity check on the parameters.
734 if ( (This==0) || (pstatstg==0))
736 res = E_INVALIDARG;
737 goto end;
741 * Read the information from the property.
743 readSuccessful = StorageImpl_ReadProperty(
744 This->ancestorStorage,
745 This->rootPropertySetIndex,
746 &curProperty);
748 if (readSuccessful)
750 StorageUtl_CopyPropertyToSTATSTG(
751 pstatstg,
752 &curProperty,
753 grfStatFlag);
755 pstatstg->grfMode = This->openFlags;
756 pstatstg->grfStateBits = This->stateBits;
758 res = S_OK;
759 goto end;
762 res = E_FAIL;
764 end:
765 if (res == S_OK)
767 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
769 TRACE("<-- %08x\n", res);
770 return res;
773 /************************************************************************
774 * Storage32BaseImpl_RenameElement (IStorage)
776 * This method will rename the specified element.
778 * See Windows documentation for more details on IStorage methods.
780 * Implementation notes: The method used to rename consists of creating a clone
781 * of the deleted StgProperty object setting it with the new name and to
782 * perform a DestroyElement of the old StgProperty.
784 static HRESULT WINAPI StorageBaseImpl_RenameElement(
785 IStorage* iface,
786 const OLECHAR* pwcsOldName, /* [in] */
787 const OLECHAR* pwcsNewName) /* [in] */
789 StorageBaseImpl *This = (StorageBaseImpl *)iface;
790 IEnumSTATSTGImpl* propertyEnumeration;
791 StgProperty currentProperty;
792 ULONG foundPropertyIndex;
794 TRACE("(%p, %s, %s)\n",
795 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
798 * Create a property enumeration to search the properties
800 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
801 This->rootPropertySetIndex);
804 * Search the enumeration for the new property name
806 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
807 pwcsNewName,
808 &currentProperty);
810 if (foundPropertyIndex != PROPERTY_NULL)
813 * There is already a property with the new name
815 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
816 return STG_E_FILEALREADYEXISTS;
819 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
822 * Search the enumeration for the old property name
824 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
825 pwcsOldName,
826 &currentProperty);
829 * Delete the property enumeration since we don't need it anymore
831 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
833 if (foundPropertyIndex != PROPERTY_NULL)
835 StgProperty renamedProperty;
836 ULONG renamedPropertyIndex;
839 * Setup a new property for the renamed property
841 renamedProperty.sizeOfNameString =
842 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
844 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
845 return STG_E_INVALIDNAME;
847 strcpyW(renamedProperty.name, pwcsNewName);
849 renamedProperty.propertyType = currentProperty.propertyType;
850 renamedProperty.startingBlock = currentProperty.startingBlock;
851 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
852 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
854 renamedProperty.previousProperty = PROPERTY_NULL;
855 renamedProperty.nextProperty = PROPERTY_NULL;
858 * Bring the dirProperty link in case it is a storage and in which
859 * case the renamed storage elements don't require to be reorganized.
861 renamedProperty.dirProperty = currentProperty.dirProperty;
863 /* call CoFileTime to get the current time
864 renamedProperty.timeStampS1
865 renamedProperty.timeStampD1
866 renamedProperty.timeStampS2
867 renamedProperty.timeStampD2
868 renamedProperty.propertyUniqueID
872 * Obtain a free property in the property chain
874 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
877 * Save the new property into the new property spot
879 StorageImpl_WriteProperty(
880 This->ancestorStorage,
881 renamedPropertyIndex,
882 &renamedProperty);
885 * Find a spot in the property chain for our newly created property.
887 updatePropertyChain(
888 (StorageImpl*)This,
889 renamedPropertyIndex,
890 renamedProperty);
893 * At this point the renamed property has been inserted in the tree,
894 * now, before Destroying the old property we must zero its dirProperty
895 * otherwise the DestroyProperty below will zap it all and we do not want
896 * this to happen.
897 * Also, we fake that the old property is a storage so the DestroyProperty
898 * will not do a SetSize(0) on the stream data.
900 * This means that we need to tweak the StgProperty if it is a stream or a
901 * non empty storage.
903 StorageImpl_ReadProperty(This->ancestorStorage,
904 foundPropertyIndex,
905 &currentProperty);
907 currentProperty.dirProperty = PROPERTY_NULL;
908 currentProperty.propertyType = PROPTYPE_STORAGE;
909 StorageImpl_WriteProperty(
910 This->ancestorStorage,
911 foundPropertyIndex,
912 &currentProperty);
915 * Invoke Destroy to get rid of the ole property and automatically redo
916 * the linking of its previous and next members...
918 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
921 else
924 * There is no property with the old name
926 return STG_E_FILENOTFOUND;
929 return S_OK;
932 /************************************************************************
933 * Storage32BaseImpl_CreateStream (IStorage)
935 * This method will create a stream object within this storage
937 * See Windows documentation for more details on IStorage methods.
939 static HRESULT WINAPI StorageBaseImpl_CreateStream(
940 IStorage* iface,
941 const OLECHAR* pwcsName, /* [string][in] */
942 DWORD grfMode, /* [in] */
943 DWORD reserved1, /* [in] */
944 DWORD reserved2, /* [in] */
945 IStream** ppstm) /* [out] */
947 StorageBaseImpl *This = (StorageBaseImpl *)iface;
948 IEnumSTATSTGImpl* propertyEnumeration;
949 StgStreamImpl* newStream;
950 StgProperty currentProperty, newStreamProperty;
951 ULONG foundPropertyIndex, newPropertyIndex;
953 TRACE("(%p, %s, %x, %d, %d, %p)\n",
954 iface, debugstr_w(pwcsName), grfMode,
955 reserved1, reserved2, ppstm);
958 * Validate parameters
960 if (ppstm == 0)
961 return STG_E_INVALIDPOINTER;
963 if (pwcsName == 0)
964 return STG_E_INVALIDNAME;
966 if (reserved1 || reserved2)
967 return STG_E_INVALIDPARAMETER;
970 * Validate the STGM flags
972 if ( FAILED( validateSTGM(grfMode) ))
973 return STG_E_INVALIDFLAG;
975 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
976 return STG_E_INVALIDFLAG;
979 * As documented.
981 if ((grfMode & STGM_DELETEONRELEASE) ||
982 (grfMode & STGM_TRANSACTED))
983 return STG_E_INVALIDFUNCTION;
986 * Check that we're compatible with the parent's storage mode
987 * if not in transacted mode
989 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
990 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
991 return STG_E_ACCESSDENIED;
995 * Initialize the out parameter
997 *ppstm = 0;
1000 * Create a property enumeration to search the properties
1002 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
1003 This->rootPropertySetIndex);
1005 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1006 pwcsName,
1007 &currentProperty);
1009 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1011 if (foundPropertyIndex != PROPERTY_NULL)
1014 * An element with this name already exists
1016 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1018 IStorage_DestroyElement(iface, pwcsName);
1020 else
1021 return STG_E_FILEALREADYEXISTS;
1023 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1025 WARN("read-only storage\n");
1026 return STG_E_ACCESSDENIED;
1030 * memset the empty property
1032 memset(&newStreamProperty, 0, sizeof(StgProperty));
1034 newStreamProperty.sizeOfNameString =
1035 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1037 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1038 return STG_E_INVALIDNAME;
1040 strcpyW(newStreamProperty.name, pwcsName);
1042 newStreamProperty.propertyType = PROPTYPE_STREAM;
1043 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1044 newStreamProperty.size.u.LowPart = 0;
1045 newStreamProperty.size.u.HighPart = 0;
1047 newStreamProperty.previousProperty = PROPERTY_NULL;
1048 newStreamProperty.nextProperty = PROPERTY_NULL;
1049 newStreamProperty.dirProperty = PROPERTY_NULL;
1051 /* call CoFileTime to get the current time
1052 newStreamProperty.timeStampS1
1053 newStreamProperty.timeStampD1
1054 newStreamProperty.timeStampS2
1055 newStreamProperty.timeStampD2
1058 /* newStreamProperty.propertyUniqueID */
1061 * Get a free property or create a new one
1063 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1066 * Save the new property into the new property spot
1068 StorageImpl_WriteProperty(
1069 This->ancestorStorage,
1070 newPropertyIndex,
1071 &newStreamProperty);
1074 * Find a spot in the property chain for our newly created property.
1076 updatePropertyChain(
1077 (StorageImpl*)This,
1078 newPropertyIndex,
1079 newStreamProperty);
1082 * Open the stream to return it.
1084 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1086 if (newStream != 0)
1088 *ppstm = (IStream*)newStream;
1091 * Since we are returning a pointer to the interface, we have to nail down
1092 * the reference.
1094 IStream_AddRef(*ppstm);
1096 else
1098 return STG_E_INSUFFICIENTMEMORY;
1101 return S_OK;
1104 /************************************************************************
1105 * Storage32BaseImpl_SetClass (IStorage)
1107 * This method will write the specified CLSID in the property of this
1108 * storage.
1110 * See Windows documentation for more details on IStorage methods.
1112 static HRESULT WINAPI StorageBaseImpl_SetClass(
1113 IStorage* iface,
1114 REFCLSID clsid) /* [in] */
1116 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1117 HRESULT hRes = E_FAIL;
1118 StgProperty curProperty;
1119 BOOL success;
1121 TRACE("(%p, %p)\n", iface, clsid);
1123 success = StorageImpl_ReadProperty(This->ancestorStorage,
1124 This->rootPropertySetIndex,
1125 &curProperty);
1126 if (success)
1128 curProperty.propertyUniqueID = *clsid;
1130 success = StorageImpl_WriteProperty(This->ancestorStorage,
1131 This->rootPropertySetIndex,
1132 &curProperty);
1133 if (success)
1134 hRes = S_OK;
1137 return hRes;
1140 /************************************************************************
1141 ** Storage32Impl implementation
1144 /************************************************************************
1145 * Storage32Impl_CreateStorage (IStorage)
1147 * This method will create the storage object within the provided storage.
1149 * See Windows documentation for more details on IStorage methods.
1151 static HRESULT WINAPI StorageImpl_CreateStorage(
1152 IStorage* iface,
1153 const OLECHAR *pwcsName, /* [string][in] */
1154 DWORD grfMode, /* [in] */
1155 DWORD reserved1, /* [in] */
1156 DWORD reserved2, /* [in] */
1157 IStorage **ppstg) /* [out] */
1159 StorageImpl* const This=(StorageImpl*)iface;
1161 IEnumSTATSTGImpl *propertyEnumeration;
1162 StgProperty currentProperty;
1163 StgProperty newProperty;
1164 ULONG foundPropertyIndex;
1165 ULONG newPropertyIndex;
1166 HRESULT hr;
1168 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1169 iface, debugstr_w(pwcsName), grfMode,
1170 reserved1, reserved2, ppstg);
1173 * Validate parameters
1175 if (ppstg == 0)
1176 return STG_E_INVALIDPOINTER;
1178 if (pwcsName == 0)
1179 return STG_E_INVALIDNAME;
1182 * Initialize the out parameter
1184 *ppstg = NULL;
1187 * Validate the STGM flags
1189 if ( FAILED( validateSTGM(grfMode) ) ||
1190 (grfMode & STGM_DELETEONRELEASE) )
1192 WARN("bad grfMode: 0x%x\n", grfMode);
1193 return STG_E_INVALIDFLAG;
1197 * Check that we're compatible with the parent's storage mode
1199 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1201 WARN("access denied\n");
1202 return STG_E_ACCESSDENIED;
1206 * Create a property enumeration and search the properties
1208 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1209 This->base.rootPropertySetIndex);
1211 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1212 pwcsName,
1213 &currentProperty);
1214 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1216 if (foundPropertyIndex != PROPERTY_NULL)
1219 * An element with this name already exists
1221 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1222 IStorage_DestroyElement(iface, pwcsName);
1223 else
1225 WARN("file already exists\n");
1226 return STG_E_FILEALREADYEXISTS;
1229 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1231 WARN("read-only storage\n");
1232 return STG_E_ACCESSDENIED;
1236 * memset the empty property
1238 memset(&newProperty, 0, sizeof(StgProperty));
1240 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1242 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1244 FIXME("name too long\n");
1245 return STG_E_INVALIDNAME;
1248 strcpyW(newProperty.name, pwcsName);
1250 newProperty.propertyType = PROPTYPE_STORAGE;
1251 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1252 newProperty.size.u.LowPart = 0;
1253 newProperty.size.u.HighPart = 0;
1255 newProperty.previousProperty = PROPERTY_NULL;
1256 newProperty.nextProperty = PROPERTY_NULL;
1257 newProperty.dirProperty = PROPERTY_NULL;
1259 /* call CoFileTime to get the current time
1260 newProperty.timeStampS1
1261 newProperty.timeStampD1
1262 newProperty.timeStampS2
1263 newProperty.timeStampD2
1266 /* newStorageProperty.propertyUniqueID */
1269 * Obtain a free property in the property chain
1271 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1274 * Save the new property into the new property spot
1276 StorageImpl_WriteProperty(
1277 This->base.ancestorStorage,
1278 newPropertyIndex,
1279 &newProperty);
1282 * Find a spot in the property chain for our newly created property.
1284 updatePropertyChain(
1285 This,
1286 newPropertyIndex,
1287 newProperty);
1290 * Open it to get a pointer to return.
1292 hr = IStorage_OpenStorage(
1293 iface,
1294 (const OLECHAR*)pwcsName,
1296 grfMode,
1299 ppstg);
1301 if( (hr != S_OK) || (*ppstg == NULL))
1303 return hr;
1307 return S_OK;
1311 /***************************************************************************
1313 * Internal Method
1315 * Get a free property or create a new one.
1317 static ULONG getFreeProperty(
1318 StorageImpl *storage)
1320 ULONG currentPropertyIndex = 0;
1321 ULONG newPropertyIndex = PROPERTY_NULL;
1322 BOOL readSuccessful = TRUE;
1323 StgProperty currentProperty;
1328 * Start by reading the root property
1330 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1331 currentPropertyIndex,
1332 &currentProperty);
1333 if (readSuccessful)
1335 if (currentProperty.sizeOfNameString == 0)
1338 * The property existis and is available, we found it.
1340 newPropertyIndex = currentPropertyIndex;
1343 else
1346 * We exhausted the property list, we will create more space below
1348 newPropertyIndex = currentPropertyIndex;
1350 currentPropertyIndex++;
1352 } while (newPropertyIndex == PROPERTY_NULL);
1355 * grow the property chain
1357 if (! readSuccessful)
1359 StgProperty emptyProperty;
1360 ULARGE_INTEGER newSize;
1361 ULONG propertyIndex;
1362 ULONG lastProperty = 0;
1363 ULONG blockCount = 0;
1366 * obtain the new count of property blocks
1368 blockCount = BlockChainStream_GetCount(
1369 storage->base.ancestorStorage->rootBlockChain)+1;
1372 * initialize the size used by the property stream
1374 newSize.u.HighPart = 0;
1375 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1378 * add a property block to the property chain
1380 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1383 * memset the empty property in order to initialize the unused newly
1384 * created property
1386 memset(&emptyProperty, 0, sizeof(StgProperty));
1389 * initialize them
1391 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1393 for(
1394 propertyIndex = newPropertyIndex;
1395 propertyIndex < lastProperty;
1396 propertyIndex++)
1398 StorageImpl_WriteProperty(
1399 storage->base.ancestorStorage,
1400 propertyIndex,
1401 &emptyProperty);
1405 return newPropertyIndex;
1408 /****************************************************************************
1410 * Internal Method
1412 * Case insensitive comparaison of StgProperty.name by first considering
1413 * their size.
1415 * Returns <0 when newPrpoerty < currentProperty
1416 * >0 when newPrpoerty > currentProperty
1417 * 0 when newPrpoerty == currentProperty
1419 static LONG propertyNameCmp(
1420 const OLECHAR *newProperty,
1421 const OLECHAR *currentProperty)
1423 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1425 if (diff == 0)
1428 * We compare the string themselves only when they are of the same length
1430 diff = lstrcmpiW( newProperty, currentProperty);
1433 return diff;
1436 /****************************************************************************
1438 * Internal Method
1440 * Properly link this new element in the property chain.
1442 static void updatePropertyChain(
1443 StorageImpl *storage,
1444 ULONG newPropertyIndex,
1445 StgProperty newProperty)
1447 StgProperty currentProperty;
1450 * Read the root property
1452 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1453 storage->base.rootPropertySetIndex,
1454 &currentProperty);
1456 if (currentProperty.dirProperty != PROPERTY_NULL)
1459 * The root storage contains some element, therefore, start the research
1460 * for the appropriate location.
1462 BOOL found = 0;
1463 ULONG current, next, previous, currentPropertyId;
1466 * Keep the StgProperty sequence number of the storage first property
1468 currentPropertyId = currentProperty.dirProperty;
1471 * Read
1473 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1474 currentProperty.dirProperty,
1475 &currentProperty);
1477 previous = currentProperty.previousProperty;
1478 next = currentProperty.nextProperty;
1479 current = currentPropertyId;
1481 while (found == 0)
1483 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1485 if (diff < 0)
1487 if (previous != PROPERTY_NULL)
1489 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1490 previous,
1491 &currentProperty);
1492 current = previous;
1494 else
1496 currentProperty.previousProperty = newPropertyIndex;
1497 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1498 current,
1499 &currentProperty);
1500 found = 1;
1503 else if (diff > 0)
1505 if (next != PROPERTY_NULL)
1507 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1508 next,
1509 &currentProperty);
1510 current = next;
1512 else
1514 currentProperty.nextProperty = newPropertyIndex;
1515 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1516 current,
1517 &currentProperty);
1518 found = 1;
1521 else
1524 * Trying to insert an item with the same name in the
1525 * subtree structure.
1527 assert(FALSE);
1530 previous = currentProperty.previousProperty;
1531 next = currentProperty.nextProperty;
1534 else
1537 * The root storage is empty, link the new property to its dir property
1539 currentProperty.dirProperty = newPropertyIndex;
1540 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1541 storage->base.rootPropertySetIndex,
1542 &currentProperty);
1547 /*************************************************************************
1548 * CopyTo (IStorage)
1550 static HRESULT WINAPI StorageImpl_CopyTo(
1551 IStorage* iface,
1552 DWORD ciidExclude, /* [in] */
1553 const IID* rgiidExclude, /* [size_is][unique][in] */
1554 SNB snbExclude, /* [unique][in] */
1555 IStorage* pstgDest) /* [unique][in] */
1557 IEnumSTATSTG *elements = 0;
1558 STATSTG curElement, strStat;
1559 HRESULT hr;
1560 IStorage *pstgTmp, *pstgChild;
1561 IStream *pstrTmp, *pstrChild;
1563 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1564 FIXME("Exclude option not implemented\n");
1566 TRACE("(%p, %d, %p, %p, %p)\n",
1567 iface, ciidExclude, rgiidExclude,
1568 snbExclude, pstgDest);
1571 * Perform a sanity check
1573 if ( pstgDest == 0 )
1574 return STG_E_INVALIDPOINTER;
1577 * Enumerate the elements
1579 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1581 if ( hr != S_OK )
1582 return hr;
1585 * set the class ID
1587 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1588 IStorage_SetClass( pstgDest, &curElement.clsid );
1593 * Obtain the next element
1595 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1597 if ( hr == S_FALSE )
1599 hr = S_OK; /* done, every element has been copied */
1600 break;
1603 if (curElement.type == STGTY_STORAGE)
1606 * open child source storage
1608 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1609 STGM_READ|STGM_SHARE_EXCLUSIVE,
1610 NULL, 0, &pstgChild );
1612 if (hr != S_OK)
1613 break;
1616 * Check if destination storage is not a child of the source
1617 * storage, which will cause an infinite loop
1619 if (pstgChild == pstgDest)
1621 IEnumSTATSTG_Release(elements);
1623 return STG_E_ACCESSDENIED;
1627 * create a new storage in destination storage
1629 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1630 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1631 0, 0,
1632 &pstgTmp );
1634 * if it already exist, don't create a new one use this one
1636 if (hr == STG_E_FILEALREADYEXISTS)
1638 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1639 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1640 NULL, 0, &pstgTmp );
1643 if (hr != S_OK)
1644 break;
1648 * do the copy recursively
1650 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1651 snbExclude, pstgTmp );
1653 IStorage_Release( pstgTmp );
1654 IStorage_Release( pstgChild );
1656 else if (curElement.type == STGTY_STREAM)
1659 * create a new stream in destination storage. If the stream already
1660 * exist, it will be deleted and a new one will be created.
1662 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1663 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1664 0, 0, &pstrTmp );
1666 if (hr != S_OK)
1667 break;
1670 * open child stream storage
1672 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1673 STGM_READ|STGM_SHARE_EXCLUSIVE,
1674 0, &pstrChild );
1676 if (hr != S_OK)
1677 break;
1680 * Get the size of the source stream
1682 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1685 * Set the size of the destination stream.
1687 IStream_SetSize(pstrTmp, strStat.cbSize);
1690 * do the copy
1692 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1693 NULL, NULL );
1695 IStream_Release( pstrTmp );
1696 IStream_Release( pstrChild );
1698 else
1700 WARN("unknown element type: %d\n", curElement.type);
1703 } while (hr == S_OK);
1706 * Clean-up
1708 IEnumSTATSTG_Release(elements);
1710 return hr;
1713 /*************************************************************************
1714 * MoveElementTo (IStorage)
1716 static HRESULT WINAPI StorageImpl_MoveElementTo(
1717 IStorage* iface,
1718 const OLECHAR *pwcsName, /* [string][in] */
1719 IStorage *pstgDest, /* [unique][in] */
1720 const OLECHAR *pwcsNewName,/* [string][in] */
1721 DWORD grfFlags) /* [in] */
1723 FIXME("(%p %s %p %s %u): stub\n", iface,
1724 debugstr_w(pwcsName), pstgDest,
1725 debugstr_w(pwcsNewName), grfFlags);
1726 return E_NOTIMPL;
1729 /*************************************************************************
1730 * Commit (IStorage)
1732 * Ensures that any changes made to a storage object open in transacted mode
1733 * are reflected in the parent storage
1735 * NOTES
1736 * Wine doesn't implement transacted mode, which seems to be a basic
1737 * optimization, so we can ignore this stub for now.
1739 static HRESULT WINAPI StorageImpl_Commit(
1740 IStorage* iface,
1741 DWORD grfCommitFlags)/* [in] */
1743 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1744 return S_OK;
1747 /*************************************************************************
1748 * Revert (IStorage)
1750 * Discard all changes that have been made since the last commit operation
1752 static HRESULT WINAPI StorageImpl_Revert(
1753 IStorage* iface)
1755 FIXME("(%p): stub\n", iface);
1756 return E_NOTIMPL;
1759 /*************************************************************************
1760 * DestroyElement (IStorage)
1762 * Strategy: This implementation is built this way for simplicity not for speed.
1763 * I always delete the topmost element of the enumeration and adjust
1764 * the deleted element pointer all the time. This takes longer to
1765 * do but allow to reinvoke DestroyElement whenever we encounter a
1766 * storage object. The optimisation resides in the usage of another
1767 * enumeration strategy that would give all the leaves of a storage
1768 * first. (postfix order)
1770 static HRESULT WINAPI StorageImpl_DestroyElement(
1771 IStorage* iface,
1772 const OLECHAR *pwcsName)/* [string][in] */
1774 StorageImpl* const This=(StorageImpl*)iface;
1776 IEnumSTATSTGImpl* propertyEnumeration;
1777 HRESULT hr = S_OK;
1778 BOOL res;
1779 StgProperty propertyToDelete;
1780 StgProperty parentProperty;
1781 ULONG foundPropertyIndexToDelete;
1782 ULONG typeOfRelation;
1783 ULONG parentPropertyId = 0;
1785 TRACE("(%p, %s)\n",
1786 iface, debugstr_w(pwcsName));
1789 * Perform a sanity check on the parameters.
1791 if (pwcsName==NULL)
1792 return STG_E_INVALIDPOINTER;
1795 * Create a property enumeration to search the property with the given name
1797 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1798 This->base.ancestorStorage,
1799 This->base.rootPropertySetIndex);
1801 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1802 propertyEnumeration,
1803 pwcsName,
1804 &propertyToDelete);
1806 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1808 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1810 return STG_E_FILENOTFOUND;
1814 * Find the parent property of the property to delete (the one that
1815 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1816 * the parent is This. Otherwise, the parent is one of its sibling...
1820 * First, read This's StgProperty..
1822 res = StorageImpl_ReadProperty(
1823 This->base.ancestorStorage,
1824 This->base.rootPropertySetIndex,
1825 &parentProperty);
1827 assert(res);
1830 * Second, check to see if by any chance the actual storage (This) is not
1831 * the parent of the property to delete... We never know...
1833 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1836 * Set data as it would have been done in the else part...
1838 typeOfRelation = PROPERTY_RELATION_DIR;
1839 parentPropertyId = This->base.rootPropertySetIndex;
1841 else
1844 * Create a property enumeration to search the parent properties, and
1845 * delete it once done.
1847 IEnumSTATSTGImpl* propertyEnumeration2;
1849 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1850 This->base.ancestorStorage,
1851 This->base.rootPropertySetIndex);
1853 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1854 propertyEnumeration2,
1855 foundPropertyIndexToDelete,
1856 &parentProperty,
1857 &parentPropertyId);
1859 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1862 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1864 hr = deleteStorageProperty(
1865 This,
1866 foundPropertyIndexToDelete,
1867 propertyToDelete);
1869 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1871 hr = deleteStreamProperty(
1872 This,
1873 foundPropertyIndexToDelete,
1874 propertyToDelete);
1877 if (hr!=S_OK)
1878 return hr;
1881 * Adjust the property chain
1883 hr = adjustPropertyChain(
1884 This,
1885 propertyToDelete,
1886 parentProperty,
1887 parentPropertyId,
1888 typeOfRelation);
1890 return hr;
1894 /************************************************************************
1895 * StorageImpl_Stat (IStorage)
1897 * This method will retrieve information about this storage object.
1899 * See Windows documentation for more details on IStorage methods.
1901 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1902 STATSTG* pstatstg, /* [out] */
1903 DWORD grfStatFlag) /* [in] */
1905 StorageImpl* const This = (StorageImpl*)iface;
1906 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1908 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1910 CoTaskMemFree(pstatstg->pwcsName);
1911 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1912 strcpyW(pstatstg->pwcsName, This->pwcsName);
1915 return result;
1918 /******************************************************************************
1919 * Internal stream list handlers
1922 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1924 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1925 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1928 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1930 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1931 list_remove(&(strm->StrmListEntry));
1934 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1936 struct list *cur, *cur2;
1937 StgStreamImpl *strm=NULL;
1939 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1940 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1941 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1942 strm->parentStorage = NULL;
1943 list_remove(cur);
1948 /*********************************************************************
1950 * Internal Method
1952 * Perform the deletion of a complete storage node
1955 static HRESULT deleteStorageProperty(
1956 StorageImpl *parentStorage,
1957 ULONG indexOfPropertyToDelete,
1958 StgProperty propertyToDelete)
1960 IEnumSTATSTG *elements = 0;
1961 IStorage *childStorage = 0;
1962 STATSTG currentElement;
1963 HRESULT hr;
1964 HRESULT destroyHr = S_OK;
1967 * Open the storage and enumerate it
1969 hr = StorageBaseImpl_OpenStorage(
1970 (IStorage*)parentStorage,
1971 propertyToDelete.name,
1973 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1976 &childStorage);
1978 if (hr != S_OK)
1980 return hr;
1984 * Enumerate the elements
1986 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1991 * Obtain the next element
1993 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1994 if (hr==S_OK)
1996 destroyHr = StorageImpl_DestroyElement(
1997 (IStorage*)childStorage,
1998 (OLECHAR*)currentElement.pwcsName);
2000 CoTaskMemFree(currentElement.pwcsName);
2004 * We need to Reset the enumeration every time because we delete elements
2005 * and the enumeration could be invalid
2007 IEnumSTATSTG_Reset(elements);
2009 } while ((hr == S_OK) && (destroyHr == S_OK));
2012 * Invalidate the property by zeroing its name member.
2014 propertyToDelete.sizeOfNameString = 0;
2016 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
2017 indexOfPropertyToDelete,
2018 &propertyToDelete);
2020 IStorage_Release(childStorage);
2021 IEnumSTATSTG_Release(elements);
2023 return destroyHr;
2026 /*********************************************************************
2028 * Internal Method
2030 * Perform the deletion of a stream node
2033 static HRESULT deleteStreamProperty(
2034 StorageImpl *parentStorage,
2035 ULONG indexOfPropertyToDelete,
2036 StgProperty propertyToDelete)
2038 IStream *pis;
2039 HRESULT hr;
2040 ULARGE_INTEGER size;
2042 size.u.HighPart = 0;
2043 size.u.LowPart = 0;
2045 hr = StorageBaseImpl_OpenStream(
2046 (IStorage*)parentStorage,
2047 (OLECHAR*)propertyToDelete.name,
2048 NULL,
2049 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2051 &pis);
2053 if (hr!=S_OK)
2055 return(hr);
2059 * Zap the stream
2061 hr = IStream_SetSize(pis, size);
2063 if(hr != S_OK)
2065 return hr;
2069 * Release the stream object.
2071 IStream_Release(pis);
2074 * Invalidate the property by zeroing its name member.
2076 propertyToDelete.sizeOfNameString = 0;
2079 * Here we should re-read the property so we get the updated pointer
2080 * but since we are here to zap it, I don't do it...
2082 StorageImpl_WriteProperty(
2083 parentStorage->base.ancestorStorage,
2084 indexOfPropertyToDelete,
2085 &propertyToDelete);
2087 return S_OK;
2090 /*********************************************************************
2092 * Internal Method
2094 * Finds a placeholder for the StgProperty within the Storage
2097 static HRESULT findPlaceholder(
2098 StorageImpl *storage,
2099 ULONG propertyIndexToStore,
2100 ULONG storePropertyIndex,
2101 INT typeOfRelation)
2103 StgProperty storeProperty;
2104 HRESULT hr = S_OK;
2105 BOOL res = TRUE;
2108 * Read the storage property
2110 res = StorageImpl_ReadProperty(
2111 storage->base.ancestorStorage,
2112 storePropertyIndex,
2113 &storeProperty);
2115 if(! res)
2117 return E_FAIL;
2120 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2122 if (storeProperty.previousProperty != PROPERTY_NULL)
2124 return findPlaceholder(
2125 storage,
2126 propertyIndexToStore,
2127 storeProperty.previousProperty,
2128 typeOfRelation);
2130 else
2132 storeProperty.previousProperty = propertyIndexToStore;
2135 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2137 if (storeProperty.nextProperty != PROPERTY_NULL)
2139 return findPlaceholder(
2140 storage,
2141 propertyIndexToStore,
2142 storeProperty.nextProperty,
2143 typeOfRelation);
2145 else
2147 storeProperty.nextProperty = propertyIndexToStore;
2150 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2152 if (storeProperty.dirProperty != PROPERTY_NULL)
2154 return findPlaceholder(
2155 storage,
2156 propertyIndexToStore,
2157 storeProperty.dirProperty,
2158 typeOfRelation);
2160 else
2162 storeProperty.dirProperty = propertyIndexToStore;
2166 hr = StorageImpl_WriteProperty(
2167 storage->base.ancestorStorage,
2168 storePropertyIndex,
2169 &storeProperty);
2171 if(! hr)
2173 return E_FAIL;
2176 return S_OK;
2179 /*************************************************************************
2181 * Internal Method
2183 * This method takes the previous and the next property link of a property
2184 * to be deleted and find them a place in the Storage.
2186 static HRESULT adjustPropertyChain(
2187 StorageImpl *This,
2188 StgProperty propertyToDelete,
2189 StgProperty parentProperty,
2190 ULONG parentPropertyId,
2191 INT typeOfRelation)
2193 ULONG newLinkProperty = PROPERTY_NULL;
2194 BOOL needToFindAPlaceholder = FALSE;
2195 ULONG storeNode = PROPERTY_NULL;
2196 ULONG toStoreNode = PROPERTY_NULL;
2197 INT relationType = 0;
2198 HRESULT hr = S_OK;
2199 BOOL res = TRUE;
2201 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2203 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2206 * Set the parent previous to the property to delete previous
2208 newLinkProperty = propertyToDelete.previousProperty;
2210 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2213 * We also need to find a storage for the other link, setup variables
2214 * to do this at the end...
2216 needToFindAPlaceholder = TRUE;
2217 storeNode = propertyToDelete.previousProperty;
2218 toStoreNode = propertyToDelete.nextProperty;
2219 relationType = PROPERTY_RELATION_NEXT;
2222 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2225 * Set the parent previous to the property to delete next
2227 newLinkProperty = propertyToDelete.nextProperty;
2231 * Link it for real...
2233 parentProperty.previousProperty = newLinkProperty;
2236 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2238 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2241 * Set the parent next to the property to delete next previous
2243 newLinkProperty = propertyToDelete.previousProperty;
2245 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2248 * We also need to find a storage for the other link, setup variables
2249 * to do this at the end...
2251 needToFindAPlaceholder = TRUE;
2252 storeNode = propertyToDelete.previousProperty;
2253 toStoreNode = propertyToDelete.nextProperty;
2254 relationType = PROPERTY_RELATION_NEXT;
2257 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2260 * Set the parent next to the property to delete next
2262 newLinkProperty = propertyToDelete.nextProperty;
2266 * Link it for real...
2268 parentProperty.nextProperty = newLinkProperty;
2270 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2272 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2275 * Set the parent dir to the property to delete previous
2277 newLinkProperty = propertyToDelete.previousProperty;
2279 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2282 * We also need to find a storage for the other link, setup variables
2283 * to do this at the end...
2285 needToFindAPlaceholder = TRUE;
2286 storeNode = propertyToDelete.previousProperty;
2287 toStoreNode = propertyToDelete.nextProperty;
2288 relationType = PROPERTY_RELATION_NEXT;
2291 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2294 * Set the parent dir to the property to delete next
2296 newLinkProperty = propertyToDelete.nextProperty;
2300 * Link it for real...
2302 parentProperty.dirProperty = newLinkProperty;
2306 * Write back the parent property
2308 res = StorageImpl_WriteProperty(
2309 This->base.ancestorStorage,
2310 parentPropertyId,
2311 &parentProperty);
2312 if(! res)
2314 return E_FAIL;
2318 * If a placeholder is required for the other link, then, find one and
2319 * get out of here...
2321 if (needToFindAPlaceholder)
2323 hr = findPlaceholder(
2324 This,
2325 toStoreNode,
2326 storeNode,
2327 relationType);
2330 return hr;
2334 /******************************************************************************
2335 * SetElementTimes (IStorage)
2337 static HRESULT WINAPI StorageImpl_SetElementTimes(
2338 IStorage* iface,
2339 const OLECHAR *pwcsName,/* [string][in] */
2340 const FILETIME *pctime, /* [in] */
2341 const FILETIME *patime, /* [in] */
2342 const FILETIME *pmtime) /* [in] */
2344 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2345 return S_OK;
2348 /******************************************************************************
2349 * SetStateBits (IStorage)
2351 static HRESULT WINAPI StorageImpl_SetStateBits(
2352 IStorage* iface,
2353 DWORD grfStateBits,/* [in] */
2354 DWORD grfMask) /* [in] */
2356 StorageImpl* const This = (StorageImpl*)iface;
2357 This->base.stateBits = (This->base.stateBits & ~grfMask) | (grfStateBits & grfMask);
2358 return S_OK;
2362 * Virtual function table for the IStorage32Impl class.
2364 static const IStorageVtbl Storage32Impl_Vtbl =
2366 StorageBaseImpl_QueryInterface,
2367 StorageBaseImpl_AddRef,
2368 StorageBaseImpl_Release,
2369 StorageBaseImpl_CreateStream,
2370 StorageBaseImpl_OpenStream,
2371 StorageImpl_CreateStorage,
2372 StorageBaseImpl_OpenStorage,
2373 StorageImpl_CopyTo,
2374 StorageImpl_MoveElementTo,
2375 StorageImpl_Commit,
2376 StorageImpl_Revert,
2377 StorageBaseImpl_EnumElements,
2378 StorageImpl_DestroyElement,
2379 StorageBaseImpl_RenameElement,
2380 StorageImpl_SetElementTimes,
2381 StorageBaseImpl_SetClass,
2382 StorageImpl_SetStateBits,
2383 StorageImpl_Stat
2386 static HRESULT StorageImpl_Construct(
2387 StorageImpl* This,
2388 HANDLE hFile,
2389 LPCOLESTR pwcsName,
2390 ILockBytes* pLkbyt,
2391 DWORD openFlags,
2392 BOOL fileBased,
2393 BOOL fileCreate)
2395 HRESULT hr = S_OK;
2396 StgProperty currentProperty;
2397 BOOL readSuccessful;
2398 ULONG currentPropertyIndex;
2400 if ( FAILED( validateSTGM(openFlags) ))
2401 return STG_E_INVALIDFLAG;
2403 memset(This, 0, sizeof(StorageImpl));
2406 * Initialize stream list
2409 list_init(&This->base.strmHead);
2412 * Initialize the virtual function table.
2414 This->base.lpVtbl = &Storage32Impl_Vtbl;
2415 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2416 This->base.v_destructor = &StorageImpl_Destroy;
2417 This->base.openFlags = (openFlags & ~STGM_CREATE);
2420 * This is the top-level storage so initialize the ancestor pointer
2421 * to this.
2423 This->base.ancestorStorage = This;
2426 * Initialize the physical support of the storage.
2428 This->hFile = hFile;
2431 * Store copy of file path.
2433 if(pwcsName) {
2434 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2435 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2436 if (!This->pwcsName)
2437 return STG_E_INSUFFICIENTMEMORY;
2438 strcpyW(This->pwcsName, pwcsName);
2442 * Initialize the big block cache.
2444 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2445 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2446 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2447 pLkbyt,
2448 openFlags,
2449 This->bigBlockSize,
2450 fileBased);
2452 if (This->bigBlockFile == 0)
2453 return E_FAIL;
2455 if (fileCreate)
2457 ULARGE_INTEGER size;
2458 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2461 * Initialize all header variables:
2462 * - The big block depot consists of one block and it is at block 0
2463 * - The properties start at block 1
2464 * - There is no small block depot
2466 memset( This->bigBlockDepotStart,
2467 BLOCK_UNUSED,
2468 sizeof(This->bigBlockDepotStart));
2470 This->bigBlockDepotCount = 1;
2471 This->bigBlockDepotStart[0] = 0;
2472 This->rootStartBlock = 1;
2473 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2474 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2475 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2476 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2477 This->extBigBlockDepotCount = 0;
2479 StorageImpl_SaveFileHeader(This);
2482 * Add one block for the big block depot and one block for the properties
2484 size.u.HighPart = 0;
2485 size.u.LowPart = This->bigBlockSize * 3;
2486 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2489 * Initialize the big block depot
2491 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2492 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2493 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2494 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2496 else
2499 * Load the header for the file.
2501 hr = StorageImpl_LoadFileHeader(This);
2503 if (FAILED(hr))
2505 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2507 return hr;
2512 * There is no block depot cached yet.
2514 This->indexBlockDepotCached = 0xFFFFFFFF;
2517 * Start searching for free blocks with block 0.
2519 This->prevFreeBlock = 0;
2522 * Create the block chain abstractions.
2524 if(!(This->rootBlockChain =
2525 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2526 return STG_E_READFAULT;
2528 if(!(This->smallBlockDepotChain =
2529 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2530 PROPERTY_NULL)))
2531 return STG_E_READFAULT;
2534 * Write the root property (memory only)
2536 if (fileCreate)
2538 StgProperty rootProp;
2540 * Initialize the property chain
2542 memset(&rootProp, 0, sizeof(rootProp));
2543 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2544 sizeof(rootProp.name)/sizeof(WCHAR) );
2545 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2546 rootProp.propertyType = PROPTYPE_ROOT;
2547 rootProp.previousProperty = PROPERTY_NULL;
2548 rootProp.nextProperty = PROPERTY_NULL;
2549 rootProp.dirProperty = PROPERTY_NULL;
2550 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2551 rootProp.size.u.HighPart = 0;
2552 rootProp.size.u.LowPart = 0;
2554 StorageImpl_WriteProperty(This, 0, &rootProp);
2558 * Find the ID of the root in the property sets.
2560 currentPropertyIndex = 0;
2564 readSuccessful = StorageImpl_ReadProperty(
2565 This,
2566 currentPropertyIndex,
2567 &currentProperty);
2569 if (readSuccessful)
2571 if ( (currentProperty.sizeOfNameString != 0 ) &&
2572 (currentProperty.propertyType == PROPTYPE_ROOT) )
2574 This->base.rootPropertySetIndex = currentPropertyIndex;
2578 currentPropertyIndex++;
2580 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2582 if (!readSuccessful)
2584 /* TODO CLEANUP */
2585 return STG_E_READFAULT;
2589 * Create the block chain abstraction for the small block root chain.
2591 if(!(This->smallBlockRootChain =
2592 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2593 return STG_E_READFAULT;
2595 return hr;
2598 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2600 StorageImpl *This = (StorageImpl*) iface;
2601 TRACE("(%p)\n", This);
2603 StorageBaseImpl_DeleteAll(&This->base);
2605 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2607 BlockChainStream_Destroy(This->smallBlockRootChain);
2608 BlockChainStream_Destroy(This->rootBlockChain);
2609 BlockChainStream_Destroy(This->smallBlockDepotChain);
2611 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2612 HeapFree(GetProcessHeap(), 0, This);
2615 /******************************************************************************
2616 * Storage32Impl_GetNextFreeBigBlock
2618 * Returns the index of the next free big block.
2619 * If the big block depot is filled, this method will enlarge it.
2622 static ULONG StorageImpl_GetNextFreeBigBlock(
2623 StorageImpl* This)
2625 ULONG depotBlockIndexPos;
2626 BYTE depotBuffer[BIG_BLOCK_SIZE];
2627 BOOL success;
2628 ULONG depotBlockOffset;
2629 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2630 ULONG nextBlockIndex = BLOCK_SPECIAL;
2631 int depotIndex = 0;
2632 ULONG freeBlock = BLOCK_UNUSED;
2634 depotIndex = This->prevFreeBlock / blocksPerDepot;
2635 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2638 * Scan the entire big block depot until we find a block marked free
2640 while (nextBlockIndex != BLOCK_UNUSED)
2642 if (depotIndex < COUNT_BBDEPOTINHEADER)
2644 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2647 * Grow the primary depot.
2649 if (depotBlockIndexPos == BLOCK_UNUSED)
2651 depotBlockIndexPos = depotIndex*blocksPerDepot;
2654 * Add a block depot.
2656 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2657 This->bigBlockDepotCount++;
2658 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2661 * Flag it as a block depot.
2663 StorageImpl_SetNextBlockInChain(This,
2664 depotBlockIndexPos,
2665 BLOCK_SPECIAL);
2667 /* Save new header information.
2669 StorageImpl_SaveFileHeader(This);
2672 else
2674 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2676 if (depotBlockIndexPos == BLOCK_UNUSED)
2679 * Grow the extended depot.
2681 ULONG extIndex = BLOCK_UNUSED;
2682 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2683 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2685 if (extBlockOffset == 0)
2687 /* We need an extended block.
2689 extIndex = Storage32Impl_AddExtBlockDepot(This);
2690 This->extBigBlockDepotCount++;
2691 depotBlockIndexPos = extIndex + 1;
2693 else
2694 depotBlockIndexPos = depotIndex * blocksPerDepot;
2697 * Add a block depot and mark it in the extended block.
2699 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2700 This->bigBlockDepotCount++;
2701 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2703 /* Flag the block depot.
2705 StorageImpl_SetNextBlockInChain(This,
2706 depotBlockIndexPos,
2707 BLOCK_SPECIAL);
2709 /* If necessary, flag the extended depot block.
2711 if (extIndex != BLOCK_UNUSED)
2712 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2714 /* Save header information.
2716 StorageImpl_SaveFileHeader(This);
2720 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2722 if (success)
2724 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2725 ( nextBlockIndex != BLOCK_UNUSED))
2727 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2729 if (nextBlockIndex == BLOCK_UNUSED)
2731 freeBlock = (depotIndex * blocksPerDepot) +
2732 (depotBlockOffset/sizeof(ULONG));
2735 depotBlockOffset += sizeof(ULONG);
2739 depotIndex++;
2740 depotBlockOffset = 0;
2744 * make sure that the block physically exists before using it
2746 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2748 This->prevFreeBlock = freeBlock;
2750 return freeBlock;
2753 /******************************************************************************
2754 * Storage32Impl_AddBlockDepot
2756 * This will create a depot block, essentially it is a block initialized
2757 * to BLOCK_UNUSEDs.
2759 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2761 BYTE blockBuffer[BIG_BLOCK_SIZE];
2764 * Initialize blocks as free
2766 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2767 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2770 /******************************************************************************
2771 * Storage32Impl_GetExtDepotBlock
2773 * Returns the index of the block that corresponds to the specified depot
2774 * index. This method is only for depot indexes equal or greater than
2775 * COUNT_BBDEPOTINHEADER.
2777 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2779 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2780 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2781 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2782 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2783 ULONG blockIndex = BLOCK_UNUSED;
2784 ULONG extBlockIndex = This->extBigBlockDepotStart;
2786 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2788 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2789 return BLOCK_UNUSED;
2791 while (extBlockCount > 0)
2793 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2794 extBlockCount--;
2797 if (extBlockIndex != BLOCK_UNUSED)
2798 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2799 extBlockOffset * sizeof(ULONG), &blockIndex);
2801 return blockIndex;
2804 /******************************************************************************
2805 * Storage32Impl_SetExtDepotBlock
2807 * Associates the specified block index to the specified depot index.
2808 * This method is only for depot indexes equal or greater than
2809 * COUNT_BBDEPOTINHEADER.
2811 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2813 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2814 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2815 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2816 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2817 ULONG extBlockIndex = This->extBigBlockDepotStart;
2819 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2821 while (extBlockCount > 0)
2823 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2824 extBlockCount--;
2827 if (extBlockIndex != BLOCK_UNUSED)
2829 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2830 extBlockOffset * sizeof(ULONG),
2831 blockIndex);
2835 /******************************************************************************
2836 * Storage32Impl_AddExtBlockDepot
2838 * Creates an extended depot block.
2840 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2842 ULONG numExtBlocks = This->extBigBlockDepotCount;
2843 ULONG nextExtBlock = This->extBigBlockDepotStart;
2844 BYTE depotBuffer[BIG_BLOCK_SIZE];
2845 ULONG index = BLOCK_UNUSED;
2846 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2847 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2848 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2850 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2851 blocksPerDepotBlock;
2853 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2856 * The first extended block.
2858 This->extBigBlockDepotStart = index;
2860 else
2862 unsigned int i;
2864 * Follow the chain to the last one.
2866 for (i = 0; i < (numExtBlocks - 1); i++)
2868 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2872 * Add the new extended block to the chain.
2874 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2875 index);
2879 * Initialize this block.
2881 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2882 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2884 return index;
2887 /******************************************************************************
2888 * Storage32Impl_FreeBigBlock
2890 * This method will flag the specified block as free in the big block depot.
2892 static void StorageImpl_FreeBigBlock(
2893 StorageImpl* This,
2894 ULONG blockIndex)
2896 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2898 if (blockIndex < This->prevFreeBlock)
2899 This->prevFreeBlock = blockIndex;
2902 /************************************************************************
2903 * Storage32Impl_GetNextBlockInChain
2905 * This method will retrieve the block index of the next big block in
2906 * in the chain.
2908 * Params: This - Pointer to the Storage object.
2909 * blockIndex - Index of the block to retrieve the chain
2910 * for.
2911 * nextBlockIndex - receives the return value.
2913 * Returns: This method returns the index of the next block in the chain.
2914 * It will return the constants:
2915 * BLOCK_SPECIAL - If the block given was not part of a
2916 * chain.
2917 * BLOCK_END_OF_CHAIN - If the block given was the last in
2918 * a chain.
2919 * BLOCK_UNUSED - If the block given was not past of a chain
2920 * and is available.
2921 * BLOCK_EXTBBDEPOT - This block is part of the extended
2922 * big block depot.
2924 * See Windows documentation for more details on IStorage methods.
2926 static HRESULT StorageImpl_GetNextBlockInChain(
2927 StorageImpl* This,
2928 ULONG blockIndex,
2929 ULONG* nextBlockIndex)
2931 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2932 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2933 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2934 BYTE depotBuffer[BIG_BLOCK_SIZE];
2935 BOOL success;
2936 ULONG depotBlockIndexPos;
2937 int index;
2939 *nextBlockIndex = BLOCK_SPECIAL;
2941 if(depotBlockCount >= This->bigBlockDepotCount)
2943 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2944 This->bigBlockDepotCount);
2945 return STG_E_READFAULT;
2949 * Cache the currently accessed depot block.
2951 if (depotBlockCount != This->indexBlockDepotCached)
2953 This->indexBlockDepotCached = depotBlockCount;
2955 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2957 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2959 else
2962 * We have to look in the extended depot.
2964 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2967 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2969 if (!success)
2970 return STG_E_READFAULT;
2972 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2974 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2975 This->blockDepotCached[index] = *nextBlockIndex;
2979 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2981 return S_OK;
2984 /******************************************************************************
2985 * Storage32Impl_GetNextExtendedBlock
2987 * Given an extended block this method will return the next extended block.
2989 * NOTES:
2990 * The last ULONG of an extended block is the block index of the next
2991 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2992 * depot.
2994 * Return values:
2995 * - The index of the next extended block
2996 * - BLOCK_UNUSED: there is no next extended block.
2997 * - Any other return values denotes failure.
2999 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
3001 ULONG nextBlockIndex = BLOCK_SPECIAL;
3002 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
3004 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
3005 &nextBlockIndex);
3007 return nextBlockIndex;
3010 /******************************************************************************
3011 * Storage32Impl_SetNextBlockInChain
3013 * This method will write the index of the specified block's next block
3014 * in the big block depot.
3016 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3017 * do the following
3019 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3020 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3021 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3024 static void StorageImpl_SetNextBlockInChain(
3025 StorageImpl* This,
3026 ULONG blockIndex,
3027 ULONG nextBlock)
3029 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3030 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3031 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3032 ULONG depotBlockIndexPos;
3034 assert(depotBlockCount < This->bigBlockDepotCount);
3035 assert(blockIndex != nextBlock);
3037 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3039 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3041 else
3044 * We have to look in the extended depot.
3046 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3049 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
3050 nextBlock);
3052 * Update the cached block depot, if necessary.
3054 if (depotBlockCount == This->indexBlockDepotCached)
3056 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3060 /******************************************************************************
3061 * Storage32Impl_LoadFileHeader
3063 * This method will read in the file header, i.e. big block index -1.
3065 static HRESULT StorageImpl_LoadFileHeader(
3066 StorageImpl* This)
3068 HRESULT hr = STG_E_FILENOTFOUND;
3069 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3070 BOOL success;
3071 int index;
3073 TRACE("\n");
3075 * Get a pointer to the big block of data containing the header.
3077 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3080 * Extract the information from the header.
3082 if (success)
3085 * Check for the "magic number" signature and return an error if it is not
3086 * found.
3088 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3090 return STG_E_OLDFORMAT;
3093 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3095 return STG_E_INVALIDHEADER;
3098 StorageUtl_ReadWord(
3099 headerBigBlock,
3100 OFFSET_BIGBLOCKSIZEBITS,
3101 &This->bigBlockSizeBits);
3103 StorageUtl_ReadWord(
3104 headerBigBlock,
3105 OFFSET_SMALLBLOCKSIZEBITS,
3106 &This->smallBlockSizeBits);
3108 StorageUtl_ReadDWord(
3109 headerBigBlock,
3110 OFFSET_BBDEPOTCOUNT,
3111 &This->bigBlockDepotCount);
3113 StorageUtl_ReadDWord(
3114 headerBigBlock,
3115 OFFSET_ROOTSTARTBLOCK,
3116 &This->rootStartBlock);
3118 StorageUtl_ReadDWord(
3119 headerBigBlock,
3120 OFFSET_SBDEPOTSTART,
3121 &This->smallBlockDepotStart);
3123 StorageUtl_ReadDWord(
3124 headerBigBlock,
3125 OFFSET_EXTBBDEPOTSTART,
3126 &This->extBigBlockDepotStart);
3128 StorageUtl_ReadDWord(
3129 headerBigBlock,
3130 OFFSET_EXTBBDEPOTCOUNT,
3131 &This->extBigBlockDepotCount);
3133 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3135 StorageUtl_ReadDWord(
3136 headerBigBlock,
3137 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3138 &(This->bigBlockDepotStart[index]));
3142 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3144 if ((1 << 2) == 4)
3146 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3147 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3149 else
3151 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3152 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3156 * Right now, the code is making some assumptions about the size of the
3157 * blocks, just make sure they are what we're expecting.
3159 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3160 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3162 WARN("Broken OLE storage file\n");
3163 hr = STG_E_INVALIDHEADER;
3165 else
3166 hr = S_OK;
3169 return hr;
3172 /******************************************************************************
3173 * Storage32Impl_SaveFileHeader
3175 * This method will save to the file the header, i.e. big block -1.
3177 static void StorageImpl_SaveFileHeader(
3178 StorageImpl* This)
3180 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3181 int index;
3182 BOOL success;
3185 * Get a pointer to the big block of data containing the header.
3187 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3190 * If the block read failed, the file is probably new.
3192 if (!success)
3195 * Initialize for all unknown fields.
3197 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3200 * Initialize the magic number.
3202 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3205 * And a bunch of things we don't know what they mean
3207 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3208 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3209 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3210 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3214 * Write the information to the header.
3216 StorageUtl_WriteWord(
3217 headerBigBlock,
3218 OFFSET_BIGBLOCKSIZEBITS,
3219 This->bigBlockSizeBits);
3221 StorageUtl_WriteWord(
3222 headerBigBlock,
3223 OFFSET_SMALLBLOCKSIZEBITS,
3224 This->smallBlockSizeBits);
3226 StorageUtl_WriteDWord(
3227 headerBigBlock,
3228 OFFSET_BBDEPOTCOUNT,
3229 This->bigBlockDepotCount);
3231 StorageUtl_WriteDWord(
3232 headerBigBlock,
3233 OFFSET_ROOTSTARTBLOCK,
3234 This->rootStartBlock);
3236 StorageUtl_WriteDWord(
3237 headerBigBlock,
3238 OFFSET_SBDEPOTSTART,
3239 This->smallBlockDepotStart);
3241 StorageUtl_WriteDWord(
3242 headerBigBlock,
3243 OFFSET_SBDEPOTCOUNT,
3244 This->smallBlockDepotChain ?
3245 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3247 StorageUtl_WriteDWord(
3248 headerBigBlock,
3249 OFFSET_EXTBBDEPOTSTART,
3250 This->extBigBlockDepotStart);
3252 StorageUtl_WriteDWord(
3253 headerBigBlock,
3254 OFFSET_EXTBBDEPOTCOUNT,
3255 This->extBigBlockDepotCount);
3257 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3259 StorageUtl_WriteDWord(
3260 headerBigBlock,
3261 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3262 (This->bigBlockDepotStart[index]));
3266 * Write the big block back to the file.
3268 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3271 /******************************************************************************
3272 * Storage32Impl_ReadProperty
3274 * This method will read the specified property from the property chain.
3276 BOOL StorageImpl_ReadProperty(
3277 StorageImpl* This,
3278 ULONG index,
3279 StgProperty* buffer)
3281 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3282 ULARGE_INTEGER offsetInPropSet;
3283 HRESULT readRes;
3284 ULONG bytesRead;
3286 offsetInPropSet.u.HighPart = 0;
3287 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3289 readRes = BlockChainStream_ReadAt(
3290 This->rootBlockChain,
3291 offsetInPropSet,
3292 PROPSET_BLOCK_SIZE,
3293 currentProperty,
3294 &bytesRead);
3296 if (SUCCEEDED(readRes))
3298 /* replace the name of root entry (often "Root Entry") by the file name */
3299 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3300 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3302 memset(buffer->name, 0, sizeof(buffer->name));
3303 memcpy(
3304 buffer->name,
3305 propName,
3306 PROPERTY_NAME_BUFFER_LEN );
3307 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3309 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3311 StorageUtl_ReadWord(
3312 currentProperty,
3313 OFFSET_PS_NAMELENGTH,
3314 &buffer->sizeOfNameString);
3316 StorageUtl_ReadDWord(
3317 currentProperty,
3318 OFFSET_PS_PREVIOUSPROP,
3319 &buffer->previousProperty);
3321 StorageUtl_ReadDWord(
3322 currentProperty,
3323 OFFSET_PS_NEXTPROP,
3324 &buffer->nextProperty);
3326 StorageUtl_ReadDWord(
3327 currentProperty,
3328 OFFSET_PS_DIRPROP,
3329 &buffer->dirProperty);
3331 StorageUtl_ReadGUID(
3332 currentProperty,
3333 OFFSET_PS_GUID,
3334 &buffer->propertyUniqueID);
3336 StorageUtl_ReadDWord(
3337 currentProperty,
3338 OFFSET_PS_TSS1,
3339 &buffer->timeStampS1);
3341 StorageUtl_ReadDWord(
3342 currentProperty,
3343 OFFSET_PS_TSD1,
3344 &buffer->timeStampD1);
3346 StorageUtl_ReadDWord(
3347 currentProperty,
3348 OFFSET_PS_TSS2,
3349 &buffer->timeStampS2);
3351 StorageUtl_ReadDWord(
3352 currentProperty,
3353 OFFSET_PS_TSD2,
3354 &buffer->timeStampD2);
3356 StorageUtl_ReadDWord(
3357 currentProperty,
3358 OFFSET_PS_STARTBLOCK,
3359 &buffer->startingBlock);
3361 StorageUtl_ReadDWord(
3362 currentProperty,
3363 OFFSET_PS_SIZE,
3364 &buffer->size.u.LowPart);
3366 buffer->size.u.HighPart = 0;
3369 return SUCCEEDED(readRes) ? TRUE : FALSE;
3372 /*********************************************************************
3373 * Write the specified property into the property chain
3375 BOOL StorageImpl_WriteProperty(
3376 StorageImpl* This,
3377 ULONG index,
3378 StgProperty* buffer)
3380 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3381 ULARGE_INTEGER offsetInPropSet;
3382 HRESULT writeRes;
3383 ULONG bytesWritten;
3385 offsetInPropSet.u.HighPart = 0;
3386 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3388 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3390 memcpy(
3391 currentProperty + OFFSET_PS_NAME,
3392 buffer->name,
3393 PROPERTY_NAME_BUFFER_LEN );
3395 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3397 StorageUtl_WriteWord(
3398 currentProperty,
3399 OFFSET_PS_NAMELENGTH,
3400 buffer->sizeOfNameString);
3402 StorageUtl_WriteDWord(
3403 currentProperty,
3404 OFFSET_PS_PREVIOUSPROP,
3405 buffer->previousProperty);
3407 StorageUtl_WriteDWord(
3408 currentProperty,
3409 OFFSET_PS_NEXTPROP,
3410 buffer->nextProperty);
3412 StorageUtl_WriteDWord(
3413 currentProperty,
3414 OFFSET_PS_DIRPROP,
3415 buffer->dirProperty);
3417 StorageUtl_WriteGUID(
3418 currentProperty,
3419 OFFSET_PS_GUID,
3420 &buffer->propertyUniqueID);
3422 StorageUtl_WriteDWord(
3423 currentProperty,
3424 OFFSET_PS_TSS1,
3425 buffer->timeStampS1);
3427 StorageUtl_WriteDWord(
3428 currentProperty,
3429 OFFSET_PS_TSD1,
3430 buffer->timeStampD1);
3432 StorageUtl_WriteDWord(
3433 currentProperty,
3434 OFFSET_PS_TSS2,
3435 buffer->timeStampS2);
3437 StorageUtl_WriteDWord(
3438 currentProperty,
3439 OFFSET_PS_TSD2,
3440 buffer->timeStampD2);
3442 StorageUtl_WriteDWord(
3443 currentProperty,
3444 OFFSET_PS_STARTBLOCK,
3445 buffer->startingBlock);
3447 StorageUtl_WriteDWord(
3448 currentProperty,
3449 OFFSET_PS_SIZE,
3450 buffer->size.u.LowPart);
3452 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3453 offsetInPropSet,
3454 PROPSET_BLOCK_SIZE,
3455 currentProperty,
3456 &bytesWritten);
3457 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3460 static BOOL StorageImpl_ReadBigBlock(
3461 StorageImpl* This,
3462 ULONG blockIndex,
3463 void* buffer)
3465 ULARGE_INTEGER ulOffset;
3466 DWORD read;
3468 ulOffset.u.HighPart = 0;
3469 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3471 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3472 return (read == This->bigBlockSize);
3475 static BOOL StorageImpl_ReadDWordFromBigBlock(
3476 StorageImpl* This,
3477 ULONG blockIndex,
3478 ULONG offset,
3479 DWORD* value)
3481 ULARGE_INTEGER ulOffset;
3482 DWORD read;
3483 DWORD tmp;
3485 ulOffset.u.HighPart = 0;
3486 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3487 ulOffset.u.LowPart += offset;
3489 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3490 *value = le32toh(tmp);
3491 return (read == sizeof(DWORD));
3494 static BOOL StorageImpl_WriteBigBlock(
3495 StorageImpl* This,
3496 ULONG blockIndex,
3497 void* buffer)
3499 ULARGE_INTEGER ulOffset;
3500 DWORD wrote;
3502 ulOffset.u.HighPart = 0;
3503 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3505 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3506 return (wrote == This->bigBlockSize);
3509 static BOOL StorageImpl_WriteDWordToBigBlock(
3510 StorageImpl* This,
3511 ULONG blockIndex,
3512 ULONG offset,
3513 DWORD value)
3515 ULARGE_INTEGER ulOffset;
3516 DWORD wrote;
3518 ulOffset.u.HighPart = 0;
3519 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3520 ulOffset.u.LowPart += offset;
3522 value = htole32(value);
3523 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3524 return (wrote == sizeof(DWORD));
3527 /******************************************************************************
3528 * Storage32Impl_SmallBlocksToBigBlocks
3530 * This method will convert a small block chain to a big block chain.
3531 * The small block chain will be destroyed.
3533 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3534 StorageImpl* This,
3535 SmallBlockChainStream** ppsbChain)
3537 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3538 ULARGE_INTEGER size, offset;
3539 ULONG cbRead, cbWritten;
3540 ULARGE_INTEGER cbTotalRead;
3541 ULONG propertyIndex;
3542 HRESULT resWrite = S_OK;
3543 HRESULT resRead;
3544 StgProperty chainProperty;
3545 BYTE *buffer;
3546 BlockChainStream *bbTempChain = NULL;
3547 BlockChainStream *bigBlockChain = NULL;
3550 * Create a temporary big block chain that doesn't have
3551 * an associated property. This temporary chain will be
3552 * used to copy data from small blocks to big blocks.
3554 bbTempChain = BlockChainStream_Construct(This,
3555 &bbHeadOfChain,
3556 PROPERTY_NULL);
3557 if(!bbTempChain) return NULL;
3559 * Grow the big block chain.
3561 size = SmallBlockChainStream_GetSize(*ppsbChain);
3562 BlockChainStream_SetSize(bbTempChain, size);
3565 * Copy the contents of the small block chain to the big block chain
3566 * by small block size increments.
3568 offset.u.LowPart = 0;
3569 offset.u.HighPart = 0;
3570 cbTotalRead.QuadPart = 0;
3572 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3575 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3576 offset,
3577 This->smallBlockSize,
3578 buffer,
3579 &cbRead);
3580 if (FAILED(resRead))
3581 break;
3583 if (cbRead > 0)
3585 cbTotalRead.QuadPart += cbRead;
3587 resWrite = BlockChainStream_WriteAt(bbTempChain,
3588 offset,
3589 cbRead,
3590 buffer,
3591 &cbWritten);
3593 if (FAILED(resWrite))
3594 break;
3596 offset.u.LowPart += This->smallBlockSize;
3598 } while (cbTotalRead.QuadPart < size.QuadPart);
3599 HeapFree(GetProcessHeap(),0,buffer);
3601 if (FAILED(resRead) || FAILED(resWrite))
3603 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3604 BlockChainStream_Destroy(bbTempChain);
3605 return NULL;
3609 * Destroy the small block chain.
3611 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3612 size.u.HighPart = 0;
3613 size.u.LowPart = 0;
3614 SmallBlockChainStream_SetSize(*ppsbChain, size);
3615 SmallBlockChainStream_Destroy(*ppsbChain);
3616 *ppsbChain = 0;
3619 * Change the property information. This chain is now a big block chain
3620 * and it doesn't reside in the small blocks chain anymore.
3622 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3624 chainProperty.startingBlock = bbHeadOfChain;
3626 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3629 * Destroy the temporary propertyless big block chain.
3630 * Create a new big block chain associated with this property.
3632 BlockChainStream_Destroy(bbTempChain);
3633 bigBlockChain = BlockChainStream_Construct(This,
3634 NULL,
3635 propertyIndex);
3637 return bigBlockChain;
3640 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3642 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3644 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3645 HeapFree(GetProcessHeap(), 0, This);
3648 /******************************************************************************
3650 ** Storage32InternalImpl_Commit
3652 ** The non-root storages cannot be opened in transacted mode thus this function
3653 ** does nothing.
3655 static HRESULT WINAPI StorageInternalImpl_Commit(
3656 IStorage* iface,
3657 DWORD grfCommitFlags) /* [in] */
3659 return S_OK;
3662 /******************************************************************************
3664 ** Storage32InternalImpl_Revert
3666 ** The non-root storages cannot be opened in transacted mode thus this function
3667 ** does nothing.
3669 static HRESULT WINAPI StorageInternalImpl_Revert(
3670 IStorage* iface)
3672 return S_OK;
3675 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3677 IStorage_Release((IStorage*)This->parentStorage);
3678 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3679 HeapFree(GetProcessHeap(), 0, This);
3682 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3683 IEnumSTATSTG* iface,
3684 REFIID riid,
3685 void** ppvObject)
3687 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3690 * Perform a sanity check on the parameters.
3692 if (ppvObject==0)
3693 return E_INVALIDARG;
3696 * Initialize the return parameter.
3698 *ppvObject = 0;
3701 * Compare the riid with the interface IDs implemented by this object.
3703 if (IsEqualGUID(&IID_IUnknown, riid) ||
3704 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3706 *ppvObject = (IEnumSTATSTG*)This;
3707 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3708 return S_OK;
3711 return E_NOINTERFACE;
3714 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3715 IEnumSTATSTG* iface)
3717 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3718 return InterlockedIncrement(&This->ref);
3721 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3722 IEnumSTATSTG* iface)
3724 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3726 ULONG newRef;
3728 newRef = InterlockedDecrement(&This->ref);
3731 * If the reference count goes down to 0, perform suicide.
3733 if (newRef==0)
3735 IEnumSTATSTGImpl_Destroy(This);
3738 return newRef;
3741 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3742 IEnumSTATSTG* iface,
3743 ULONG celt,
3744 STATSTG* rgelt,
3745 ULONG* pceltFetched)
3747 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3749 StgProperty currentProperty;
3750 STATSTG* currentReturnStruct = rgelt;
3751 ULONG objectFetched = 0;
3752 ULONG currentSearchNode;
3755 * Perform a sanity check on the parameters.
3757 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3758 return E_INVALIDARG;
3761 * To avoid the special case, get another pointer to a ULONG value if
3762 * the caller didn't supply one.
3764 if (pceltFetched==0)
3765 pceltFetched = &objectFetched;
3768 * Start the iteration, we will iterate until we hit the end of the
3769 * linked list or until we hit the number of items to iterate through
3771 *pceltFetched = 0;
3774 * Start with the node at the top of the stack.
3776 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3778 while ( ( *pceltFetched < celt) &&
3779 ( currentSearchNode!=PROPERTY_NULL) )
3782 * Remove the top node from the stack
3784 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3787 * Read the property from the storage.
3789 StorageImpl_ReadProperty(This->parentStorage,
3790 currentSearchNode,
3791 &currentProperty);
3794 * Copy the information to the return buffer.
3796 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3797 &currentProperty,
3798 STATFLAG_DEFAULT);
3801 * Step to the next item in the iteration
3803 (*pceltFetched)++;
3804 currentReturnStruct++;
3807 * Push the next search node in the search stack.
3809 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3812 * continue the iteration.
3814 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3817 if (*pceltFetched == celt)
3818 return S_OK;
3820 return S_FALSE;
3824 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3825 IEnumSTATSTG* iface,
3826 ULONG celt)
3828 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3830 StgProperty currentProperty;
3831 ULONG objectFetched = 0;
3832 ULONG currentSearchNode;
3835 * Start with the node at the top of the stack.
3837 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3839 while ( (objectFetched < celt) &&
3840 (currentSearchNode!=PROPERTY_NULL) )
3843 * Remove the top node from the stack
3845 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3848 * Read the property from the storage.
3850 StorageImpl_ReadProperty(This->parentStorage,
3851 currentSearchNode,
3852 &currentProperty);
3855 * Step to the next item in the iteration
3857 objectFetched++;
3860 * Push the next search node in the search stack.
3862 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3865 * continue the iteration.
3867 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3870 if (objectFetched == celt)
3871 return S_OK;
3873 return S_FALSE;
3876 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3877 IEnumSTATSTG* iface)
3879 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3881 StgProperty rootProperty;
3882 BOOL readSuccessful;
3885 * Re-initialize the search stack to an empty stack
3887 This->stackSize = 0;
3890 * Read the root property from the storage.
3892 readSuccessful = StorageImpl_ReadProperty(
3893 This->parentStorage,
3894 This->firstPropertyNode,
3895 &rootProperty);
3897 if (readSuccessful)
3899 assert(rootProperty.sizeOfNameString!=0);
3902 * Push the search node in the search stack.
3904 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3907 return S_OK;
3910 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3911 IEnumSTATSTG* iface,
3912 IEnumSTATSTG** ppenum)
3914 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3916 IEnumSTATSTGImpl* newClone;
3919 * Perform a sanity check on the parameters.
3921 if (ppenum==0)
3922 return E_INVALIDARG;
3924 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3925 This->firstPropertyNode);
3929 * The new clone enumeration must point to the same current node as
3930 * the ole one.
3932 newClone->stackSize = This->stackSize ;
3933 newClone->stackMaxSize = This->stackMaxSize ;
3934 newClone->stackToVisit =
3935 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3937 memcpy(
3938 newClone->stackToVisit,
3939 This->stackToVisit,
3940 sizeof(ULONG) * newClone->stackSize);
3942 *ppenum = (IEnumSTATSTG*)newClone;
3945 * Don't forget to nail down a reference to the clone before
3946 * returning it.
3948 IEnumSTATSTGImpl_AddRef(*ppenum);
3950 return S_OK;
3953 static INT IEnumSTATSTGImpl_FindParentProperty(
3954 IEnumSTATSTGImpl *This,
3955 ULONG childProperty,
3956 StgProperty *currentProperty,
3957 ULONG *thisNodeId)
3959 ULONG currentSearchNode;
3960 ULONG foundNode;
3963 * To avoid the special case, get another pointer to a ULONG value if
3964 * the caller didn't supply one.
3966 if (thisNodeId==0)
3967 thisNodeId = &foundNode;
3970 * Start with the node at the top of the stack.
3972 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3975 while (currentSearchNode!=PROPERTY_NULL)
3978 * Store the current node in the returned parameters
3980 *thisNodeId = currentSearchNode;
3983 * Remove the top node from the stack
3985 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3988 * Read the property from the storage.
3990 StorageImpl_ReadProperty(
3991 This->parentStorage,
3992 currentSearchNode,
3993 currentProperty);
3995 if (currentProperty->previousProperty == childProperty)
3996 return PROPERTY_RELATION_PREVIOUS;
3998 else if (currentProperty->nextProperty == childProperty)
3999 return PROPERTY_RELATION_NEXT;
4001 else if (currentProperty->dirProperty == childProperty)
4002 return PROPERTY_RELATION_DIR;
4005 * Push the next search node in the search stack.
4007 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4010 * continue the iteration.
4012 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4015 return PROPERTY_NULL;
4018 static ULONG IEnumSTATSTGImpl_FindProperty(
4019 IEnumSTATSTGImpl* This,
4020 const OLECHAR* lpszPropName,
4021 StgProperty* currentProperty)
4023 ULONG currentSearchNode;
4026 * Start with the node at the top of the stack.
4028 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4030 while (currentSearchNode!=PROPERTY_NULL)
4033 * Remove the top node from the stack
4035 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4038 * Read the property from the storage.
4040 StorageImpl_ReadProperty(This->parentStorage,
4041 currentSearchNode,
4042 currentProperty);
4044 if ( propertyNameCmp(
4045 (const OLECHAR*)currentProperty->name,
4046 (const OLECHAR*)lpszPropName) == 0)
4047 return currentSearchNode;
4050 * Push the next search node in the search stack.
4052 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4055 * continue the iteration.
4057 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4060 return PROPERTY_NULL;
4063 static void IEnumSTATSTGImpl_PushSearchNode(
4064 IEnumSTATSTGImpl* This,
4065 ULONG nodeToPush)
4067 StgProperty rootProperty;
4068 BOOL readSuccessful;
4071 * First, make sure we're not trying to push an unexisting node.
4073 if (nodeToPush==PROPERTY_NULL)
4074 return;
4077 * First push the node to the stack
4079 if (This->stackSize == This->stackMaxSize)
4081 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4083 This->stackToVisit = HeapReAlloc(
4084 GetProcessHeap(),
4086 This->stackToVisit,
4087 sizeof(ULONG) * This->stackMaxSize);
4090 This->stackToVisit[This->stackSize] = nodeToPush;
4091 This->stackSize++;
4094 * Read the root property from the storage.
4096 readSuccessful = StorageImpl_ReadProperty(
4097 This->parentStorage,
4098 nodeToPush,
4099 &rootProperty);
4101 if (readSuccessful)
4103 assert(rootProperty.sizeOfNameString!=0);
4106 * Push the previous search node in the search stack.
4108 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4112 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4113 IEnumSTATSTGImpl* This,
4114 BOOL remove)
4116 ULONG topNode;
4118 if (This->stackSize == 0)
4119 return PROPERTY_NULL;
4121 topNode = This->stackToVisit[This->stackSize-1];
4123 if (remove)
4124 This->stackSize--;
4126 return topNode;
4130 * Virtual function table for the IEnumSTATSTGImpl class.
4132 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4134 IEnumSTATSTGImpl_QueryInterface,
4135 IEnumSTATSTGImpl_AddRef,
4136 IEnumSTATSTGImpl_Release,
4137 IEnumSTATSTGImpl_Next,
4138 IEnumSTATSTGImpl_Skip,
4139 IEnumSTATSTGImpl_Reset,
4140 IEnumSTATSTGImpl_Clone
4143 /******************************************************************************
4144 ** IEnumSTATSTGImpl implementation
4147 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4148 StorageImpl* parentStorage,
4149 ULONG firstPropertyNode)
4151 IEnumSTATSTGImpl* newEnumeration;
4153 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4155 if (newEnumeration!=0)
4158 * Set-up the virtual function table and reference count.
4160 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4161 newEnumeration->ref = 0;
4164 * We want to nail-down the reference to the storage in case the
4165 * enumeration out-lives the storage in the client application.
4167 newEnumeration->parentStorage = parentStorage;
4168 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4170 newEnumeration->firstPropertyNode = firstPropertyNode;
4173 * Initialize the search stack
4175 newEnumeration->stackSize = 0;
4176 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4177 newEnumeration->stackToVisit =
4178 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4181 * Make sure the current node of the iterator is the first one.
4183 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4186 return newEnumeration;
4190 * Virtual function table for the Storage32InternalImpl class.
4192 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4194 StorageBaseImpl_QueryInterface,
4195 StorageBaseImpl_AddRef,
4196 StorageBaseImpl_Release,
4197 StorageBaseImpl_CreateStream,
4198 StorageBaseImpl_OpenStream,
4199 StorageImpl_CreateStorage,
4200 StorageBaseImpl_OpenStorage,
4201 StorageImpl_CopyTo,
4202 StorageImpl_MoveElementTo,
4203 StorageInternalImpl_Commit,
4204 StorageInternalImpl_Revert,
4205 StorageBaseImpl_EnumElements,
4206 StorageImpl_DestroyElement,
4207 StorageBaseImpl_RenameElement,
4208 StorageImpl_SetElementTimes,
4209 StorageBaseImpl_SetClass,
4210 StorageImpl_SetStateBits,
4211 StorageBaseImpl_Stat
4214 /******************************************************************************
4215 ** Storage32InternalImpl implementation
4218 static StorageInternalImpl* StorageInternalImpl_Construct(
4219 StorageImpl* ancestorStorage,
4220 DWORD openFlags,
4221 ULONG rootPropertyIndex)
4223 StorageInternalImpl* newStorage;
4226 * Allocate space for the new storage object
4228 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
4230 if (newStorage!=0)
4233 * Initialize the stream list
4235 list_init(&newStorage->base.strmHead);
4238 * Initialize the virtual function table.
4240 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4241 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4242 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4245 * Keep the ancestor storage pointer and nail a reference to it.
4247 newStorage->base.ancestorStorage = ancestorStorage;
4248 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4251 * Keep the index of the root property set for this storage,
4253 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4255 return newStorage;
4258 return 0;
4261 /******************************************************************************
4262 ** StorageUtl implementation
4265 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4267 WORD tmp;
4269 memcpy(&tmp, buffer+offset, sizeof(WORD));
4270 *value = le16toh(tmp);
4273 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4275 value = htole16(value);
4276 memcpy(buffer+offset, &value, sizeof(WORD));
4279 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4281 DWORD tmp;
4283 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4284 *value = le32toh(tmp);
4287 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4289 value = htole32(value);
4290 memcpy(buffer+offset, &value, sizeof(DWORD));
4293 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4294 ULARGE_INTEGER* value)
4296 #ifdef WORDS_BIGENDIAN
4297 ULARGE_INTEGER tmp;
4299 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4300 value->u.LowPart = htole32(tmp.u.HighPart);
4301 value->u.HighPart = htole32(tmp.u.LowPart);
4302 #else
4303 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4304 #endif
4307 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4308 const ULARGE_INTEGER *value)
4310 #ifdef WORDS_BIGENDIAN
4311 ULARGE_INTEGER tmp;
4313 tmp.u.LowPart = htole32(value->u.HighPart);
4314 tmp.u.HighPart = htole32(value->u.LowPart);
4315 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4316 #else
4317 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4318 #endif
4321 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4323 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4324 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4325 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4327 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4330 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4332 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4333 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4334 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4336 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4339 void StorageUtl_CopyPropertyToSTATSTG(
4340 STATSTG* destination,
4341 StgProperty* source,
4342 int statFlags)
4345 * The copy of the string occurs only when the flag is not set
4347 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4348 (source->name == NULL) ||
4349 (source->name[0] == 0) )
4351 destination->pwcsName = 0;
4353 else
4355 destination->pwcsName =
4356 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4358 strcpyW((LPWSTR)destination->pwcsName, source->name);
4361 switch (source->propertyType)
4363 case PROPTYPE_STORAGE:
4364 case PROPTYPE_ROOT:
4365 destination->type = STGTY_STORAGE;
4366 break;
4367 case PROPTYPE_STREAM:
4368 destination->type = STGTY_STREAM;
4369 break;
4370 default:
4371 destination->type = STGTY_STREAM;
4372 break;
4375 destination->cbSize = source->size;
4377 currentReturnStruct->mtime = {0}; TODO
4378 currentReturnStruct->ctime = {0};
4379 currentReturnStruct->atime = {0};
4381 destination->grfMode = 0;
4382 destination->grfLocksSupported = 0;
4383 destination->clsid = source->propertyUniqueID;
4384 destination->grfStateBits = 0;
4385 destination->reserved = 0;
4388 /******************************************************************************
4389 ** BlockChainStream implementation
4392 BlockChainStream* BlockChainStream_Construct(
4393 StorageImpl* parentStorage,
4394 ULONG* headOfStreamPlaceHolder,
4395 ULONG propertyIndex)
4397 BlockChainStream* newStream;
4398 ULONG blockIndex;
4400 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4402 newStream->parentStorage = parentStorage;
4403 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4404 newStream->ownerPropertyIndex = propertyIndex;
4405 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4406 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4407 newStream->numBlocks = 0;
4409 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4411 while (blockIndex != BLOCK_END_OF_CHAIN)
4413 newStream->numBlocks++;
4414 newStream->tailIndex = blockIndex;
4416 if(FAILED(StorageImpl_GetNextBlockInChain(
4417 parentStorage,
4418 blockIndex,
4419 &blockIndex)))
4421 HeapFree(GetProcessHeap(), 0, newStream);
4422 return NULL;
4426 return newStream;
4429 void BlockChainStream_Destroy(BlockChainStream* This)
4431 HeapFree(GetProcessHeap(), 0, This);
4434 /******************************************************************************
4435 * BlockChainStream_GetHeadOfChain
4437 * Returns the head of this stream chain.
4438 * Some special chains don't have properties, their heads are kept in
4439 * This->headOfStreamPlaceHolder.
4442 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4444 StgProperty chainProperty;
4445 BOOL readSuccessful;
4447 if (This->headOfStreamPlaceHolder != 0)
4448 return *(This->headOfStreamPlaceHolder);
4450 if (This->ownerPropertyIndex != PROPERTY_NULL)
4452 readSuccessful = StorageImpl_ReadProperty(
4453 This->parentStorage,
4454 This->ownerPropertyIndex,
4455 &chainProperty);
4457 if (readSuccessful)
4459 return chainProperty.startingBlock;
4463 return BLOCK_END_OF_CHAIN;
4466 /******************************************************************************
4467 * BlockChainStream_GetCount
4469 * Returns the number of blocks that comprises this chain.
4470 * This is not the size of the stream as the last block may not be full!
4473 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4475 ULONG blockIndex;
4476 ULONG count = 0;
4478 blockIndex = BlockChainStream_GetHeadOfChain(This);
4480 while (blockIndex != BLOCK_END_OF_CHAIN)
4482 count++;
4484 if(FAILED(StorageImpl_GetNextBlockInChain(
4485 This->parentStorage,
4486 blockIndex,
4487 &blockIndex)))
4488 return 0;
4491 return count;
4494 /******************************************************************************
4495 * BlockChainStream_ReadAt
4497 * Reads a specified number of bytes from this chain at the specified offset.
4498 * bytesRead may be NULL.
4499 * Failure will be returned if the specified number of bytes has not been read.
4501 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4502 ULARGE_INTEGER offset,
4503 ULONG size,
4504 void* buffer,
4505 ULONG* bytesRead)
4507 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4508 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4509 ULONG bytesToReadInBuffer;
4510 ULONG blockIndex;
4511 BYTE* bufferWalker;
4513 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4516 * Find the first block in the stream that contains part of the buffer.
4518 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4519 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4520 (blockNoInSequence < This->lastBlockNoInSequence) )
4522 blockIndex = BlockChainStream_GetHeadOfChain(This);
4523 This->lastBlockNoInSequence = blockNoInSequence;
4525 else
4527 ULONG temp = blockNoInSequence;
4529 blockIndex = This->lastBlockNoInSequenceIndex;
4530 blockNoInSequence -= This->lastBlockNoInSequence;
4531 This->lastBlockNoInSequence = temp;
4534 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4536 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4537 return STG_E_DOCFILECORRUPT;
4538 blockNoInSequence--;
4541 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4542 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4544 This->lastBlockNoInSequenceIndex = blockIndex;
4547 * Start reading the buffer.
4549 *bytesRead = 0;
4550 bufferWalker = buffer;
4552 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4554 ULARGE_INTEGER ulOffset;
4555 DWORD bytesReadAt;
4557 * Calculate how many bytes we can copy from this big block.
4559 bytesToReadInBuffer =
4560 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4562 TRACE("block %i\n",blockIndex);
4563 ulOffset.u.HighPart = 0;
4564 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4565 offsetInBlock;
4567 StorageImpl_ReadAt(This->parentStorage,
4568 ulOffset,
4569 bufferWalker,
4570 bytesToReadInBuffer,
4571 &bytesReadAt);
4573 * Step to the next big block.
4575 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4576 return STG_E_DOCFILECORRUPT;
4578 bufferWalker += bytesReadAt;
4579 size -= bytesReadAt;
4580 *bytesRead += bytesReadAt;
4581 offsetInBlock = 0; /* There is no offset on the next block */
4583 if (bytesToReadInBuffer != bytesReadAt)
4584 break;
4587 return (size == 0) ? S_OK : STG_E_READFAULT;
4590 /******************************************************************************
4591 * BlockChainStream_WriteAt
4593 * Writes the specified number of bytes to this chain at the specified offset.
4594 * bytesWritten may be NULL.
4595 * Will fail if not all specified number of bytes have been written.
4597 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4598 ULARGE_INTEGER offset,
4599 ULONG size,
4600 const void* buffer,
4601 ULONG* bytesWritten)
4603 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4604 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4605 ULONG bytesToWrite;
4606 ULONG blockIndex;
4607 const BYTE* bufferWalker;
4610 * Find the first block in the stream that contains part of the buffer.
4612 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4613 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4614 (blockNoInSequence < This->lastBlockNoInSequence) )
4616 blockIndex = BlockChainStream_GetHeadOfChain(This);
4617 This->lastBlockNoInSequence = blockNoInSequence;
4619 else
4621 ULONG temp = blockNoInSequence;
4623 blockIndex = This->lastBlockNoInSequenceIndex;
4624 blockNoInSequence -= This->lastBlockNoInSequence;
4625 This->lastBlockNoInSequence = temp;
4628 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4630 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4631 &blockIndex)))
4632 return STG_E_DOCFILECORRUPT;
4633 blockNoInSequence--;
4636 This->lastBlockNoInSequenceIndex = blockIndex;
4638 /* BlockChainStream_SetSize should have already been called to ensure we have
4639 * enough blocks in the chain to write into */
4640 if (blockIndex == BLOCK_END_OF_CHAIN)
4642 ERR("not enough blocks in chain to write data\n");
4643 return STG_E_DOCFILECORRUPT;
4647 * Here, I'm casting away the constness on the buffer variable
4648 * This is OK since we don't intend to modify that buffer.
4650 *bytesWritten = 0;
4651 bufferWalker = (const BYTE*)buffer;
4653 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4655 ULARGE_INTEGER ulOffset;
4656 DWORD bytesWrittenAt;
4658 * Calculate how many bytes we can copy from this big block.
4660 bytesToWrite =
4661 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4663 TRACE("block %i\n",blockIndex);
4664 ulOffset.u.HighPart = 0;
4665 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4666 offsetInBlock;
4668 StorageImpl_WriteAt(This->parentStorage,
4669 ulOffset,
4670 (BYTE*)bufferWalker,
4671 bytesToWrite,
4672 &bytesWrittenAt);
4675 * Step to the next big block.
4677 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4678 &blockIndex)))
4679 return STG_E_DOCFILECORRUPT;
4681 bufferWalker += bytesWrittenAt;
4682 size -= bytesWrittenAt;
4683 *bytesWritten += bytesWrittenAt;
4684 offsetInBlock = 0; /* There is no offset on the next block */
4686 if (bytesWrittenAt != bytesToWrite)
4687 break;
4690 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4693 /******************************************************************************
4694 * BlockChainStream_Shrink
4696 * Shrinks this chain in the big block depot.
4698 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4699 ULARGE_INTEGER newSize)
4701 ULONG blockIndex, extraBlock;
4702 ULONG numBlocks;
4703 ULONG count = 1;
4706 * Reset the last accessed block cache.
4708 This->lastBlockNoInSequence = 0xFFFFFFFF;
4709 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4712 * Figure out how many blocks are needed to contain the new size
4714 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4716 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4717 numBlocks++;
4719 blockIndex = BlockChainStream_GetHeadOfChain(This);
4722 * Go to the new end of chain
4724 while (count < numBlocks)
4726 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4727 &blockIndex)))
4728 return FALSE;
4729 count++;
4732 /* Get the next block before marking the new end */
4733 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4734 &extraBlock)))
4735 return FALSE;
4737 /* Mark the new end of chain */
4738 StorageImpl_SetNextBlockInChain(
4739 This->parentStorage,
4740 blockIndex,
4741 BLOCK_END_OF_CHAIN);
4743 This->tailIndex = blockIndex;
4744 This->numBlocks = numBlocks;
4747 * Mark the extra blocks as free
4749 while (extraBlock != BLOCK_END_OF_CHAIN)
4751 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4752 &blockIndex)))
4753 return FALSE;
4754 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4755 extraBlock = blockIndex;
4758 return TRUE;
4761 /******************************************************************************
4762 * BlockChainStream_Enlarge
4764 * Grows this chain in the big block depot.
4766 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4767 ULARGE_INTEGER newSize)
4769 ULONG blockIndex, currentBlock;
4770 ULONG newNumBlocks;
4771 ULONG oldNumBlocks = 0;
4773 blockIndex = BlockChainStream_GetHeadOfChain(This);
4776 * Empty chain. Create the head.
4778 if (blockIndex == BLOCK_END_OF_CHAIN)
4780 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4781 StorageImpl_SetNextBlockInChain(This->parentStorage,
4782 blockIndex,
4783 BLOCK_END_OF_CHAIN);
4785 if (This->headOfStreamPlaceHolder != 0)
4787 *(This->headOfStreamPlaceHolder) = blockIndex;
4789 else
4791 StgProperty chainProp;
4792 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4794 StorageImpl_ReadProperty(
4795 This->parentStorage,
4796 This->ownerPropertyIndex,
4797 &chainProp);
4799 chainProp.startingBlock = blockIndex;
4801 StorageImpl_WriteProperty(
4802 This->parentStorage,
4803 This->ownerPropertyIndex,
4804 &chainProp);
4807 This->tailIndex = blockIndex;
4808 This->numBlocks = 1;
4812 * Figure out how many blocks are needed to contain this stream
4814 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4816 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4817 newNumBlocks++;
4820 * Go to the current end of chain
4822 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4824 currentBlock = blockIndex;
4826 while (blockIndex != BLOCK_END_OF_CHAIN)
4828 This->numBlocks++;
4829 currentBlock = blockIndex;
4831 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4832 &blockIndex)))
4833 return FALSE;
4836 This->tailIndex = currentBlock;
4839 currentBlock = This->tailIndex;
4840 oldNumBlocks = This->numBlocks;
4843 * Add new blocks to the chain
4845 if (oldNumBlocks < newNumBlocks)
4847 while (oldNumBlocks < newNumBlocks)
4849 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4851 StorageImpl_SetNextBlockInChain(
4852 This->parentStorage,
4853 currentBlock,
4854 blockIndex);
4856 StorageImpl_SetNextBlockInChain(
4857 This->parentStorage,
4858 blockIndex,
4859 BLOCK_END_OF_CHAIN);
4861 currentBlock = blockIndex;
4862 oldNumBlocks++;
4865 This->tailIndex = blockIndex;
4866 This->numBlocks = newNumBlocks;
4869 return TRUE;
4872 /******************************************************************************
4873 * BlockChainStream_SetSize
4875 * Sets the size of this stream. The big block depot will be updated.
4876 * The file will grow if we grow the chain.
4878 * TODO: Free the actual blocks in the file when we shrink the chain.
4879 * Currently, the blocks are still in the file. So the file size
4880 * doesn't shrink even if we shrink streams.
4882 BOOL BlockChainStream_SetSize(
4883 BlockChainStream* This,
4884 ULARGE_INTEGER newSize)
4886 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4888 if (newSize.u.LowPart == size.u.LowPart)
4889 return TRUE;
4891 if (newSize.u.LowPart < size.u.LowPart)
4893 BlockChainStream_Shrink(This, newSize);
4895 else
4897 BlockChainStream_Enlarge(This, newSize);
4900 return TRUE;
4903 /******************************************************************************
4904 * BlockChainStream_GetSize
4906 * Returns the size of this chain.
4907 * Will return the block count if this chain doesn't have a property.
4909 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4911 StgProperty chainProperty;
4913 if(This->headOfStreamPlaceHolder == NULL)
4916 * This chain is a data stream read the property and return
4917 * the appropriate size
4919 StorageImpl_ReadProperty(
4920 This->parentStorage,
4921 This->ownerPropertyIndex,
4922 &chainProperty);
4924 return chainProperty.size;
4926 else
4929 * this chain is a chain that does not have a property, figure out the
4930 * size by making the product number of used blocks times the
4931 * size of them
4933 ULARGE_INTEGER result;
4934 result.u.HighPart = 0;
4936 result.u.LowPart =
4937 BlockChainStream_GetCount(This) *
4938 This->parentStorage->bigBlockSize;
4940 return result;
4944 /******************************************************************************
4945 ** SmallBlockChainStream implementation
4948 SmallBlockChainStream* SmallBlockChainStream_Construct(
4949 StorageImpl* parentStorage,
4950 ULONG propertyIndex)
4952 SmallBlockChainStream* newStream;
4954 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4956 newStream->parentStorage = parentStorage;
4957 newStream->ownerPropertyIndex = propertyIndex;
4959 return newStream;
4962 void SmallBlockChainStream_Destroy(
4963 SmallBlockChainStream* This)
4965 HeapFree(GetProcessHeap(), 0, This);
4968 /******************************************************************************
4969 * SmallBlockChainStream_GetHeadOfChain
4971 * Returns the head of this chain of small blocks.
4973 static ULONG SmallBlockChainStream_GetHeadOfChain(
4974 SmallBlockChainStream* This)
4976 StgProperty chainProperty;
4977 BOOL readSuccessful;
4979 if (This->ownerPropertyIndex)
4981 readSuccessful = StorageImpl_ReadProperty(
4982 This->parentStorage,
4983 This->ownerPropertyIndex,
4984 &chainProperty);
4986 if (readSuccessful)
4988 return chainProperty.startingBlock;
4993 return BLOCK_END_OF_CHAIN;
4996 /******************************************************************************
4997 * SmallBlockChainStream_GetNextBlockInChain
4999 * Returns the index of the next small block in this chain.
5001 * Return Values:
5002 * - BLOCK_END_OF_CHAIN: end of this chain
5003 * - BLOCK_UNUSED: small block 'blockIndex' is free
5005 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5006 SmallBlockChainStream* This,
5007 ULONG blockIndex,
5008 ULONG* nextBlockInChain)
5010 ULARGE_INTEGER offsetOfBlockInDepot;
5011 DWORD buffer;
5012 ULONG bytesRead;
5013 HRESULT res;
5015 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5017 offsetOfBlockInDepot.u.HighPart = 0;
5018 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5021 * Read those bytes in the buffer from the small block file.
5023 res = BlockChainStream_ReadAt(
5024 This->parentStorage->smallBlockDepotChain,
5025 offsetOfBlockInDepot,
5026 sizeof(DWORD),
5027 &buffer,
5028 &bytesRead);
5030 if (SUCCEEDED(res))
5032 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5033 return S_OK;
5036 return res;
5039 /******************************************************************************
5040 * SmallBlockChainStream_SetNextBlockInChain
5042 * Writes the index of the next block of the specified block in the small
5043 * block depot.
5044 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5045 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5047 static void SmallBlockChainStream_SetNextBlockInChain(
5048 SmallBlockChainStream* This,
5049 ULONG blockIndex,
5050 ULONG nextBlock)
5052 ULARGE_INTEGER offsetOfBlockInDepot;
5053 DWORD buffer;
5054 ULONG bytesWritten;
5056 offsetOfBlockInDepot.u.HighPart = 0;
5057 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5059 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5062 * Read those bytes in the buffer from the small block file.
5064 BlockChainStream_WriteAt(
5065 This->parentStorage->smallBlockDepotChain,
5066 offsetOfBlockInDepot,
5067 sizeof(DWORD),
5068 &buffer,
5069 &bytesWritten);
5072 /******************************************************************************
5073 * SmallBlockChainStream_FreeBlock
5075 * Flag small block 'blockIndex' as free in the small block depot.
5077 static void SmallBlockChainStream_FreeBlock(
5078 SmallBlockChainStream* This,
5079 ULONG blockIndex)
5081 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5084 /******************************************************************************
5085 * SmallBlockChainStream_GetNextFreeBlock
5087 * Returns the index of a free small block. The small block depot will be
5088 * enlarged if necessary. The small block chain will also be enlarged if
5089 * necessary.
5091 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5092 SmallBlockChainStream* This)
5094 ULARGE_INTEGER offsetOfBlockInDepot;
5095 DWORD buffer;
5096 ULONG bytesRead;
5097 ULONG blockIndex = 0;
5098 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5099 HRESULT res = S_OK;
5100 ULONG smallBlocksPerBigBlock;
5102 offsetOfBlockInDepot.u.HighPart = 0;
5105 * Scan the small block depot for a free block
5107 while (nextBlockIndex != BLOCK_UNUSED)
5109 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5111 res = BlockChainStream_ReadAt(
5112 This->parentStorage->smallBlockDepotChain,
5113 offsetOfBlockInDepot,
5114 sizeof(DWORD),
5115 &buffer,
5116 &bytesRead);
5119 * If we run out of space for the small block depot, enlarge it
5121 if (SUCCEEDED(res))
5123 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5125 if (nextBlockIndex != BLOCK_UNUSED)
5126 blockIndex++;
5128 else
5130 ULONG count =
5131 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5133 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5134 ULONG nextBlock, newsbdIndex;
5135 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
5137 nextBlock = sbdIndex;
5138 while (nextBlock != BLOCK_END_OF_CHAIN)
5140 sbdIndex = nextBlock;
5141 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5144 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5145 if (sbdIndex != BLOCK_END_OF_CHAIN)
5146 StorageImpl_SetNextBlockInChain(
5147 This->parentStorage,
5148 sbdIndex,
5149 newsbdIndex);
5151 StorageImpl_SetNextBlockInChain(
5152 This->parentStorage,
5153 newsbdIndex,
5154 BLOCK_END_OF_CHAIN);
5157 * Initialize all the small blocks to free
5159 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5160 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
5162 if (count == 0)
5165 * We have just created the small block depot.
5167 StgProperty rootProp;
5168 ULONG sbStartIndex;
5171 * Save it in the header
5173 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5174 StorageImpl_SaveFileHeader(This->parentStorage);
5177 * And allocate the first big block that will contain small blocks
5179 sbStartIndex =
5180 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5182 StorageImpl_SetNextBlockInChain(
5183 This->parentStorage,
5184 sbStartIndex,
5185 BLOCK_END_OF_CHAIN);
5187 StorageImpl_ReadProperty(
5188 This->parentStorage,
5189 This->parentStorage->base.rootPropertySetIndex,
5190 &rootProp);
5192 rootProp.startingBlock = sbStartIndex;
5193 rootProp.size.u.HighPart = 0;
5194 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5196 StorageImpl_WriteProperty(
5197 This->parentStorage,
5198 This->parentStorage->base.rootPropertySetIndex,
5199 &rootProp);
5204 smallBlocksPerBigBlock =
5205 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5208 * Verify if we have to allocate big blocks to contain small blocks
5210 if (blockIndex % smallBlocksPerBigBlock == 0)
5212 StgProperty rootProp;
5213 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5215 StorageImpl_ReadProperty(
5216 This->parentStorage,
5217 This->parentStorage->base.rootPropertySetIndex,
5218 &rootProp);
5220 if (rootProp.size.u.LowPart <
5221 (blocksRequired * This->parentStorage->bigBlockSize))
5223 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5225 BlockChainStream_SetSize(
5226 This->parentStorage->smallBlockRootChain,
5227 rootProp.size);
5229 StorageImpl_WriteProperty(
5230 This->parentStorage,
5231 This->parentStorage->base.rootPropertySetIndex,
5232 &rootProp);
5236 return blockIndex;
5239 /******************************************************************************
5240 * SmallBlockChainStream_ReadAt
5242 * Reads a specified number of bytes from this chain at the specified offset.
5243 * bytesRead may be NULL.
5244 * Failure will be returned if the specified number of bytes has not been read.
5246 HRESULT SmallBlockChainStream_ReadAt(
5247 SmallBlockChainStream* This,
5248 ULARGE_INTEGER offset,
5249 ULONG size,
5250 void* buffer,
5251 ULONG* bytesRead)
5253 HRESULT rc = S_OK;
5254 ULARGE_INTEGER offsetInBigBlockFile;
5255 ULONG blockNoInSequence =
5256 offset.u.LowPart / This->parentStorage->smallBlockSize;
5258 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5259 ULONG bytesToReadInBuffer;
5260 ULONG blockIndex;
5261 ULONG bytesReadFromBigBlockFile;
5262 BYTE* bufferWalker;
5265 * This should never happen on a small block file.
5267 assert(offset.u.HighPart==0);
5270 * Find the first block in the stream that contains part of the buffer.
5272 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5274 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5276 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5277 if(FAILED(rc))
5278 return rc;
5279 blockNoInSequence--;
5283 * Start reading the buffer.
5285 *bytesRead = 0;
5286 bufferWalker = buffer;
5288 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5291 * Calculate how many bytes we can copy from this small block.
5293 bytesToReadInBuffer =
5294 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5297 * Calculate the offset of the small block in the small block file.
5299 offsetInBigBlockFile.u.HighPart = 0;
5300 offsetInBigBlockFile.u.LowPart =
5301 blockIndex * This->parentStorage->smallBlockSize;
5303 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5306 * Read those bytes in the buffer from the small block file.
5307 * The small block has already been identified so it shouldn't fail
5308 * unless the file is corrupt.
5310 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5311 offsetInBigBlockFile,
5312 bytesToReadInBuffer,
5313 bufferWalker,
5314 &bytesReadFromBigBlockFile);
5316 if (FAILED(rc))
5317 return rc;
5320 * Step to the next big block.
5322 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5323 if(FAILED(rc))
5324 return STG_E_DOCFILECORRUPT;
5326 bufferWalker += bytesReadFromBigBlockFile;
5327 size -= bytesReadFromBigBlockFile;
5328 *bytesRead += bytesReadFromBigBlockFile;
5329 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5332 return (size == 0) ? S_OK : STG_E_READFAULT;
5335 /******************************************************************************
5336 * SmallBlockChainStream_WriteAt
5338 * Writes the specified number of bytes to this chain at the specified offset.
5339 * bytesWritten may be NULL.
5340 * Will fail if not all specified number of bytes have been written.
5342 HRESULT SmallBlockChainStream_WriteAt(
5343 SmallBlockChainStream* This,
5344 ULARGE_INTEGER offset,
5345 ULONG size,
5346 const void* buffer,
5347 ULONG* bytesWritten)
5349 ULARGE_INTEGER offsetInBigBlockFile;
5350 ULONG blockNoInSequence =
5351 offset.u.LowPart / This->parentStorage->smallBlockSize;
5353 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5354 ULONG bytesToWriteInBuffer;
5355 ULONG blockIndex;
5356 ULONG bytesWrittenToBigBlockFile;
5357 const BYTE* bufferWalker;
5358 HRESULT res;
5361 * This should never happen on a small block file.
5363 assert(offset.u.HighPart==0);
5366 * Find the first block in the stream that contains part of the buffer.
5368 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5370 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5372 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5373 return STG_E_DOCFILECORRUPT;
5374 blockNoInSequence--;
5378 * Start writing the buffer.
5380 * Here, I'm casting away the constness on the buffer variable
5381 * This is OK since we don't intend to modify that buffer.
5383 *bytesWritten = 0;
5384 bufferWalker = (const BYTE*)buffer;
5385 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5388 * Calculate how many bytes we can copy to this small block.
5390 bytesToWriteInBuffer =
5391 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5394 * Calculate the offset of the small block in the small block file.
5396 offsetInBigBlockFile.u.HighPart = 0;
5397 offsetInBigBlockFile.u.LowPart =
5398 blockIndex * This->parentStorage->smallBlockSize;
5400 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5403 * Write those bytes in the buffer to the small block file.
5405 res = BlockChainStream_WriteAt(
5406 This->parentStorage->smallBlockRootChain,
5407 offsetInBigBlockFile,
5408 bytesToWriteInBuffer,
5409 bufferWalker,
5410 &bytesWrittenToBigBlockFile);
5411 if (FAILED(res))
5412 return res;
5415 * Step to the next big block.
5417 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5418 &blockIndex)))
5419 return FALSE;
5420 bufferWalker += bytesWrittenToBigBlockFile;
5421 size -= bytesWrittenToBigBlockFile;
5422 *bytesWritten += bytesWrittenToBigBlockFile;
5423 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5426 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5429 /******************************************************************************
5430 * SmallBlockChainStream_Shrink
5432 * Shrinks this chain in the small block depot.
5434 static BOOL SmallBlockChainStream_Shrink(
5435 SmallBlockChainStream* This,
5436 ULARGE_INTEGER newSize)
5438 ULONG blockIndex, extraBlock;
5439 ULONG numBlocks;
5440 ULONG count = 0;
5442 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5444 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5445 numBlocks++;
5447 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5450 * Go to the new end of chain
5452 while (count < numBlocks)
5454 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5455 &blockIndex)))
5456 return FALSE;
5457 count++;
5461 * If the count is 0, we have a special case, the head of the chain was
5462 * just freed.
5464 if (count == 0)
5466 StgProperty chainProp;
5468 StorageImpl_ReadProperty(This->parentStorage,
5469 This->ownerPropertyIndex,
5470 &chainProp);
5472 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5474 StorageImpl_WriteProperty(This->parentStorage,
5475 This->ownerPropertyIndex,
5476 &chainProp);
5479 * We start freeing the chain at the head block.
5481 extraBlock = blockIndex;
5483 else
5485 /* Get the next block before marking the new end */
5486 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5487 &extraBlock)))
5488 return FALSE;
5490 /* Mark the new end of chain */
5491 SmallBlockChainStream_SetNextBlockInChain(
5492 This,
5493 blockIndex,
5494 BLOCK_END_OF_CHAIN);
5498 * Mark the extra blocks as free
5500 while (extraBlock != BLOCK_END_OF_CHAIN)
5502 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5503 &blockIndex)))
5504 return FALSE;
5505 SmallBlockChainStream_FreeBlock(This, extraBlock);
5506 extraBlock = blockIndex;
5509 return TRUE;
5512 /******************************************************************************
5513 * SmallBlockChainStream_Enlarge
5515 * Grows this chain in the small block depot.
5517 static BOOL SmallBlockChainStream_Enlarge(
5518 SmallBlockChainStream* This,
5519 ULARGE_INTEGER newSize)
5521 ULONG blockIndex, currentBlock;
5522 ULONG newNumBlocks;
5523 ULONG oldNumBlocks = 0;
5525 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5528 * Empty chain
5530 if (blockIndex == BLOCK_END_OF_CHAIN)
5533 StgProperty chainProp;
5535 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5536 &chainProp);
5538 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5540 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5541 &chainProp);
5543 blockIndex = chainProp.startingBlock;
5544 SmallBlockChainStream_SetNextBlockInChain(
5545 This,
5546 blockIndex,
5547 BLOCK_END_OF_CHAIN);
5550 currentBlock = blockIndex;
5553 * Figure out how many blocks are needed to contain this stream
5555 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5557 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5558 newNumBlocks++;
5561 * Go to the current end of chain
5563 while (blockIndex != BLOCK_END_OF_CHAIN)
5565 oldNumBlocks++;
5566 currentBlock = blockIndex;
5567 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5568 return FALSE;
5572 * Add new blocks to the chain
5574 while (oldNumBlocks < newNumBlocks)
5576 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5577 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5579 SmallBlockChainStream_SetNextBlockInChain(
5580 This,
5581 blockIndex,
5582 BLOCK_END_OF_CHAIN);
5584 currentBlock = blockIndex;
5585 oldNumBlocks++;
5588 return TRUE;
5591 /******************************************************************************
5592 * SmallBlockChainStream_SetSize
5594 * Sets the size of this stream.
5595 * The file will grow if we grow the chain.
5597 * TODO: Free the actual blocks in the file when we shrink the chain.
5598 * Currently, the blocks are still in the file. So the file size
5599 * doesn't shrink even if we shrink streams.
5601 BOOL SmallBlockChainStream_SetSize(
5602 SmallBlockChainStream* This,
5603 ULARGE_INTEGER newSize)
5605 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5607 if (newSize.u.LowPart == size.u.LowPart)
5608 return TRUE;
5610 if (newSize.u.LowPart < size.u.LowPart)
5612 SmallBlockChainStream_Shrink(This, newSize);
5614 else
5616 SmallBlockChainStream_Enlarge(This, newSize);
5619 return TRUE;
5622 /******************************************************************************
5623 * SmallBlockChainStream_GetSize
5625 * Returns the size of this chain.
5627 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5629 StgProperty chainProperty;
5631 StorageImpl_ReadProperty(
5632 This->parentStorage,
5633 This->ownerPropertyIndex,
5634 &chainProperty);
5636 return chainProperty.size;
5639 /******************************************************************************
5640 * StgCreateDocfile [OLE32.@]
5641 * Creates a new compound file storage object
5643 * PARAMS
5644 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5645 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5646 * reserved [ ?] unused?, usually 0
5647 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5649 * RETURNS
5650 * S_OK if the file was successfully created
5651 * some STG_E_ value if error
5652 * NOTES
5653 * if pwcsName is NULL, create file with new unique name
5654 * the function can returns
5655 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5656 * (unrealized now)
5658 HRESULT WINAPI StgCreateDocfile(
5659 LPCOLESTR pwcsName,
5660 DWORD grfMode,
5661 DWORD reserved,
5662 IStorage **ppstgOpen)
5664 StorageImpl* newStorage = 0;
5665 HANDLE hFile = INVALID_HANDLE_VALUE;
5666 HRESULT hr = STG_E_INVALIDFLAG;
5667 DWORD shareMode;
5668 DWORD accessMode;
5669 DWORD creationMode;
5670 DWORD fileAttributes;
5671 WCHAR tempFileName[MAX_PATH];
5673 TRACE("(%s, %x, %d, %p)\n",
5674 debugstr_w(pwcsName), grfMode,
5675 reserved, ppstgOpen);
5678 * Validate the parameters
5680 if (ppstgOpen == 0)
5681 return STG_E_INVALIDPOINTER;
5682 if (reserved != 0)
5683 return STG_E_INVALIDPARAMETER;
5685 /* if no share mode given then DENY_NONE is the default */
5686 if (STGM_SHARE_MODE(grfMode) == 0)
5687 grfMode |= STGM_SHARE_DENY_NONE;
5690 * Validate the STGM flags
5692 if ( FAILED( validateSTGM(grfMode) ))
5693 goto end;
5695 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5696 switch(STGM_ACCESS_MODE(grfMode))
5698 case STGM_WRITE:
5699 case STGM_READWRITE:
5700 break;
5701 default:
5702 goto end;
5705 /* in direct mode, can only use SHARE_EXCLUSIVE */
5706 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5707 goto end;
5709 /* but in transacted mode, any share mode is valid */
5712 * Generate a unique name.
5714 if (pwcsName == 0)
5716 WCHAR tempPath[MAX_PATH];
5717 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5719 memset(tempPath, 0, sizeof(tempPath));
5720 memset(tempFileName, 0, sizeof(tempFileName));
5722 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5723 tempPath[0] = '.';
5725 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5726 pwcsName = tempFileName;
5727 else
5729 hr = STG_E_INSUFFICIENTMEMORY;
5730 goto end;
5733 creationMode = TRUNCATE_EXISTING;
5735 else
5737 creationMode = GetCreationModeFromSTGM(grfMode);
5741 * Interpret the STGM value grfMode
5743 shareMode = GetShareModeFromSTGM(grfMode);
5744 accessMode = GetAccessModeFromSTGM(grfMode);
5746 if (grfMode & STGM_DELETEONRELEASE)
5747 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5748 else
5749 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5751 if (grfMode & STGM_TRANSACTED)
5752 FIXME("Transacted mode not implemented.\n");
5755 * Initialize the "out" parameter.
5757 *ppstgOpen = 0;
5759 hFile = CreateFileW(pwcsName,
5760 accessMode,
5761 shareMode,
5762 NULL,
5763 creationMode,
5764 fileAttributes,
5767 if (hFile == INVALID_HANDLE_VALUE)
5769 if(GetLastError() == ERROR_FILE_EXISTS)
5770 hr = STG_E_FILEALREADYEXISTS;
5771 else
5772 hr = E_FAIL;
5773 goto end;
5777 * Allocate and initialize the new IStorage32object.
5779 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5781 if (newStorage == 0)
5783 hr = STG_E_INSUFFICIENTMEMORY;
5784 goto end;
5787 hr = StorageImpl_Construct(
5788 newStorage,
5789 hFile,
5790 pwcsName,
5791 NULL,
5792 grfMode,
5793 TRUE,
5794 TRUE);
5796 if (FAILED(hr))
5798 HeapFree(GetProcessHeap(), 0, newStorage);
5799 goto end;
5803 * Get an "out" pointer for the caller.
5805 hr = StorageBaseImpl_QueryInterface(
5806 (IStorage*)newStorage,
5807 (REFIID)&IID_IStorage,
5808 (void**)ppstgOpen);
5809 end:
5810 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5812 return hr;
5815 /******************************************************************************
5816 * StgCreateStorageEx [OLE32.@]
5818 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5820 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5821 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5823 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5825 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5826 return STG_E_INVALIDPARAMETER;
5829 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5831 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5832 return STG_E_INVALIDPARAMETER;
5835 if (stgfmt == STGFMT_FILE)
5837 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5838 return STG_E_INVALIDPARAMETER;
5841 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5843 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5844 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5847 ERR("Invalid stgfmt argument\n");
5848 return STG_E_INVALIDPARAMETER;
5851 /******************************************************************************
5852 * StgCreatePropSetStg [OLE32.@]
5854 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5855 IPropertySetStorage **ppPropSetStg)
5857 HRESULT hr;
5859 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5860 if (reserved)
5861 hr = STG_E_INVALIDPARAMETER;
5862 else
5863 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5864 (void**)ppPropSetStg);
5865 return hr;
5868 /******************************************************************************
5869 * StgOpenStorageEx [OLE32.@]
5871 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5873 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5874 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5876 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5878 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5879 return STG_E_INVALIDPARAMETER;
5882 switch (stgfmt)
5884 case STGFMT_FILE:
5885 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5886 return STG_E_INVALIDPARAMETER;
5888 case STGFMT_STORAGE:
5889 break;
5891 case STGFMT_DOCFILE:
5892 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5894 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5895 return STG_E_INVALIDPARAMETER;
5897 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5898 break;
5900 case STGFMT_ANY:
5901 WARN("STGFMT_ANY assuming storage\n");
5902 break;
5904 default:
5905 return STG_E_INVALIDPARAMETER;
5908 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5912 /******************************************************************************
5913 * StgOpenStorage [OLE32.@]
5915 HRESULT WINAPI StgOpenStorage(
5916 const OLECHAR *pwcsName,
5917 IStorage *pstgPriority,
5918 DWORD grfMode,
5919 SNB snbExclude,
5920 DWORD reserved,
5921 IStorage **ppstgOpen)
5923 StorageImpl* newStorage = 0;
5924 HRESULT hr = S_OK;
5925 HANDLE hFile = 0;
5926 DWORD shareMode;
5927 DWORD accessMode;
5928 WCHAR fullname[MAX_PATH];
5930 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5931 debugstr_w(pwcsName), pstgPriority, grfMode,
5932 snbExclude, reserved, ppstgOpen);
5935 * Perform sanity checks
5937 if (pwcsName == 0)
5939 hr = STG_E_INVALIDNAME;
5940 goto end;
5943 if (ppstgOpen == 0)
5945 hr = STG_E_INVALIDPOINTER;
5946 goto end;
5949 if (reserved)
5951 hr = STG_E_INVALIDPARAMETER;
5952 goto end;
5955 if (grfMode & STGM_PRIORITY)
5957 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5958 return STG_E_INVALIDFLAG;
5959 if (grfMode & STGM_DELETEONRELEASE)
5960 return STG_E_INVALIDFUNCTION;
5961 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5962 return STG_E_INVALIDFLAG;
5963 grfMode &= ~0xf0; /* remove the existing sharing mode */
5964 grfMode |= STGM_SHARE_DENY_NONE;
5966 /* STGM_PRIORITY stops other IStorage objects on the same file from
5967 * committing until the STGM_PRIORITY IStorage is closed. it also
5968 * stops non-transacted mode StgOpenStorage calls with write access from
5969 * succeeding. obviously, both of these cannot be achieved through just
5970 * file share flags */
5971 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5975 * Validate the sharing mode
5977 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5978 switch(STGM_SHARE_MODE(grfMode))
5980 case STGM_SHARE_EXCLUSIVE:
5981 case STGM_SHARE_DENY_WRITE:
5982 break;
5983 default:
5984 hr = STG_E_INVALIDFLAG;
5985 goto end;
5989 * Validate the STGM flags
5991 if ( FAILED( validateSTGM(grfMode) ) ||
5992 (grfMode&STGM_CREATE))
5994 hr = STG_E_INVALIDFLAG;
5995 goto end;
5998 /* shared reading requires transacted mode */
5999 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6000 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6001 !(grfMode&STGM_TRANSACTED) )
6003 hr = STG_E_INVALIDFLAG;
6004 goto end;
6008 * Interpret the STGM value grfMode
6010 shareMode = GetShareModeFromSTGM(grfMode);
6011 accessMode = GetAccessModeFromSTGM(grfMode);
6014 * Initialize the "out" parameter.
6016 *ppstgOpen = 0;
6018 hFile = CreateFileW( pwcsName,
6019 accessMode,
6020 shareMode,
6021 NULL,
6022 OPEN_EXISTING,
6023 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6026 if (hFile==INVALID_HANDLE_VALUE)
6028 DWORD last_error = GetLastError();
6030 hr = E_FAIL;
6032 switch (last_error)
6034 case ERROR_FILE_NOT_FOUND:
6035 hr = STG_E_FILENOTFOUND;
6036 break;
6038 case ERROR_PATH_NOT_FOUND:
6039 hr = STG_E_PATHNOTFOUND;
6040 break;
6042 case ERROR_ACCESS_DENIED:
6043 case ERROR_WRITE_PROTECT:
6044 hr = STG_E_ACCESSDENIED;
6045 break;
6047 case ERROR_SHARING_VIOLATION:
6048 hr = STG_E_SHAREVIOLATION;
6049 break;
6051 default:
6052 hr = E_FAIL;
6055 goto end;
6059 * Refuse to open the file if it's too small to be a structured storage file
6060 * FIXME: verify the file when reading instead of here
6062 if (GetFileSize(hFile, NULL) < 0x100)
6064 CloseHandle(hFile);
6065 hr = STG_E_FILEALREADYEXISTS;
6066 goto end;
6070 * Allocate and initialize the new IStorage32object.
6072 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6074 if (newStorage == 0)
6076 hr = STG_E_INSUFFICIENTMEMORY;
6077 goto end;
6080 /* Initialize the storage */
6081 hr = StorageImpl_Construct(
6082 newStorage,
6083 hFile,
6084 pwcsName,
6085 NULL,
6086 grfMode,
6087 TRUE,
6088 FALSE );
6090 if (FAILED(hr))
6092 HeapFree(GetProcessHeap(), 0, newStorage);
6094 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6096 if(hr == STG_E_INVALIDHEADER)
6097 hr = STG_E_FILEALREADYEXISTS;
6098 goto end;
6101 /* prepare the file name string given in lieu of the root property name */
6102 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6103 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6104 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6107 * Get an "out" pointer for the caller.
6109 hr = StorageBaseImpl_QueryInterface(
6110 (IStorage*)newStorage,
6111 (REFIID)&IID_IStorage,
6112 (void**)ppstgOpen);
6114 end:
6115 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6116 return hr;
6119 /******************************************************************************
6120 * StgCreateDocfileOnILockBytes [OLE32.@]
6122 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6123 ILockBytes *plkbyt,
6124 DWORD grfMode,
6125 DWORD reserved,
6126 IStorage** ppstgOpen)
6128 StorageImpl* newStorage = 0;
6129 HRESULT hr = S_OK;
6132 * Validate the parameters
6134 if ((ppstgOpen == 0) || (plkbyt == 0))
6135 return STG_E_INVALIDPOINTER;
6138 * Allocate and initialize the new IStorage object.
6140 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6142 if (newStorage == 0)
6143 return STG_E_INSUFFICIENTMEMORY;
6145 hr = StorageImpl_Construct(
6146 newStorage,
6149 plkbyt,
6150 grfMode,
6151 FALSE,
6152 TRUE);
6154 if (FAILED(hr))
6156 HeapFree(GetProcessHeap(), 0, newStorage);
6157 return hr;
6161 * Get an "out" pointer for the caller.
6163 hr = StorageBaseImpl_QueryInterface(
6164 (IStorage*)newStorage,
6165 (REFIID)&IID_IStorage,
6166 (void**)ppstgOpen);
6168 return hr;
6171 /******************************************************************************
6172 * StgOpenStorageOnILockBytes [OLE32.@]
6174 HRESULT WINAPI StgOpenStorageOnILockBytes(
6175 ILockBytes *plkbyt,
6176 IStorage *pstgPriority,
6177 DWORD grfMode,
6178 SNB snbExclude,
6179 DWORD reserved,
6180 IStorage **ppstgOpen)
6182 StorageImpl* newStorage = 0;
6183 HRESULT hr = S_OK;
6186 * Perform a sanity check
6188 if ((plkbyt == 0) || (ppstgOpen == 0))
6189 return STG_E_INVALIDPOINTER;
6192 * Validate the STGM flags
6194 if ( FAILED( validateSTGM(grfMode) ))
6195 return STG_E_INVALIDFLAG;
6198 * Initialize the "out" parameter.
6200 *ppstgOpen = 0;
6203 * Allocate and initialize the new IStorage object.
6205 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6207 if (newStorage == 0)
6208 return STG_E_INSUFFICIENTMEMORY;
6210 hr = StorageImpl_Construct(
6211 newStorage,
6214 plkbyt,
6215 grfMode,
6216 FALSE,
6217 FALSE);
6219 if (FAILED(hr))
6221 HeapFree(GetProcessHeap(), 0, newStorage);
6222 return hr;
6226 * Get an "out" pointer for the caller.
6228 hr = StorageBaseImpl_QueryInterface(
6229 (IStorage*)newStorage,
6230 (REFIID)&IID_IStorage,
6231 (void**)ppstgOpen);
6233 return hr;
6236 /******************************************************************************
6237 * StgSetTimes [ole32.@]
6238 * StgSetTimes [OLE32.@]
6242 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6243 FILETIME const *patime, FILETIME const *pmtime)
6245 IStorage *stg = NULL;
6246 HRESULT r;
6248 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6250 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6251 0, 0, &stg);
6252 if( SUCCEEDED(r) )
6254 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6255 IStorage_Release(stg);
6258 return r;
6261 /******************************************************************************
6262 * StgIsStorageILockBytes [OLE32.@]
6264 * Determines if the ILockBytes contains a storage object.
6266 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6268 BYTE sig[8];
6269 ULARGE_INTEGER offset;
6271 offset.u.HighPart = 0;
6272 offset.u.LowPart = 0;
6274 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6276 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6277 return S_OK;
6279 return S_FALSE;
6282 /******************************************************************************
6283 * WriteClassStg [OLE32.@]
6285 * This method will store the specified CLSID in the specified storage object
6287 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6289 HRESULT hRes;
6291 if(!pStg)
6292 return E_INVALIDARG;
6294 hRes = IStorage_SetClass(pStg, rclsid);
6296 return hRes;
6299 /***********************************************************************
6300 * ReadClassStg (OLE32.@)
6302 * This method reads the CLSID previously written to a storage object with
6303 * the WriteClassStg.
6305 * PARAMS
6306 * pstg [I] IStorage pointer
6307 * pclsid [O] Pointer to where the CLSID is written
6309 * RETURNS
6310 * Success: S_OK.
6311 * Failure: HRESULT code.
6313 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6315 STATSTG pstatstg;
6316 HRESULT hRes;
6318 TRACE("(%p, %p)\n", pstg, pclsid);
6320 if(!pstg || !pclsid)
6321 return E_INVALIDARG;
6324 * read a STATSTG structure (contains the clsid) from the storage
6326 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6328 if(SUCCEEDED(hRes))
6329 *pclsid=pstatstg.clsid;
6331 return hRes;
6334 /***********************************************************************
6335 * OleLoadFromStream (OLE32.@)
6337 * This function loads an object from stream
6339 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6341 CLSID clsid;
6342 HRESULT res;
6343 LPPERSISTSTREAM xstm;
6345 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6347 res=ReadClassStm(pStm,&clsid);
6348 if (!SUCCEEDED(res))
6349 return res;
6350 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6351 if (!SUCCEEDED(res))
6352 return res;
6353 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6354 if (!SUCCEEDED(res)) {
6355 IUnknown_Release((IUnknown*)*ppvObj);
6356 return res;
6358 res=IPersistStream_Load(xstm,pStm);
6359 IPersistStream_Release(xstm);
6360 /* FIXME: all refcounts ok at this point? I think they should be:
6361 * pStm : unchanged
6362 * ppvObj : 1
6363 * xstm : 0 (released)
6365 return res;
6368 /***********************************************************************
6369 * OleSaveToStream (OLE32.@)
6371 * This function saves an object with the IPersistStream interface on it
6372 * to the specified stream.
6374 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6377 CLSID clsid;
6378 HRESULT res;
6380 TRACE("(%p,%p)\n",pPStm,pStm);
6382 res=IPersistStream_GetClassID(pPStm,&clsid);
6384 if (SUCCEEDED(res)){
6386 res=WriteClassStm(pStm,&clsid);
6388 if (SUCCEEDED(res))
6390 res=IPersistStream_Save(pPStm,pStm,TRUE);
6393 TRACE("Finished Save\n");
6394 return res;
6397 /****************************************************************************
6398 * This method validate a STGM parameter that can contain the values below
6400 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6401 * The stgm values contained in 0xffff0000 are bitmasks.
6403 * STGM_DIRECT 0x00000000
6404 * STGM_TRANSACTED 0x00010000
6405 * STGM_SIMPLE 0x08000000
6407 * STGM_READ 0x00000000
6408 * STGM_WRITE 0x00000001
6409 * STGM_READWRITE 0x00000002
6411 * STGM_SHARE_DENY_NONE 0x00000040
6412 * STGM_SHARE_DENY_READ 0x00000030
6413 * STGM_SHARE_DENY_WRITE 0x00000020
6414 * STGM_SHARE_EXCLUSIVE 0x00000010
6416 * STGM_PRIORITY 0x00040000
6417 * STGM_DELETEONRELEASE 0x04000000
6419 * STGM_CREATE 0x00001000
6420 * STGM_CONVERT 0x00020000
6421 * STGM_FAILIFTHERE 0x00000000
6423 * STGM_NOSCRATCH 0x00100000
6424 * STGM_NOSNAPSHOT 0x00200000
6426 static HRESULT validateSTGM(DWORD stgm)
6428 DWORD access = STGM_ACCESS_MODE(stgm);
6429 DWORD share = STGM_SHARE_MODE(stgm);
6430 DWORD create = STGM_CREATE_MODE(stgm);
6432 if (stgm&~STGM_KNOWN_FLAGS)
6434 ERR("unknown flags %08x\n", stgm);
6435 return E_FAIL;
6438 switch (access)
6440 case STGM_READ:
6441 case STGM_WRITE:
6442 case STGM_READWRITE:
6443 break;
6444 default:
6445 return E_FAIL;
6448 switch (share)
6450 case STGM_SHARE_DENY_NONE:
6451 case STGM_SHARE_DENY_READ:
6452 case STGM_SHARE_DENY_WRITE:
6453 case STGM_SHARE_EXCLUSIVE:
6454 break;
6455 default:
6456 return E_FAIL;
6459 switch (create)
6461 case STGM_CREATE:
6462 case STGM_FAILIFTHERE:
6463 break;
6464 default:
6465 return E_FAIL;
6469 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6471 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6472 return E_FAIL;
6475 * STGM_CREATE | STGM_CONVERT
6476 * if both are false, STGM_FAILIFTHERE is set to TRUE
6478 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6479 return E_FAIL;
6482 * STGM_NOSCRATCH requires STGM_TRANSACTED
6484 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6485 return E_FAIL;
6488 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6489 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6491 if ( (stgm & STGM_NOSNAPSHOT) &&
6492 (!(stgm & STGM_TRANSACTED) ||
6493 share == STGM_SHARE_EXCLUSIVE ||
6494 share == STGM_SHARE_DENY_WRITE) )
6495 return E_FAIL;
6497 return S_OK;
6500 /****************************************************************************
6501 * GetShareModeFromSTGM
6503 * This method will return a share mode flag from a STGM value.
6504 * The STGM value is assumed valid.
6506 static DWORD GetShareModeFromSTGM(DWORD stgm)
6508 switch (STGM_SHARE_MODE(stgm))
6510 case STGM_SHARE_DENY_NONE:
6511 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6512 case STGM_SHARE_DENY_READ:
6513 return FILE_SHARE_WRITE;
6514 case STGM_SHARE_DENY_WRITE:
6515 return FILE_SHARE_READ;
6516 case STGM_SHARE_EXCLUSIVE:
6517 return 0;
6519 ERR("Invalid share mode!\n");
6520 assert(0);
6521 return 0;
6524 /****************************************************************************
6525 * GetAccessModeFromSTGM
6527 * This method will return an access mode flag from a STGM value.
6528 * The STGM value is assumed valid.
6530 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6532 switch (STGM_ACCESS_MODE(stgm))
6534 case STGM_READ:
6535 return GENERIC_READ;
6536 case STGM_WRITE:
6537 case STGM_READWRITE:
6538 return GENERIC_READ | GENERIC_WRITE;
6540 ERR("Invalid access mode!\n");
6541 assert(0);
6542 return 0;
6545 /****************************************************************************
6546 * GetCreationModeFromSTGM
6548 * This method will return a creation mode flag from a STGM value.
6549 * The STGM value is assumed valid.
6551 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6553 switch(STGM_CREATE_MODE(stgm))
6555 case STGM_CREATE:
6556 return CREATE_ALWAYS;
6557 case STGM_CONVERT:
6558 FIXME("STGM_CONVERT not implemented!\n");
6559 return CREATE_NEW;
6560 case STGM_FAILIFTHERE:
6561 return CREATE_NEW;
6563 ERR("Invalid create mode!\n");
6564 assert(0);
6565 return 0;
6569 /*************************************************************************
6570 * OLECONVERT_LoadOLE10 [Internal]
6572 * Loads the OLE10 STREAM to memory
6574 * PARAMS
6575 * pOleStream [I] The OLESTREAM
6576 * pData [I] Data Structure for the OLESTREAM Data
6578 * RETURNS
6579 * Success: S_OK
6580 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6581 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6583 * NOTES
6584 * This function is used by OleConvertOLESTREAMToIStorage only.
6586 * Memory allocated for pData must be freed by the caller
6588 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6590 DWORD dwSize;
6591 HRESULT hRes = S_OK;
6592 int nTryCnt=0;
6593 int max_try = 6;
6595 pData->pData = NULL;
6596 pData->pstrOleObjFileName = (CHAR *) NULL;
6598 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6600 /* Get the OleID */
6601 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6602 if(dwSize != sizeof(pData->dwOleID))
6604 hRes = CONVERT10_E_OLESTREAM_GET;
6606 else if(pData->dwOleID != OLESTREAM_ID)
6608 hRes = CONVERT10_E_OLESTREAM_FMT;
6610 else
6612 hRes = S_OK;
6613 break;
6617 if(hRes == S_OK)
6619 /* Get the TypeID...more info needed for this field */
6620 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6621 if(dwSize != sizeof(pData->dwTypeID))
6623 hRes = CONVERT10_E_OLESTREAM_GET;
6626 if(hRes == S_OK)
6628 if(pData->dwTypeID != 0)
6630 /* Get the length of the OleTypeName */
6631 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6632 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6634 hRes = CONVERT10_E_OLESTREAM_GET;
6637 if(hRes == S_OK)
6639 if(pData->dwOleTypeNameLength > 0)
6641 /* Get the OleTypeName */
6642 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6643 if(dwSize != pData->dwOleTypeNameLength)
6645 hRes = CONVERT10_E_OLESTREAM_GET;
6649 if(bStrem1)
6651 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6652 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6654 hRes = CONVERT10_E_OLESTREAM_GET;
6656 if(hRes == S_OK)
6658 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6659 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6660 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6661 if(pData->pstrOleObjFileName)
6663 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6664 if(dwSize != pData->dwOleObjFileNameLength)
6666 hRes = CONVERT10_E_OLESTREAM_GET;
6669 else
6670 hRes = CONVERT10_E_OLESTREAM_GET;
6673 else
6675 /* Get the Width of the Metafile */
6676 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6677 if(dwSize != sizeof(pData->dwMetaFileWidth))
6679 hRes = CONVERT10_E_OLESTREAM_GET;
6681 if(hRes == S_OK)
6683 /* Get the Height of the Metafile */
6684 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6685 if(dwSize != sizeof(pData->dwMetaFileHeight))
6687 hRes = CONVERT10_E_OLESTREAM_GET;
6691 if(hRes == S_OK)
6693 /* Get the Length of the Data */
6694 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6695 if(dwSize != sizeof(pData->dwDataLength))
6697 hRes = CONVERT10_E_OLESTREAM_GET;
6701 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6703 if(!bStrem1) /* if it is a second OLE stream data */
6705 pData->dwDataLength -= 8;
6706 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6707 if(dwSize != sizeof(pData->strUnknown))
6709 hRes = CONVERT10_E_OLESTREAM_GET;
6713 if(hRes == S_OK)
6715 if(pData->dwDataLength > 0)
6717 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6719 /* Get Data (ex. IStorage, Metafile, or BMP) */
6720 if(pData->pData)
6722 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6723 if(dwSize != pData->dwDataLength)
6725 hRes = CONVERT10_E_OLESTREAM_GET;
6728 else
6730 hRes = CONVERT10_E_OLESTREAM_GET;
6736 return hRes;
6739 /*************************************************************************
6740 * OLECONVERT_SaveOLE10 [Internal]
6742 * Saves the OLE10 STREAM From memory
6744 * PARAMS
6745 * pData [I] Data Structure for the OLESTREAM Data
6746 * pOleStream [I] The OLESTREAM to save
6748 * RETURNS
6749 * Success: S_OK
6750 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6752 * NOTES
6753 * This function is used by OleConvertIStorageToOLESTREAM only.
6756 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6758 DWORD dwSize;
6759 HRESULT hRes = S_OK;
6762 /* Set the OleID */
6763 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6764 if(dwSize != sizeof(pData->dwOleID))
6766 hRes = CONVERT10_E_OLESTREAM_PUT;
6769 if(hRes == S_OK)
6771 /* Set the TypeID */
6772 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6773 if(dwSize != sizeof(pData->dwTypeID))
6775 hRes = CONVERT10_E_OLESTREAM_PUT;
6779 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6781 /* Set the Length of the OleTypeName */
6782 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6783 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6785 hRes = CONVERT10_E_OLESTREAM_PUT;
6788 if(hRes == S_OK)
6790 if(pData->dwOleTypeNameLength > 0)
6792 /* Set the OleTypeName */
6793 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6794 if(dwSize != pData->dwOleTypeNameLength)
6796 hRes = CONVERT10_E_OLESTREAM_PUT;
6801 if(hRes == S_OK)
6803 /* Set the width of the Metafile */
6804 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6805 if(dwSize != sizeof(pData->dwMetaFileWidth))
6807 hRes = CONVERT10_E_OLESTREAM_PUT;
6811 if(hRes == S_OK)
6813 /* Set the height of the Metafile */
6814 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6815 if(dwSize != sizeof(pData->dwMetaFileHeight))
6817 hRes = CONVERT10_E_OLESTREAM_PUT;
6821 if(hRes == S_OK)
6823 /* Set the length of the Data */
6824 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6825 if(dwSize != sizeof(pData->dwDataLength))
6827 hRes = CONVERT10_E_OLESTREAM_PUT;
6831 if(hRes == S_OK)
6833 if(pData->dwDataLength > 0)
6835 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6836 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6837 if(dwSize != pData->dwDataLength)
6839 hRes = CONVERT10_E_OLESTREAM_PUT;
6844 return hRes;
6847 /*************************************************************************
6848 * OLECONVERT_GetOLE20FromOLE10[Internal]
6850 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6851 * opens it, and copies the content to the dest IStorage for
6852 * OleConvertOLESTREAMToIStorage
6855 * PARAMS
6856 * pDestStorage [I] The IStorage to copy the data to
6857 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6858 * nBufferLength [I] The size of the buffer
6860 * RETURNS
6861 * Nothing
6863 * NOTES
6867 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6869 HRESULT hRes;
6870 HANDLE hFile;
6871 IStorage *pTempStorage;
6872 DWORD dwNumOfBytesWritten;
6873 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6874 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6876 /* Create a temp File */
6877 GetTempPathW(MAX_PATH, wstrTempDir);
6878 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6879 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6881 if(hFile != INVALID_HANDLE_VALUE)
6883 /* Write IStorage Data to File */
6884 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6885 CloseHandle(hFile);
6887 /* Open and copy temp storage to the Dest Storage */
6888 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6889 if(hRes == S_OK)
6891 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6892 StorageBaseImpl_Release(pTempStorage);
6894 DeleteFileW(wstrTempFile);
6899 /*************************************************************************
6900 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6902 * Saves the OLE10 STREAM From memory
6904 * PARAMS
6905 * pStorage [I] The Src IStorage to copy
6906 * pData [I] The Dest Memory to write to.
6908 * RETURNS
6909 * The size in bytes allocated for pData
6911 * NOTES
6912 * Memory allocated for pData must be freed by the caller
6914 * Used by OleConvertIStorageToOLESTREAM only.
6917 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6919 HANDLE hFile;
6920 HRESULT hRes;
6921 DWORD nDataLength = 0;
6922 IStorage *pTempStorage;
6923 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6924 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6926 *pData = NULL;
6928 /* Create temp Storage */
6929 GetTempPathW(MAX_PATH, wstrTempDir);
6930 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6931 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6933 if(hRes == S_OK)
6935 /* Copy Src Storage to the Temp Storage */
6936 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6937 StorageBaseImpl_Release(pTempStorage);
6939 /* Open Temp Storage as a file and copy to memory */
6940 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6941 if(hFile != INVALID_HANDLE_VALUE)
6943 nDataLength = GetFileSize(hFile, NULL);
6944 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6945 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6946 CloseHandle(hFile);
6948 DeleteFileW(wstrTempFile);
6950 return nDataLength;
6953 /*************************************************************************
6954 * OLECONVERT_CreateOleStream [Internal]
6956 * Creates the "\001OLE" stream in the IStorage if necessary.
6958 * PARAMS
6959 * pStorage [I] Dest storage to create the stream in
6961 * RETURNS
6962 * Nothing
6964 * NOTES
6965 * This function is used by OleConvertOLESTREAMToIStorage only.
6967 * This stream is still unknown, MS Word seems to have extra data
6968 * but since the data is stored in the OLESTREAM there should be
6969 * no need to recreate the stream. If the stream is manually
6970 * deleted it will create it with this default data.
6973 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6975 HRESULT hRes;
6976 IStream *pStream;
6977 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6978 BYTE pOleStreamHeader [] =
6980 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6981 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6982 0x00, 0x00, 0x00, 0x00
6985 /* Create stream if not present */
6986 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6987 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6989 if(hRes == S_OK)
6991 /* Write default Data */
6992 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6993 IStream_Release(pStream);
6997 /* write a string to a stream, preceded by its length */
6998 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7000 HRESULT r;
7001 LPSTR str;
7002 DWORD len = 0;
7004 if( string )
7005 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7006 r = IStream_Write( stm, &len, sizeof(len), NULL);
7007 if( FAILED( r ) )
7008 return r;
7009 if(len == 0)
7010 return r;
7011 str = CoTaskMemAlloc( len );
7012 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7013 r = IStream_Write( stm, str, len, NULL);
7014 CoTaskMemFree( str );
7015 return r;
7018 /* read a string preceded by its length from a stream */
7019 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7021 HRESULT r;
7022 DWORD len, count = 0;
7023 LPSTR str;
7024 LPWSTR wstr;
7026 r = IStream_Read( stm, &len, sizeof(len), &count );
7027 if( FAILED( r ) )
7028 return r;
7029 if( count != sizeof(len) )
7030 return E_OUTOFMEMORY;
7032 TRACE("%d bytes\n",len);
7034 str = CoTaskMemAlloc( len );
7035 if( !str )
7036 return E_OUTOFMEMORY;
7037 count = 0;
7038 r = IStream_Read( stm, str, len, &count );
7039 if( FAILED( r ) )
7040 return r;
7041 if( count != len )
7043 CoTaskMemFree( str );
7044 return E_OUTOFMEMORY;
7047 TRACE("Read string %s\n",debugstr_an(str,len));
7049 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7050 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7051 if( wstr )
7052 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7053 CoTaskMemFree( str );
7055 *string = wstr;
7057 return r;
7061 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7062 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7064 IStream *pstm;
7065 HRESULT r = S_OK;
7066 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7068 static const BYTE unknown1[12] =
7069 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7070 0xFF, 0xFF, 0xFF, 0xFF};
7071 static const BYTE unknown2[16] =
7072 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7073 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7075 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7076 debugstr_w(lpszUserType), debugstr_w(szClipName),
7077 debugstr_w(szProgIDName));
7079 /* Create a CompObj stream if it doesn't exist */
7080 r = IStorage_CreateStream(pstg, szwStreamName,
7081 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7082 if( FAILED (r) )
7083 return r;
7085 /* Write CompObj Structure to stream */
7086 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7088 if( SUCCEEDED( r ) )
7089 r = WriteClassStm( pstm, clsid );
7091 if( SUCCEEDED( r ) )
7092 r = STREAM_WriteString( pstm, lpszUserType );
7093 if( SUCCEEDED( r ) )
7094 r = STREAM_WriteString( pstm, szClipName );
7095 if( SUCCEEDED( r ) )
7096 r = STREAM_WriteString( pstm, szProgIDName );
7097 if( SUCCEEDED( r ) )
7098 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7100 IStream_Release( pstm );
7102 return r;
7105 /***********************************************************************
7106 * WriteFmtUserTypeStg (OLE32.@)
7108 HRESULT WINAPI WriteFmtUserTypeStg(
7109 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7111 HRESULT r;
7112 WCHAR szwClipName[0x40];
7113 CLSID clsid = CLSID_NULL;
7114 LPWSTR wstrProgID = NULL;
7115 DWORD n;
7117 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7119 /* get the clipboard format name */
7120 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7121 szwClipName[n]=0;
7123 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7125 /* FIXME: There's room to save a CLSID and its ProgID, but
7126 the CLSID is not looked up in the registry and in all the
7127 tests I wrote it was CLSID_NULL. Where does it come from?
7130 /* get the real program ID. This may fail, but that's fine */
7131 ProgIDFromCLSID(&clsid, &wstrProgID);
7133 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7135 r = STORAGE_WriteCompObj( pstg, &clsid,
7136 lpszUserType, szwClipName, wstrProgID );
7138 CoTaskMemFree(wstrProgID);
7140 return r;
7144 /******************************************************************************
7145 * ReadFmtUserTypeStg [OLE32.@]
7147 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7149 HRESULT r;
7150 IStream *stm = 0;
7151 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7152 unsigned char unknown1[12];
7153 unsigned char unknown2[16];
7154 DWORD count;
7155 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7156 CLSID clsid;
7158 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7160 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7161 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7162 if( FAILED ( r ) )
7164 WARN("Failed to open stream r = %08x\n", r);
7165 return r;
7168 /* read the various parts of the structure */
7169 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7170 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7171 goto end;
7172 r = ReadClassStm( stm, &clsid );
7173 if( FAILED( r ) )
7174 goto end;
7176 r = STREAM_ReadString( stm, &szCLSIDName );
7177 if( FAILED( r ) )
7178 goto end;
7180 r = STREAM_ReadString( stm, &szOleTypeName );
7181 if( FAILED( r ) )
7182 goto end;
7184 r = STREAM_ReadString( stm, &szProgIDName );
7185 if( FAILED( r ) )
7186 goto end;
7188 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7189 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7190 goto end;
7192 /* ok, success... now we just need to store what we found */
7193 if( pcf )
7194 *pcf = RegisterClipboardFormatW( szOleTypeName );
7195 CoTaskMemFree( szOleTypeName );
7197 if( lplpszUserType )
7198 *lplpszUserType = szCLSIDName;
7199 CoTaskMemFree( szProgIDName );
7201 end:
7202 IStream_Release( stm );
7204 return r;
7208 /*************************************************************************
7209 * OLECONVERT_CreateCompObjStream [Internal]
7211 * Creates a "\001CompObj" is the destination IStorage if necessary.
7213 * PARAMS
7214 * pStorage [I] The dest IStorage to create the CompObj Stream
7215 * if necessary.
7216 * strOleTypeName [I] The ProgID
7218 * RETURNS
7219 * Success: S_OK
7220 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7222 * NOTES
7223 * This function is used by OleConvertOLESTREAMToIStorage only.
7225 * The stream data is stored in the OLESTREAM and there should be
7226 * no need to recreate the stream. If the stream is manually
7227 * deleted it will attempt to create it by querying the registry.
7231 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7233 IStream *pStream;
7234 HRESULT hStorageRes, hRes = S_OK;
7235 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7236 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7237 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7239 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7240 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7242 /* Initialize the CompObj structure */
7243 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7244 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7245 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7248 /* Create a CompObj stream if it doesn't exist */
7249 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7250 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7251 if(hStorageRes == S_OK)
7253 /* copy the OleTypeName to the compobj struct */
7254 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7255 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7257 /* copy the OleTypeName to the compobj struct */
7258 /* Note: in the test made, these were Identical */
7259 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7260 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7262 /* Get the CLSID */
7263 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7264 bufferW, OLESTREAM_MAX_STR_LEN );
7265 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7267 if(hRes == S_OK)
7269 HKEY hKey;
7270 LONG hErr;
7271 /* Get the CLSID Default Name from the Registry */
7272 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7273 if(hErr == ERROR_SUCCESS)
7275 char strTemp[OLESTREAM_MAX_STR_LEN];
7276 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7277 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7278 if(hErr == ERROR_SUCCESS)
7280 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7282 RegCloseKey(hKey);
7286 /* Write CompObj Structure to stream */
7287 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7289 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7291 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7292 if(IStorageCompObj.dwCLSIDNameLength > 0)
7294 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7296 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7297 if(IStorageCompObj.dwOleTypeNameLength > 0)
7299 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7301 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7302 if(IStorageCompObj.dwProgIDNameLength > 0)
7304 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7306 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7307 IStream_Release(pStream);
7309 return hRes;
7313 /*************************************************************************
7314 * OLECONVERT_CreateOlePresStream[Internal]
7316 * Creates the "\002OlePres000" Stream with the Metafile data
7318 * PARAMS
7319 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7320 * dwExtentX [I] Width of the Metafile
7321 * dwExtentY [I] Height of the Metafile
7322 * pData [I] Metafile data
7323 * dwDataLength [I] Size of the Metafile data
7325 * RETURNS
7326 * Success: S_OK
7327 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7329 * NOTES
7330 * This function is used by OleConvertOLESTREAMToIStorage only.
7333 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7335 HRESULT hRes;
7336 IStream *pStream;
7337 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7338 BYTE pOlePresStreamHeader [] =
7340 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7341 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7342 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7343 0x00, 0x00, 0x00, 0x00
7346 BYTE pOlePresStreamHeaderEmpty [] =
7348 0x00, 0x00, 0x00, 0x00,
7349 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7350 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7351 0x00, 0x00, 0x00, 0x00
7354 /* Create the OlePres000 Stream */
7355 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7356 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7358 if(hRes == S_OK)
7360 DWORD nHeaderSize;
7361 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7363 memset(&OlePres, 0, sizeof(OlePres));
7364 /* Do we have any metafile data to save */
7365 if(dwDataLength > 0)
7367 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7368 nHeaderSize = sizeof(pOlePresStreamHeader);
7370 else
7372 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7373 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7375 /* Set width and height of the metafile */
7376 OlePres.dwExtentX = dwExtentX;
7377 OlePres.dwExtentY = -dwExtentY;
7379 /* Set Data and Length */
7380 if(dwDataLength > sizeof(METAFILEPICT16))
7382 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7383 OlePres.pData = &(pData[8]);
7385 /* Save OlePres000 Data to Stream */
7386 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7387 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7388 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7389 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7390 if(OlePres.dwSize > 0)
7392 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7394 IStream_Release(pStream);
7398 /*************************************************************************
7399 * OLECONVERT_CreateOle10NativeStream [Internal]
7401 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7403 * PARAMS
7404 * pStorage [I] Dest storage to create the stream in
7405 * pData [I] Ole10 Native Data (ex. bmp)
7406 * dwDataLength [I] Size of the Ole10 Native Data
7408 * RETURNS
7409 * Nothing
7411 * NOTES
7412 * This function is used by OleConvertOLESTREAMToIStorage only.
7414 * Might need to verify the data and return appropriate error message
7417 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7419 HRESULT hRes;
7420 IStream *pStream;
7421 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7423 /* Create the Ole10Native Stream */
7424 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7425 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7427 if(hRes == S_OK)
7429 /* Write info to stream */
7430 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7431 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7432 IStream_Release(pStream);
7437 /*************************************************************************
7438 * OLECONVERT_GetOLE10ProgID [Internal]
7440 * Finds the ProgID (or OleTypeID) from the IStorage
7442 * PARAMS
7443 * pStorage [I] The Src IStorage to get the ProgID
7444 * strProgID [I] the ProgID string to get
7445 * dwSize [I] the size of the string
7447 * RETURNS
7448 * Success: S_OK
7449 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7451 * NOTES
7452 * This function is used by OleConvertIStorageToOLESTREAM only.
7456 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7458 HRESULT hRes;
7459 IStream *pStream;
7460 LARGE_INTEGER iSeekPos;
7461 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7462 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7464 /* Open the CompObj Stream */
7465 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7466 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7467 if(hRes == S_OK)
7470 /*Get the OleType from the CompObj Stream */
7471 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7472 iSeekPos.u.HighPart = 0;
7474 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7475 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7476 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7477 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7478 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7479 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7480 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7482 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7483 if(*dwSize > 0)
7485 IStream_Read(pStream, strProgID, *dwSize, NULL);
7487 IStream_Release(pStream);
7489 else
7491 STATSTG stat;
7492 LPOLESTR wstrProgID;
7494 /* Get the OleType from the registry */
7495 REFCLSID clsid = &(stat.clsid);
7496 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7497 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7498 if(hRes == S_OK)
7500 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7504 return hRes;
7507 /*************************************************************************
7508 * OLECONVERT_GetOle10PresData [Internal]
7510 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7512 * PARAMS
7513 * pStorage [I] Src IStroage
7514 * pOleStream [I] Dest OleStream Mem Struct
7516 * RETURNS
7517 * Nothing
7519 * NOTES
7520 * This function is used by OleConvertIStorageToOLESTREAM only.
7522 * Memory allocated for pData must be freed by the caller
7526 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7529 HRESULT hRes;
7530 IStream *pStream;
7531 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7533 /* Initialize Default data for OLESTREAM */
7534 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7535 pOleStreamData[0].dwTypeID = 2;
7536 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7537 pOleStreamData[1].dwTypeID = 0;
7538 pOleStreamData[0].dwMetaFileWidth = 0;
7539 pOleStreamData[0].dwMetaFileHeight = 0;
7540 pOleStreamData[0].pData = NULL;
7541 pOleStreamData[1].pData = NULL;
7543 /* Open Ole10Native Stream */
7544 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7545 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7546 if(hRes == S_OK)
7549 /* Read Size and Data */
7550 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7551 if(pOleStreamData->dwDataLength > 0)
7553 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7554 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7556 IStream_Release(pStream);
7562 /*************************************************************************
7563 * OLECONVERT_GetOle20PresData[Internal]
7565 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7567 * PARAMS
7568 * pStorage [I] Src IStroage
7569 * pOleStreamData [I] Dest OleStream Mem Struct
7571 * RETURNS
7572 * Nothing
7574 * NOTES
7575 * This function is used by OleConvertIStorageToOLESTREAM only.
7577 * Memory allocated for pData must be freed by the caller
7579 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7581 HRESULT hRes;
7582 IStream *pStream;
7583 OLECONVERT_ISTORAGE_OLEPRES olePress;
7584 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7586 /* Initialize Default data for OLESTREAM */
7587 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7588 pOleStreamData[0].dwTypeID = 2;
7589 pOleStreamData[0].dwMetaFileWidth = 0;
7590 pOleStreamData[0].dwMetaFileHeight = 0;
7591 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7592 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7593 pOleStreamData[1].dwTypeID = 0;
7594 pOleStreamData[1].dwOleTypeNameLength = 0;
7595 pOleStreamData[1].strOleTypeName[0] = 0;
7596 pOleStreamData[1].dwMetaFileWidth = 0;
7597 pOleStreamData[1].dwMetaFileHeight = 0;
7598 pOleStreamData[1].pData = NULL;
7599 pOleStreamData[1].dwDataLength = 0;
7602 /* Open OlePress000 stream */
7603 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7604 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7605 if(hRes == S_OK)
7607 LARGE_INTEGER iSeekPos;
7608 METAFILEPICT16 MetaFilePict;
7609 static const char strMetafilePictName[] = "METAFILEPICT";
7611 /* Set the TypeID for a Metafile */
7612 pOleStreamData[1].dwTypeID = 5;
7614 /* Set the OleTypeName to Metafile */
7615 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7616 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7618 iSeekPos.u.HighPart = 0;
7619 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7621 /* Get Presentation Data */
7622 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7623 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7624 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7625 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7627 /*Set width and Height */
7628 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7629 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7630 if(olePress.dwSize > 0)
7632 /* Set Length */
7633 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7635 /* Set MetaFilePict struct */
7636 MetaFilePict.mm = 8;
7637 MetaFilePict.xExt = olePress.dwExtentX;
7638 MetaFilePict.yExt = olePress.dwExtentY;
7639 MetaFilePict.hMF = 0;
7641 /* Get Metafile Data */
7642 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7643 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7644 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7646 IStream_Release(pStream);
7650 /*************************************************************************
7651 * OleConvertOLESTREAMToIStorage [OLE32.@]
7653 * Read info on MSDN
7655 * TODO
7656 * DVTARGETDEVICE paramenter is not handled
7657 * Still unsure of some mem fields for OLE 10 Stream
7658 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7659 * and "\001OLE" streams
7662 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7663 LPOLESTREAM pOleStream,
7664 LPSTORAGE pstg,
7665 const DVTARGETDEVICE* ptd)
7667 int i;
7668 HRESULT hRes=S_OK;
7669 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7671 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7673 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7675 if(ptd != NULL)
7677 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7680 if(pstg == NULL || pOleStream == NULL)
7682 hRes = E_INVALIDARG;
7685 if(hRes == S_OK)
7687 /* Load the OLESTREAM to Memory */
7688 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7691 if(hRes == S_OK)
7693 /* Load the OLESTREAM to Memory (part 2)*/
7694 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7697 if(hRes == S_OK)
7700 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7702 /* Do we have the IStorage Data in the OLESTREAM */
7703 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7705 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7706 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7708 else
7710 /* It must be an original OLE 1.0 source */
7711 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7714 else
7716 /* It must be an original OLE 1.0 source */
7717 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7720 /* Create CompObj Stream if necessary */
7721 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7722 if(hRes == S_OK)
7724 /*Create the Ole Stream if necessary */
7725 OLECONVERT_CreateOleStream(pstg);
7730 /* Free allocated memory */
7731 for(i=0; i < 2; i++)
7733 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7734 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7735 pOleStreamData[i].pstrOleObjFileName = NULL;
7737 return hRes;
7740 /*************************************************************************
7741 * OleConvertIStorageToOLESTREAM [OLE32.@]
7743 * Read info on MSDN
7745 * Read info on MSDN
7747 * TODO
7748 * Still unsure of some mem fields for OLE 10 Stream
7749 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7750 * and "\001OLE" streams.
7753 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7754 LPSTORAGE pstg,
7755 LPOLESTREAM pOleStream)
7757 int i;
7758 HRESULT hRes = S_OK;
7759 IStream *pStream;
7760 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7761 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7763 TRACE("%p %p\n", pstg, pOleStream);
7765 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7767 if(pstg == NULL || pOleStream == NULL)
7769 hRes = E_INVALIDARG;
7771 if(hRes == S_OK)
7773 /* Get the ProgID */
7774 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7775 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7777 if(hRes == S_OK)
7779 /* Was it originally Ole10 */
7780 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7781 if(hRes == S_OK)
7783 IStream_Release(pStream);
7784 /* Get Presentation Data for Ole10Native */
7785 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7787 else
7789 /* Get Presentation Data (OLE20) */
7790 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7793 /* Save OLESTREAM */
7794 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7795 if(hRes == S_OK)
7797 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7802 /* Free allocated memory */
7803 for(i=0; i < 2; i++)
7805 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7808 return hRes;
7811 /***********************************************************************
7812 * GetConvertStg (OLE32.@)
7814 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7815 FIXME("unimplemented stub!\n");
7816 return E_FAIL;
7819 /******************************************************************************
7820 * StgIsStorageFile [OLE32.@]
7821 * Verify if the file contains a storage object
7823 * PARAMS
7824 * fn [ I] Filename
7826 * RETURNS
7827 * S_OK if file has magic bytes as a storage object
7828 * S_FALSE if file is not storage
7830 HRESULT WINAPI
7831 StgIsStorageFile(LPCOLESTR fn)
7833 HANDLE hf;
7834 BYTE magic[8];
7835 DWORD bytes_read;
7837 TRACE("%s\n", debugstr_w(fn));
7838 hf = CreateFileW(fn, GENERIC_READ,
7839 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7840 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7842 if (hf == INVALID_HANDLE_VALUE)
7843 return STG_E_FILENOTFOUND;
7845 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7847 WARN(" unable to read file\n");
7848 CloseHandle(hf);
7849 return S_FALSE;
7852 CloseHandle(hf);
7854 if (bytes_read != 8) {
7855 WARN(" too short\n");
7856 return S_FALSE;
7859 if (!memcmp(magic,STORAGE_magic,8)) {
7860 WARN(" -> YES\n");
7861 return S_OK;
7864 WARN(" -> Invalid header.\n");
7865 return S_FALSE;
7868 /***********************************************************************
7869 * WriteClassStm (OLE32.@)
7871 * Writes a CLSID to a stream.
7873 * PARAMS
7874 * pStm [I] Stream to write to.
7875 * rclsid [I] CLSID to write.
7877 * RETURNS
7878 * Success: S_OK.
7879 * Failure: HRESULT code.
7881 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7883 TRACE("(%p,%p)\n",pStm,rclsid);
7885 if (!pStm || !rclsid)
7886 return E_INVALIDARG;
7888 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7891 /***********************************************************************
7892 * ReadClassStm (OLE32.@)
7894 * Reads a CLSID from a stream.
7896 * PARAMS
7897 * pStm [I] Stream to read from.
7898 * rclsid [O] CLSID to read.
7900 * RETURNS
7901 * Success: S_OK.
7902 * Failure: HRESULT code.
7904 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7906 ULONG nbByte;
7907 HRESULT res;
7909 TRACE("(%p,%p)\n",pStm,pclsid);
7911 if (!pStm || !pclsid)
7912 return E_INVALIDARG;
7914 /* clear the output args */
7915 memcpy(pclsid, &CLSID_NULL, sizeof(*pclsid));
7917 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7919 if (FAILED(res))
7920 return res;
7922 if (nbByte != sizeof(CLSID))
7923 return STG_E_READFAULT;
7924 else
7925 return S_OK;