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
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.
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic
[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic
[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootEntryName
[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base
;
82 * Entry in the parent's stream tracking list
84 struct list ParentListEntry
;
86 typedef struct StorageInternalImpl StorageInternalImpl
;
88 /* Method definitions for the Storage32InternalImpl class. */
89 static StorageInternalImpl
* StorageInternalImpl_Construct(StorageImpl
* ancestorStorage
,
90 DWORD openFlags
, DirRef storageDirEntry
);
91 static void StorageImpl_Destroy(StorageBaseImpl
* iface
);
92 static BOOL
StorageImpl_ReadBigBlock(StorageImpl
* This
, ULONG blockIndex
, void* buffer
);
93 static BOOL
StorageImpl_WriteBigBlock(StorageImpl
* This
, ULONG blockIndex
, const void* buffer
);
94 static void StorageImpl_SetNextBlockInChain(StorageImpl
* This
, ULONG blockIndex
, ULONG nextBlock
);
95 static HRESULT
StorageImpl_LoadFileHeader(StorageImpl
* This
);
96 static void StorageImpl_SaveFileHeader(StorageImpl
* This
);
98 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
);
99 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
);
100 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
);
101 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
);
102 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
);
104 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
);
105 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
);
106 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
);
108 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
);
109 static ULONG
SmallBlockChainStream_GetHeadOfChain(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 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
);
116 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
);
117 static void StorageInternalImpl_Invalidate( StorageInternalImpl
*This
);
119 /* OLESTREAM memory structure to use for Get and Put Routines */
120 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
125 DWORD dwOleTypeNameLength
;
126 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
127 CHAR
*pstrOleObjFileName
;
128 DWORD dwOleObjFileNameLength
;
129 DWORD dwMetaFileWidth
;
130 DWORD dwMetaFileHeight
;
131 CHAR strUnknown
[8]; /* don't know what this 8 byte information in OLE stream is. */
134 }OLECONVERT_OLESTREAM_DATA
;
136 /* CompObj Stream structure */
137 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
142 DWORD dwCLSIDNameLength
;
143 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
144 DWORD dwOleTypeNameLength
;
145 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
146 DWORD dwProgIDNameLength
;
147 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
149 }OLECONVERT_ISTORAGE_COMPOBJ
;
152 /* Ole Presentation Stream structure */
153 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
161 }OLECONVERT_ISTORAGE_OLEPRES
;
165 /***********************************************************************
166 * Forward declaration of internal functions used by the method DestroyElement
168 static HRESULT
deleteStorageContents(
169 StorageBaseImpl
*parentStorage
,
170 DirRef indexToDelete
,
171 DirEntry entryDataToDelete
);
173 static HRESULT
deleteStreamContents(
174 StorageBaseImpl
*parentStorage
,
175 DirRef indexToDelete
,
176 DirEntry entryDataToDelete
);
178 static HRESULT
removeFromTree(
180 DirRef parentStorageIndex
,
181 DirRef deletedIndex
);
183 /***********************************************************************
184 * Declaration of the functions used to manipulate DirEntry
187 static HRESULT
destroyDirEntry(
188 StorageImpl
*storage
,
191 static HRESULT
insertIntoTree(
193 DirRef parentStorageIndex
,
194 DirRef newEntryIndex
);
196 static LONG
entryNameCmp(
197 const OLECHAR
*name1
,
198 const OLECHAR
*name2
);
200 static DirRef
findElement(
201 StorageImpl
*storage
,
206 static HRESULT
findTreeParent(
207 StorageImpl
*storage
,
209 const OLECHAR
*childName
,
210 DirEntry
*parentData
,
214 /***********************************************************************
215 * Declaration of miscellaneous functions...
217 static HRESULT
validateSTGM(DWORD stgmValue
);
219 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
220 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
221 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
223 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
226 /****************************************************************************
227 * IEnumSTATSTGImpl definitions.
229 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
230 * This class allows iterating through the content of a storage and to find
231 * specific items inside it.
233 struct IEnumSTATSTGImpl
235 const IEnumSTATSTGVtbl
*lpVtbl
; /* Needs to be the first item in the struct
236 * since we want to cast this in an IEnumSTATSTG pointer */
238 LONG ref
; /* Reference count */
239 StorageImpl
* parentStorage
; /* Reference to the parent storage */
240 DirRef storageDirEntry
; /* Directory entry of the storage to enumerate */
243 * The current implementation of the IEnumSTATSTGImpl class uses a stack
244 * to walk the directory entries to get the content of a storage. This stack
245 * is implemented by the following 3 data members
249 DirRef
* stackToVisit
;
251 #define ENUMSTATSGT_SIZE_INCREMENT 10
255 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(StorageImpl
* This
, DirRef storageDirEntry
);
256 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
);
257 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl
* This
, DirRef nodeToPush
);
258 static DirRef
IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl
* This
, BOOL remove
);
260 /************************************************************************
264 static ULONG
BLOCK_GetBigBlockOffset(ULONG index
)
266 if (index
== 0xffffffff)
271 return index
* BIG_BLOCK_SIZE
;
274 /************************************************************************
275 ** Storage32BaseImpl implementation
277 static HRESULT
StorageImpl_ReadAt(StorageImpl
* This
,
278 ULARGE_INTEGER offset
,
283 return BIGBLOCKFILE_ReadAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesRead
);
286 static HRESULT
StorageImpl_WriteAt(StorageImpl
* This
,
287 ULARGE_INTEGER offset
,
292 return BIGBLOCKFILE_WriteAt(This
->bigBlockFile
,offset
,buffer
,size
,bytesWritten
);
295 /************************************************************************
296 * Storage32BaseImpl_QueryInterface (IUnknown)
298 * This method implements the common QueryInterface for all IStorage32
299 * implementations contained in this file.
301 * See Windows documentation for more details on IUnknown methods.
303 static HRESULT WINAPI
StorageBaseImpl_QueryInterface(
308 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
310 if ( (This
==0) || (ppvObject
==0) )
315 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
316 IsEqualGUID(&IID_IStorage
, riid
))
320 else if (IsEqualGUID(&IID_IPropertySetStorage
, riid
))
322 *ppvObject
= &This
->pssVtbl
;
326 return E_NOINTERFACE
;
328 IStorage_AddRef(iface
);
333 /************************************************************************
334 * Storage32BaseImpl_AddRef (IUnknown)
336 * This method implements the common AddRef for all IStorage32
337 * implementations contained in this file.
339 * See Windows documentation for more details on IUnknown methods.
341 static ULONG WINAPI
StorageBaseImpl_AddRef(
344 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
345 ULONG ref
= InterlockedIncrement(&This
->ref
);
347 TRACE("(%p) AddRef to %d\n", This
, ref
);
352 /************************************************************************
353 * Storage32BaseImpl_Release (IUnknown)
355 * This method implements the common Release for all IStorage32
356 * implementations contained in this file.
358 * See Windows documentation for more details on IUnknown methods.
360 static ULONG WINAPI
StorageBaseImpl_Release(
363 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
365 ULONG ref
= InterlockedDecrement(&This
->ref
);
367 TRACE("(%p) ReleaseRef to %d\n", This
, ref
);
372 * Since we are using a system of base-classes, we want to call the
373 * destructor of the appropriate derived class. To do this, we are
374 * using virtual functions to implement the destructor.
376 StorageBaseImpl_Destroy(This
);
382 /************************************************************************
383 * Storage32BaseImpl_OpenStream (IStorage)
385 * This method will open the specified stream object from the current storage.
387 * See Windows documentation for more details on IStorage methods.
389 static HRESULT WINAPI
StorageBaseImpl_OpenStream(
391 const OLECHAR
* pwcsName
, /* [string][in] */
392 void* reserved1
, /* [unique][in] */
393 DWORD grfMode
, /* [in] */
394 DWORD reserved2
, /* [in] */
395 IStream
** ppstm
) /* [out] */
397 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
398 StgStreamImpl
* newStream
;
399 DirEntry currentEntry
;
400 DirRef streamEntryRef
;
401 HRESULT res
= STG_E_UNKNOWN
;
403 TRACE("(%p, %s, %p, %x, %d, %p)\n",
404 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
406 if ( (pwcsName
==NULL
) || (ppstm
==0) )
414 if ( FAILED( validateSTGM(grfMode
) ) ||
415 STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
417 res
= STG_E_INVALIDFLAG
;
424 if ( (grfMode
& STGM_DELETEONRELEASE
) || (grfMode
& STGM_TRANSACTED
) )
426 res
= STG_E_INVALIDFUNCTION
;
430 if (!This
->ancestorStorage
)
432 res
= STG_E_REVERTED
;
437 * Check that we're compatible with the parent's storage mode, but
438 * only if we are not in transacted mode
440 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
441 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
443 res
= STG_E_ACCESSDENIED
;
449 * Search for the element with the given name
451 streamEntryRef
= findElement(
452 This
->ancestorStorage
,
453 This
->storageDirEntry
,
458 * If it was found, construct the stream object and return a pointer to it.
460 if ( (streamEntryRef
!=DIRENTRY_NULL
) &&
461 (currentEntry
.stgType
==STGTY_STREAM
) )
463 if (StorageBaseImpl_IsStreamOpen(This
, streamEntryRef
))
465 /* A single stream cannot be opened a second time. */
466 res
= STG_E_ACCESSDENIED
;
470 newStream
= StgStreamImpl_Construct(This
, grfMode
, streamEntryRef
);
474 newStream
->grfMode
= grfMode
;
475 *ppstm
= (IStream
*)newStream
;
477 IStream_AddRef(*ppstm
);
487 res
= STG_E_FILENOTFOUND
;
491 TRACE("<-- IStream %p\n", *ppstm
);
492 TRACE("<-- %08x\n", res
);
496 /************************************************************************
497 * Storage32BaseImpl_OpenStorage (IStorage)
499 * This method will open a new storage object from the current storage.
501 * See Windows documentation for more details on IStorage methods.
503 static HRESULT WINAPI
StorageBaseImpl_OpenStorage(
505 const OLECHAR
* pwcsName
, /* [string][unique][in] */
506 IStorage
* pstgPriority
, /* [unique][in] */
507 DWORD grfMode
, /* [in] */
508 SNB snbExclude
, /* [unique][in] */
509 DWORD reserved
, /* [in] */
510 IStorage
** ppstg
) /* [out] */
512 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
513 StorageInternalImpl
* newStorage
;
514 DirEntry currentEntry
;
515 DirRef storageEntryRef
;
516 HRESULT res
= STG_E_UNKNOWN
;
518 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
519 iface
, debugstr_w(pwcsName
), pstgPriority
,
520 grfMode
, snbExclude
, reserved
, ppstg
);
522 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
528 if (This
->openFlags
& STGM_SIMPLE
)
530 res
= STG_E_INVALIDFUNCTION
;
535 if (snbExclude
!= NULL
)
537 res
= STG_E_INVALIDPARAMETER
;
541 if ( FAILED( validateSTGM(grfMode
) ))
543 res
= STG_E_INVALIDFLAG
;
550 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
551 (grfMode
& STGM_DELETEONRELEASE
) ||
552 (grfMode
& STGM_PRIORITY
) )
554 res
= STG_E_INVALIDFUNCTION
;
558 if (!This
->ancestorStorage
)
559 return STG_E_REVERTED
;
562 * Check that we're compatible with the parent's storage mode,
563 * but only if we are not transacted
565 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
566 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
568 res
= STG_E_ACCESSDENIED
;
575 storageEntryRef
= findElement(
576 This
->ancestorStorage
,
577 This
->storageDirEntry
,
581 if ( (storageEntryRef
!=DIRENTRY_NULL
) &&
582 (currentEntry
.stgType
==STGTY_STORAGE
) )
584 if (StorageBaseImpl_IsStorageOpen(This
, storageEntryRef
))
586 /* A single storage cannot be opened a second time. */
587 res
= STG_E_ACCESSDENIED
;
591 newStorage
= StorageInternalImpl_Construct(
592 This
->ancestorStorage
,
598 *ppstg
= (IStorage
*)newStorage
;
600 StorageBaseImpl_AddRef(*ppstg
);
602 list_add_tail(&This
->storageHead
, &newStorage
->ParentListEntry
);
608 res
= STG_E_INSUFFICIENTMEMORY
;
612 res
= STG_E_FILENOTFOUND
;
615 TRACE("<-- %08x\n", res
);
619 /************************************************************************
620 * Storage32BaseImpl_EnumElements (IStorage)
622 * This method will create an enumerator object that can be used to
623 * retrieve information about all the elements in the storage object.
625 * See Windows documentation for more details on IStorage methods.
627 static HRESULT WINAPI
StorageBaseImpl_EnumElements(
629 DWORD reserved1
, /* [in] */
630 void* reserved2
, /* [size_is][unique][in] */
631 DWORD reserved3
, /* [in] */
632 IEnumSTATSTG
** ppenum
) /* [out] */
634 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
635 IEnumSTATSTGImpl
* newEnum
;
637 TRACE("(%p, %d, %p, %d, %p)\n",
638 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
640 if ( (This
==0) || (ppenum
==0))
643 if (!This
->ancestorStorage
)
644 return STG_E_REVERTED
;
646 newEnum
= IEnumSTATSTGImpl_Construct(
647 This
->ancestorStorage
,
648 This
->storageDirEntry
);
652 *ppenum
= (IEnumSTATSTG
*)newEnum
;
654 IEnumSTATSTG_AddRef(*ppenum
);
659 return E_OUTOFMEMORY
;
662 /************************************************************************
663 * Storage32BaseImpl_Stat (IStorage)
665 * This method will retrieve information about this storage object.
667 * See Windows documentation for more details on IStorage methods.
669 static HRESULT WINAPI
StorageBaseImpl_Stat(
671 STATSTG
* pstatstg
, /* [out] */
672 DWORD grfStatFlag
) /* [in] */
674 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
675 DirEntry currentEntry
;
677 HRESULT res
= STG_E_UNKNOWN
;
679 TRACE("(%p, %p, %x)\n",
680 iface
, pstatstg
, grfStatFlag
);
682 if ( (This
==0) || (pstatstg
==0))
688 if (!This
->ancestorStorage
)
690 res
= STG_E_REVERTED
;
694 readSuccessful
= StorageImpl_ReadDirEntry(
695 This
->ancestorStorage
,
696 This
->storageDirEntry
,
701 StorageUtl_CopyDirEntryToSTATSTG(
707 pstatstg
->grfMode
= This
->openFlags
;
708 pstatstg
->grfStateBits
= This
->stateBits
;
719 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
);
721 TRACE("<-- %08x\n", res
);
725 /************************************************************************
726 * Storage32BaseImpl_RenameElement (IStorage)
728 * This method will rename the specified element.
730 * See Windows documentation for more details on IStorage methods.
732 static HRESULT WINAPI
StorageBaseImpl_RenameElement(
734 const OLECHAR
* pwcsOldName
, /* [in] */
735 const OLECHAR
* pwcsNewName
) /* [in] */
737 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
738 DirEntry currentEntry
;
739 DirRef currentEntryRef
;
741 TRACE("(%p, %s, %s)\n",
742 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
744 if (!This
->ancestorStorage
)
745 return STG_E_REVERTED
;
747 currentEntryRef
= findElement(This
->ancestorStorage
,
748 This
->storageDirEntry
,
752 if (currentEntryRef
!= DIRENTRY_NULL
)
755 * There is already an element with the new name
757 return STG_E_FILEALREADYEXISTS
;
761 * Search for the old element name
763 currentEntryRef
= findElement(This
->ancestorStorage
,
764 This
->storageDirEntry
,
768 if (currentEntryRef
!= DIRENTRY_NULL
)
770 if (StorageBaseImpl_IsStreamOpen(This
, currentEntryRef
) ||
771 StorageBaseImpl_IsStorageOpen(This
, currentEntryRef
))
773 WARN("Element is already open; cannot rename.\n");
774 return STG_E_ACCESSDENIED
;
777 /* Remove the element from its current position in the tree */
778 removeFromTree(This
->ancestorStorage
, This
->storageDirEntry
,
781 /* Change the name of the element */
782 strcpyW(currentEntry
.name
, pwcsNewName
);
784 StorageImpl_WriteDirEntry(This
->ancestorStorage
, currentEntryRef
,
787 /* Insert the element in a new position in the tree */
788 insertIntoTree(This
->ancestorStorage
, This
->storageDirEntry
,
794 * There is no element with the old name
796 return STG_E_FILENOTFOUND
;
802 /************************************************************************
803 * Storage32BaseImpl_CreateStream (IStorage)
805 * This method will create a stream object within this storage
807 * See Windows documentation for more details on IStorage methods.
809 static HRESULT WINAPI
StorageBaseImpl_CreateStream(
811 const OLECHAR
* pwcsName
, /* [string][in] */
812 DWORD grfMode
, /* [in] */
813 DWORD reserved1
, /* [in] */
814 DWORD reserved2
, /* [in] */
815 IStream
** ppstm
) /* [out] */
817 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
818 StgStreamImpl
* newStream
;
819 DirEntry currentEntry
, newStreamEntry
;
820 DirRef currentEntryRef
, newStreamEntryRef
;
822 TRACE("(%p, %s, %x, %d, %d, %p)\n",
823 iface
, debugstr_w(pwcsName
), grfMode
,
824 reserved1
, reserved2
, ppstm
);
827 return STG_E_INVALIDPOINTER
;
830 return STG_E_INVALIDNAME
;
832 if (reserved1
|| reserved2
)
833 return STG_E_INVALIDPARAMETER
;
835 if ( FAILED( validateSTGM(grfMode
) ))
836 return STG_E_INVALIDFLAG
;
838 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
839 return STG_E_INVALIDFLAG
;
841 if (!This
->ancestorStorage
)
842 return STG_E_REVERTED
;
847 if ((grfMode
& STGM_DELETEONRELEASE
) ||
848 (grfMode
& STGM_TRANSACTED
))
849 return STG_E_INVALIDFUNCTION
;
851 /* Can't create a stream on read-only storage */
852 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
853 return STG_E_ACCESSDENIED
;
856 * Check that we're compatible with the parent's storage mode
857 * if not in transacted mode
859 if(!(This
->ancestorStorage
->base
.openFlags
& STGM_TRANSACTED
)) {
860 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
861 return STG_E_ACCESSDENIED
;
864 if(This
->openFlags
& STGM_SIMPLE
)
865 if(grfMode
& STGM_CREATE
) return STG_E_INVALIDFLAG
;
869 currentEntryRef
= findElement(This
->ancestorStorage
,
870 This
->storageDirEntry
,
874 if (currentEntryRef
!= DIRENTRY_NULL
)
877 * An element with this name already exists
879 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
881 IStorage_DestroyElement(iface
, pwcsName
);
884 return STG_E_FILEALREADYEXISTS
;
886 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
888 WARN("read-only storage\n");
889 return STG_E_ACCESSDENIED
;
893 * memset the empty entry
895 memset(&newStreamEntry
, 0, sizeof(DirEntry
));
897 newStreamEntry
.sizeOfNameString
=
898 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
900 if (newStreamEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
901 return STG_E_INVALIDNAME
;
903 strcpyW(newStreamEntry
.name
, pwcsName
);
905 newStreamEntry
.stgType
= STGTY_STREAM
;
906 newStreamEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
907 newStreamEntry
.size
.u
.LowPart
= 0;
908 newStreamEntry
.size
.u
.HighPart
= 0;
910 newStreamEntry
.leftChild
= DIRENTRY_NULL
;
911 newStreamEntry
.rightChild
= DIRENTRY_NULL
;
912 newStreamEntry
.dirRootEntry
= DIRENTRY_NULL
;
914 /* call CoFileTime to get the current time
919 /* newStreamEntry.clsid */
922 * Create an entry with the new data
924 StorageBaseImpl_CreateDirEntry(This
, &newStreamEntry
, &newStreamEntryRef
);
926 * Insert the new entry in the parent storage's tree.
929 This
->ancestorStorage
,
930 This
->storageDirEntry
,
934 * Open the stream to return it.
936 newStream
= StgStreamImpl_Construct(This
, grfMode
, newStreamEntryRef
);
940 *ppstm
= (IStream
*)newStream
;
942 IStream_AddRef(*ppstm
);
946 return STG_E_INSUFFICIENTMEMORY
;
952 /************************************************************************
953 * Storage32BaseImpl_SetClass (IStorage)
955 * This method will write the specified CLSID in the directory entry of this
958 * See Windows documentation for more details on IStorage methods.
960 static HRESULT WINAPI
StorageBaseImpl_SetClass(
962 REFCLSID clsid
) /* [in] */
964 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
965 HRESULT hRes
= E_FAIL
;
966 DirEntry currentEntry
;
969 TRACE("(%p, %p)\n", iface
, clsid
);
971 if (!This
->ancestorStorage
)
972 return STG_E_REVERTED
;
974 success
= StorageImpl_ReadDirEntry(This
->ancestorStorage
,
975 This
->storageDirEntry
,
979 currentEntry
.clsid
= *clsid
;
981 hRes
= StorageImpl_WriteDirEntry(This
->ancestorStorage
,
982 This
->storageDirEntry
,
989 /************************************************************************
990 ** Storage32Impl implementation
993 /************************************************************************
994 * Storage32BaseImpl_CreateStorage (IStorage)
996 * This method will create the storage object within the provided storage.
998 * See Windows documentation for more details on IStorage methods.
1000 static HRESULT WINAPI
StorageBaseImpl_CreateStorage(
1002 const OLECHAR
*pwcsName
, /* [string][in] */
1003 DWORD grfMode
, /* [in] */
1004 DWORD reserved1
, /* [in] */
1005 DWORD reserved2
, /* [in] */
1006 IStorage
**ppstg
) /* [out] */
1008 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1010 DirEntry currentEntry
;
1012 DirRef currentEntryRef
;
1016 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1017 iface
, debugstr_w(pwcsName
), grfMode
,
1018 reserved1
, reserved2
, ppstg
);
1021 return STG_E_INVALIDPOINTER
;
1023 if (This
->openFlags
& STGM_SIMPLE
)
1025 return STG_E_INVALIDFUNCTION
;
1029 return STG_E_INVALIDNAME
;
1033 if ( FAILED( validateSTGM(grfMode
) ) ||
1034 (grfMode
& STGM_DELETEONRELEASE
) )
1036 WARN("bad grfMode: 0x%x\n", grfMode
);
1037 return STG_E_INVALIDFLAG
;
1040 if (!This
->ancestorStorage
)
1041 return STG_E_REVERTED
;
1044 * Check that we're compatible with the parent's storage mode
1046 if ( STGM_ACCESS_MODE( grfMode
) > STGM_ACCESS_MODE( This
->openFlags
) )
1048 WARN("access denied\n");
1049 return STG_E_ACCESSDENIED
;
1052 currentEntryRef
= findElement(This
->ancestorStorage
,
1053 This
->storageDirEntry
,
1057 if (currentEntryRef
!= DIRENTRY_NULL
)
1060 * An element with this name already exists
1062 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
&&
1063 STGM_ACCESS_MODE(This
->openFlags
) != STGM_READ
)
1065 hr
= IStorage_DestroyElement(iface
, pwcsName
);
1071 WARN("file already exists\n");
1072 return STG_E_FILEALREADYEXISTS
;
1075 else if (STGM_ACCESS_MODE(This
->openFlags
) == STGM_READ
)
1077 WARN("read-only storage\n");
1078 return STG_E_ACCESSDENIED
;
1081 memset(&newEntry
, 0, sizeof(DirEntry
));
1083 newEntry
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1085 if (newEntry
.sizeOfNameString
> DIRENTRY_NAME_BUFFER_LEN
)
1087 FIXME("name too long\n");
1088 return STG_E_INVALIDNAME
;
1091 strcpyW(newEntry
.name
, pwcsName
);
1093 newEntry
.stgType
= STGTY_STORAGE
;
1094 newEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
1095 newEntry
.size
.u
.LowPart
= 0;
1096 newEntry
.size
.u
.HighPart
= 0;
1098 newEntry
.leftChild
= DIRENTRY_NULL
;
1099 newEntry
.rightChild
= DIRENTRY_NULL
;
1100 newEntry
.dirRootEntry
= DIRENTRY_NULL
;
1102 /* call CoFileTime to get the current time
1107 /* newEntry.clsid */
1110 * Create a new directory entry for the storage
1112 StorageBaseImpl_CreateDirEntry(This
, &newEntry
, &newEntryRef
);
1115 * Insert the new directory entry into the parent storage's tree
1118 This
->ancestorStorage
,
1119 This
->storageDirEntry
,
1123 * Open it to get a pointer to return.
1125 hr
= IStorage_OpenStorage(iface
, pwcsName
, 0, grfMode
, 0, 0, ppstg
);
1127 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1137 /***************************************************************************
1141 * Reserve a directory entry in the file and initialize it.
1143 static HRESULT
StorageImpl_CreateDirEntry(
1144 StorageBaseImpl
*base
,
1145 const DirEntry
*newData
,
1148 StorageImpl
*storage
= (StorageImpl
*)base
;
1149 ULONG currentEntryIndex
= 0;
1150 ULONG newEntryIndex
= DIRENTRY_NULL
;
1152 BYTE currentData
[RAW_DIRENTRY_SIZE
];
1153 WORD sizeOfNameString
;
1157 hr
= StorageImpl_ReadRawDirEntry(storage
,
1163 StorageUtl_ReadWord(
1165 OFFSET_PS_NAMELENGTH
,
1168 if (sizeOfNameString
== 0)
1171 * The entry exists and is available, we found it.
1173 newEntryIndex
= currentEntryIndex
;
1179 * We exhausted the directory entries, we will create more space below
1181 newEntryIndex
= currentEntryIndex
;
1183 currentEntryIndex
++;
1185 } while (newEntryIndex
== DIRENTRY_NULL
);
1188 * grow the directory stream
1192 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1193 ULARGE_INTEGER newSize
;
1195 ULONG lastEntry
= 0;
1196 ULONG blockCount
= 0;
1199 * obtain the new count of blocks in the directory stream
1201 blockCount
= BlockChainStream_GetCount(
1202 storage
->rootBlockChain
)+1;
1205 * initialize the size used by the directory stream
1207 newSize
.u
.HighPart
= 0;
1208 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1211 * add a block to the directory stream
1213 BlockChainStream_SetSize(storage
->rootBlockChain
, newSize
);
1216 * memset the empty entry in order to initialize the unused newly
1219 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1224 lastEntry
= storage
->bigBlockSize
/ RAW_DIRENTRY_SIZE
* blockCount
;
1227 entryIndex
= newEntryIndex
+ 1;
1228 entryIndex
< lastEntry
;
1231 StorageImpl_WriteRawDirEntry(
1238 UpdateRawDirEntry(currentData
, newData
);
1240 hr
= StorageImpl_WriteRawDirEntry(storage
, newEntryIndex
, currentData
);
1243 *index
= newEntryIndex
;
1248 /***************************************************************************
1252 * Mark a directory entry in the file as free.
1254 static HRESULT
destroyDirEntry(
1255 StorageImpl
*storage
,
1259 BYTE emptyData
[RAW_DIRENTRY_SIZE
];
1261 memset(&emptyData
, 0, RAW_DIRENTRY_SIZE
);
1263 hr
= StorageImpl_WriteRawDirEntry(storage
, index
, emptyData
);
1269 /****************************************************************************
1273 * Case insensitive comparison of DirEntry.name by first considering
1276 * Returns <0 when name1 < name2
1277 * >0 when name1 > name2
1278 * 0 when name1 == name2
1280 static LONG
entryNameCmp(
1281 const OLECHAR
*name1
,
1282 const OLECHAR
*name2
)
1284 LONG diff
= lstrlenW(name1
) - lstrlenW(name2
);
1289 * We compare the string themselves only when they are of the same length
1291 diff
= lstrcmpiW( name1
, name2
);
1297 /****************************************************************************
1301 * Add a directory entry to a storage
1303 static HRESULT
insertIntoTree(
1305 DirRef parentStorageIndex
,
1306 DirRef newEntryIndex
)
1308 DirEntry currentEntry
;
1312 * Read the inserted entry
1314 StorageImpl_ReadDirEntry(This
,
1319 * Read the storage entry
1321 StorageImpl_ReadDirEntry(This
,
1325 if (currentEntry
.dirRootEntry
!= DIRENTRY_NULL
)
1328 * The root storage contains some element, therefore, start the research
1329 * for the appropriate location.
1332 DirRef current
, next
, previous
, currentEntryId
;
1335 * Keep a reference to the root of the storage's element tree
1337 currentEntryId
= currentEntry
.dirRootEntry
;
1342 StorageImpl_ReadDirEntry(This
,
1343 currentEntry
.dirRootEntry
,
1346 previous
= currentEntry
.leftChild
;
1347 next
= currentEntry
.rightChild
;
1348 current
= currentEntryId
;
1352 LONG diff
= entryNameCmp( newEntry
.name
, currentEntry
.name
);
1356 if (previous
!= DIRENTRY_NULL
)
1358 StorageImpl_ReadDirEntry(This
,
1365 currentEntry
.leftChild
= newEntryIndex
;
1366 StorageImpl_WriteDirEntry(This
,
1374 if (next
!= DIRENTRY_NULL
)
1376 StorageImpl_ReadDirEntry(This
,
1383 currentEntry
.rightChild
= newEntryIndex
;
1384 StorageImpl_WriteDirEntry(This
,
1393 * Trying to insert an item with the same name in the
1394 * subtree structure.
1396 return STG_E_FILEALREADYEXISTS
;
1399 previous
= currentEntry
.leftChild
;
1400 next
= currentEntry
.rightChild
;
1406 * The storage is empty, make the new entry the root of its element tree
1408 currentEntry
.dirRootEntry
= newEntryIndex
;
1409 StorageImpl_WriteDirEntry(This
,
1417 /****************************************************************************
1421 * Find and read the element of a storage with the given name.
1423 static DirRef
findElement(StorageImpl
*storage
, DirRef storageEntry
,
1424 const OLECHAR
*name
, DirEntry
*data
)
1426 DirRef currentEntry
;
1428 /* Read the storage entry to find the root of the tree. */
1429 StorageImpl_ReadDirEntry(storage
, storageEntry
, data
);
1431 currentEntry
= data
->dirRootEntry
;
1433 while (currentEntry
!= DIRENTRY_NULL
)
1437 StorageImpl_ReadDirEntry(storage
, currentEntry
, data
);
1439 cmp
= entryNameCmp(name
, data
->name
);
1446 currentEntry
= data
->leftChild
;
1449 currentEntry
= data
->rightChild
;
1452 return currentEntry
;
1455 /****************************************************************************
1459 * Find and read the binary tree parent of the element with the given name.
1461 * If there is no such element, find a place where it could be inserted and
1462 * return STG_E_FILENOTFOUND.
1464 static HRESULT
findTreeParent(StorageImpl
*storage
, DirRef storageEntry
,
1465 const OLECHAR
*childName
, DirEntry
*parentData
, DirRef
*parentEntry
,
1471 /* Read the storage entry to find the root of the tree. */
1472 StorageImpl_ReadDirEntry(storage
, storageEntry
, parentData
);
1474 *parentEntry
= storageEntry
;
1475 *relation
= DIRENTRY_RELATION_DIR
;
1477 childEntry
= parentData
->dirRootEntry
;
1479 while (childEntry
!= DIRENTRY_NULL
)
1483 StorageImpl_ReadDirEntry(storage
, childEntry
, &childData
);
1485 cmp
= entryNameCmp(childName
, childData
.name
);
1493 *parentData
= childData
;
1494 *parentEntry
= childEntry
;
1495 *relation
= DIRENTRY_RELATION_PREVIOUS
;
1497 childEntry
= parentData
->leftChild
;
1502 *parentData
= childData
;
1503 *parentEntry
= childEntry
;
1504 *relation
= DIRENTRY_RELATION_NEXT
;
1506 childEntry
= parentData
->rightChild
;
1510 if (childEntry
== DIRENTRY_NULL
)
1511 return STG_E_FILENOTFOUND
;
1517 /*************************************************************************
1520 static HRESULT WINAPI
StorageBaseImpl_CopyTo(
1522 DWORD ciidExclude
, /* [in] */
1523 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1524 SNB snbExclude
, /* [unique][in] */
1525 IStorage
* pstgDest
) /* [unique][in] */
1527 IEnumSTATSTG
*elements
= 0;
1528 STATSTG curElement
, strStat
;
1530 IStorage
*pstgTmp
, *pstgChild
;
1531 IStream
*pstrTmp
, *pstrChild
;
1532 BOOL skip
= FALSE
, skip_storage
= FALSE
, skip_stream
= FALSE
;
1535 TRACE("(%p, %d, %p, %p, %p)\n",
1536 iface
, ciidExclude
, rgiidExclude
,
1537 snbExclude
, pstgDest
);
1539 if ( pstgDest
== 0 )
1540 return STG_E_INVALIDPOINTER
;
1543 * Enumerate the elements
1545 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1553 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1554 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1556 for(i
= 0; i
< ciidExclude
; ++i
)
1558 if(IsEqualGUID(&IID_IStorage
, &rgiidExclude
[i
]))
1559 skip_storage
= TRUE
;
1560 else if(IsEqualGUID(&IID_IStream
, &rgiidExclude
[i
]))
1563 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude
[i
]));
1569 * Obtain the next element
1571 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1573 if ( hr
== S_FALSE
)
1575 hr
= S_OK
; /* done, every element has been copied */
1581 WCHAR
**snb
= snbExclude
;
1583 while ( *snb
!= NULL
&& !skip
)
1585 if ( lstrcmpW(curElement
.pwcsName
, *snb
) == 0 )
1594 if (curElement
.type
== STGTY_STORAGE
)
1600 * open child source storage
1602 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1603 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1604 NULL
, 0, &pstgChild
);
1610 * Check if destination storage is not a child of the source
1611 * storage, which will cause an infinite loop
1613 if (pstgChild
== pstgDest
)
1615 IEnumSTATSTG_Release(elements
);
1617 return STG_E_ACCESSDENIED
;
1621 * create a new storage in destination storage
1623 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1624 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1628 * if it already exist, don't create a new one use this one
1630 if (hr
== STG_E_FILEALREADYEXISTS
)
1632 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1633 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1634 NULL
, 0, &pstgTmp
);
1642 * do the copy recursively
1644 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1647 IStorage_Release( pstgTmp
);
1648 IStorage_Release( pstgChild
);
1650 else if (curElement
.type
== STGTY_STREAM
)
1656 * create a new stream in destination storage. If the stream already
1657 * exist, it will be deleted and a new one will be created.
1659 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1660 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1667 * open child stream storage
1669 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1670 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1677 * Get the size of the source stream
1679 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1682 * Set the size of the destination stream.
1684 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1689 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1692 IStream_Release( pstrTmp
);
1693 IStream_Release( pstrChild
);
1697 WARN("unknown element type: %d\n", curElement
.type
);
1700 } while (hr
== S_OK
);
1705 IEnumSTATSTG_Release(elements
);
1710 /*************************************************************************
1711 * MoveElementTo (IStorage)
1713 static HRESULT WINAPI
StorageBaseImpl_MoveElementTo(
1715 const OLECHAR
*pwcsName
, /* [string][in] */
1716 IStorage
*pstgDest
, /* [unique][in] */
1717 const OLECHAR
*pwcsNewName
,/* [string][in] */
1718 DWORD grfFlags
) /* [in] */
1720 FIXME("(%p %s %p %s %u): stub\n", iface
,
1721 debugstr_w(pwcsName
), pstgDest
,
1722 debugstr_w(pwcsNewName
), grfFlags
);
1726 /*************************************************************************
1729 * Ensures that any changes made to a storage object open in transacted mode
1730 * are reflected in the parent storage
1733 * Wine doesn't implement transacted mode, which seems to be a basic
1734 * optimization, so we can ignore this stub for now.
1736 static HRESULT WINAPI
StorageImpl_Commit(
1738 DWORD grfCommitFlags
)/* [in] */
1740 FIXME("(%p %d): stub\n", iface
, grfCommitFlags
);
1744 /*************************************************************************
1747 * Discard all changes that have been made since the last commit operation
1749 static HRESULT WINAPI
StorageImpl_Revert(
1752 FIXME("(%p): stub\n", iface
);
1756 /*************************************************************************
1757 * DestroyElement (IStorage)
1759 * Strategy: This implementation is built this way for simplicity not for speed.
1760 * I always delete the topmost element of the enumeration and adjust
1761 * the deleted element pointer all the time. This takes longer to
1762 * do but allow to reinvoke DestroyElement whenever we encounter a
1763 * storage object. The optimisation resides in the usage of another
1764 * enumeration strategy that would give all the leaves of a storage
1765 * first. (postfix order)
1767 static HRESULT WINAPI
StorageBaseImpl_DestroyElement(
1769 const OLECHAR
*pwcsName
)/* [string][in] */
1771 StorageBaseImpl
* const This
=(StorageBaseImpl
*)iface
;
1774 DirEntry entryToDelete
;
1775 DirRef entryToDeleteRef
;
1778 iface
, debugstr_w(pwcsName
));
1781 return STG_E_INVALIDPOINTER
;
1783 if (!This
->ancestorStorage
)
1784 return STG_E_REVERTED
;
1786 if ( STGM_ACCESS_MODE( This
->openFlags
) == STGM_READ
)
1787 return STG_E_ACCESSDENIED
;
1789 entryToDeleteRef
= findElement(
1790 This
->ancestorStorage
,
1791 This
->storageDirEntry
,
1795 if ( entryToDeleteRef
== DIRENTRY_NULL
)
1797 return STG_E_FILENOTFOUND
;
1800 if ( entryToDelete
.stgType
== STGTY_STORAGE
)
1802 hr
= deleteStorageContents(
1807 else if ( entryToDelete
.stgType
== STGTY_STREAM
)
1809 hr
= deleteStreamContents(
1819 * Remove the entry from its parent storage
1821 hr
= removeFromTree(
1822 This
->ancestorStorage
,
1823 This
->storageDirEntry
,
1827 * Invalidate the entry
1830 destroyDirEntry(This
->ancestorStorage
,
1837 /******************************************************************************
1838 * Internal stream list handlers
1841 void StorageBaseImpl_AddStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1843 TRACE("Stream added (stg=%p strm=%p)\n", stg
, strm
);
1844 list_add_tail(&stg
->strmHead
,&strm
->StrmListEntry
);
1847 void StorageBaseImpl_RemoveStream(StorageBaseImpl
* stg
, StgStreamImpl
* strm
)
1849 TRACE("Stream removed (stg=%p strm=%p)\n", stg
,strm
);
1850 list_remove(&(strm
->StrmListEntry
));
1853 static BOOL
StorageBaseImpl_IsStreamOpen(StorageBaseImpl
* stg
, DirRef streamEntry
)
1855 StgStreamImpl
*strm
;
1857 LIST_FOR_EACH_ENTRY(strm
, &stg
->strmHead
, StgStreamImpl
, StrmListEntry
)
1859 if (strm
->dirEntry
== streamEntry
)
1868 static BOOL
StorageBaseImpl_IsStorageOpen(StorageBaseImpl
* stg
, DirRef storageEntry
)
1870 StorageInternalImpl
*childstg
;
1872 LIST_FOR_EACH_ENTRY(childstg
, &stg
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1874 if (childstg
->base
.storageDirEntry
== storageEntry
)
1883 static void StorageBaseImpl_DeleteAll(StorageBaseImpl
* stg
)
1885 struct list
*cur
, *cur2
;
1886 StgStreamImpl
*strm
=NULL
;
1887 StorageInternalImpl
*childstg
=NULL
;
1889 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->strmHead
) {
1890 strm
= LIST_ENTRY(cur
,StgStreamImpl
,StrmListEntry
);
1891 TRACE("Streams invalidated (stg=%p strm=%p next=%p prev=%p)\n", stg
,strm
,cur
->next
,cur
->prev
);
1892 strm
->parentStorage
= NULL
;
1896 LIST_FOR_EACH_SAFE(cur
, cur2
, &stg
->storageHead
) {
1897 childstg
= LIST_ENTRY(cur
,StorageInternalImpl
,ParentListEntry
);
1898 StorageInternalImpl_Invalidate( childstg
);
1903 /*********************************************************************
1907 * Delete the contents of a storage entry.
1910 static HRESULT
deleteStorageContents(
1911 StorageBaseImpl
*parentStorage
,
1912 DirRef indexToDelete
,
1913 DirEntry entryDataToDelete
)
1915 IEnumSTATSTG
*elements
= 0;
1916 IStorage
*childStorage
= 0;
1917 STATSTG currentElement
;
1919 HRESULT destroyHr
= S_OK
;
1920 StorageInternalImpl
*stg
, *stg2
;
1922 /* Invalidate any open storage objects. */
1923 LIST_FOR_EACH_ENTRY_SAFE(stg
, stg2
, &parentStorage
->storageHead
, StorageInternalImpl
, ParentListEntry
)
1925 if (stg
->base
.storageDirEntry
== indexToDelete
)
1927 StorageInternalImpl_Invalidate(stg
);
1932 * Open the storage and enumerate it
1934 hr
= StorageBaseImpl_OpenStorage(
1935 (IStorage
*)parentStorage
,
1936 entryDataToDelete
.name
,
1938 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
1949 * Enumerate the elements
1951 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1956 * Obtain the next element
1958 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1961 destroyHr
= IStorage_DestroyElement(childStorage
, currentElement
.pwcsName
);
1963 CoTaskMemFree(currentElement
.pwcsName
);
1967 * We need to Reset the enumeration every time because we delete elements
1968 * and the enumeration could be invalid
1970 IEnumSTATSTG_Reset(elements
);
1972 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1974 IStorage_Release(childStorage
);
1975 IEnumSTATSTG_Release(elements
);
1980 /*********************************************************************
1984 * Perform the deletion of a stream's data
1987 static HRESULT
deleteStreamContents(
1988 StorageBaseImpl
*parentStorage
,
1989 DirRef indexToDelete
,
1990 DirEntry entryDataToDelete
)
1994 ULARGE_INTEGER size
;
1995 StgStreamImpl
*strm
, *strm2
;
1997 /* Invalidate any open stream objects. */
1998 LIST_FOR_EACH_ENTRY_SAFE(strm
, strm2
, &parentStorage
->strmHead
, StgStreamImpl
, StrmListEntry
)
2000 if (strm
->dirEntry
== indexToDelete
)
2002 TRACE("Stream deleted %p\n", strm
);
2003 strm
->parentStorage
= NULL
;
2004 list_remove(&strm
->StrmListEntry
);
2008 size
.u
.HighPart
= 0;
2011 hr
= StorageBaseImpl_OpenStream((IStorage
*)parentStorage
,
2012 entryDataToDelete
.name
, NULL
, STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pis
);
2022 hr
= IStream_SetSize(pis
, size
);
2030 * Release the stream object.
2032 IStream_Release(pis
);
2037 static void setEntryLink(DirEntry
*entry
, ULONG relation
, DirRef new_target
)
2041 case DIRENTRY_RELATION_PREVIOUS
:
2042 entry
->leftChild
= new_target
;
2044 case DIRENTRY_RELATION_NEXT
:
2045 entry
->rightChild
= new_target
;
2047 case DIRENTRY_RELATION_DIR
:
2048 entry
->dirRootEntry
= new_target
;
2055 /*************************************************************************
2059 * This method removes a directory entry from its parent storage tree without
2060 * freeing any resources attached to it.
2062 static HRESULT
removeFromTree(
2064 DirRef parentStorageIndex
,
2065 DirRef deletedIndex
)
2069 DirEntry entryToDelete
;
2070 DirEntry parentEntry
;
2071 DirRef parentEntryRef
;
2072 ULONG typeOfRelation
;
2074 res
= StorageImpl_ReadDirEntry(This
, deletedIndex
, &entryToDelete
);
2077 * Find the element that links to the one we want to delete.
2079 hr
= findTreeParent(This
, parentStorageIndex
, entryToDelete
.name
,
2080 &parentEntry
, &parentEntryRef
, &typeOfRelation
);
2085 if (entryToDelete
.leftChild
!= DIRENTRY_NULL
)
2088 * Replace the deleted entry with its left child
2090 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.leftChild
);
2092 hr
= StorageImpl_WriteDirEntry(
2101 if (entryToDelete
.rightChild
!= DIRENTRY_NULL
)
2104 * We need to reinsert the right child somewhere. We already know it and
2105 * its children are greater than everything in the left tree, so we
2106 * insert it at the rightmost point in the left tree.
2108 DirRef newRightChildParent
= entryToDelete
.leftChild
;
2109 DirEntry newRightChildParentEntry
;
2113 res
= StorageImpl_ReadDirEntry(
2115 newRightChildParent
,
2116 &newRightChildParentEntry
);
2122 if (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
)
2123 newRightChildParent
= newRightChildParentEntry
.rightChild
;
2124 } while (newRightChildParentEntry
.rightChild
!= DIRENTRY_NULL
);
2126 newRightChildParentEntry
.rightChild
= entryToDelete
.rightChild
;
2128 hr
= StorageImpl_WriteDirEntry(
2130 newRightChildParent
,
2131 &newRightChildParentEntry
);
2141 * Replace the deleted entry with its right child
2143 setEntryLink(&parentEntry
, typeOfRelation
, entryToDelete
.rightChild
);
2145 hr
= StorageImpl_WriteDirEntry(
2159 /******************************************************************************
2160 * SetElementTimes (IStorage)
2162 static HRESULT WINAPI
StorageBaseImpl_SetElementTimes(
2164 const OLECHAR
*pwcsName
,/* [string][in] */
2165 const FILETIME
*pctime
, /* [in] */
2166 const FILETIME
*patime
, /* [in] */
2167 const FILETIME
*pmtime
) /* [in] */
2169 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2173 /******************************************************************************
2174 * SetStateBits (IStorage)
2176 static HRESULT WINAPI
StorageBaseImpl_SetStateBits(
2178 DWORD grfStateBits
,/* [in] */
2179 DWORD grfMask
) /* [in] */
2181 StorageBaseImpl
* const This
= (StorageBaseImpl
*)iface
;
2183 if (!This
->ancestorStorage
)
2184 return STG_E_REVERTED
;
2186 This
->stateBits
= (This
->stateBits
& ~grfMask
) | (grfStateBits
& grfMask
);
2191 * Virtual function table for the IStorage32Impl class.
2193 static const IStorageVtbl Storage32Impl_Vtbl
=
2195 StorageBaseImpl_QueryInterface
,
2196 StorageBaseImpl_AddRef
,
2197 StorageBaseImpl_Release
,
2198 StorageBaseImpl_CreateStream
,
2199 StorageBaseImpl_OpenStream
,
2200 StorageBaseImpl_CreateStorage
,
2201 StorageBaseImpl_OpenStorage
,
2202 StorageBaseImpl_CopyTo
,
2203 StorageBaseImpl_MoveElementTo
,
2206 StorageBaseImpl_EnumElements
,
2207 StorageBaseImpl_DestroyElement
,
2208 StorageBaseImpl_RenameElement
,
2209 StorageBaseImpl_SetElementTimes
,
2210 StorageBaseImpl_SetClass
,
2211 StorageBaseImpl_SetStateBits
,
2212 StorageBaseImpl_Stat
2215 static const StorageBaseImplVtbl StorageImpl_BaseVtbl
=
2217 StorageImpl_Destroy
,
2218 StorageImpl_CreateDirEntry
2221 static HRESULT
StorageImpl_Construct(
2228 StorageImpl
** result
)
2232 DirEntry currentEntry
;
2233 BOOL readSuccessful
;
2234 DirRef currentEntryRef
;
2236 if ( FAILED( validateSTGM(openFlags
) ))
2237 return STG_E_INVALIDFLAG
;
2239 This
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
2241 return E_OUTOFMEMORY
;
2243 memset(This
, 0, sizeof(StorageImpl
));
2245 list_init(&This
->base
.strmHead
);
2247 list_init(&This
->base
.storageHead
);
2249 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2250 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2251 This
->base
.baseVtbl
= &StorageImpl_BaseVtbl
;
2252 This
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
2254 This
->base
.create
= create
;
2257 * This is the top-level storage so initialize the ancestor pointer
2260 This
->base
.ancestorStorage
= This
;
2262 This
->hFile
= hFile
;
2265 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2266 (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
));
2267 if (!This
->pwcsName
)
2269 hr
= STG_E_INSUFFICIENTMEMORY
;
2272 strcpyW(This
->pwcsName
, pwcsName
);
2274 memcpy(This
->base
.filename
, pwcsName
, DIRENTRY_NAME_BUFFER_LEN
-1);
2275 This
->base
.filename
[DIRENTRY_NAME_BUFFER_LEN
-1] = 0;
2279 * Initialize the big block cache.
2281 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2282 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2283 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2289 if (This
->bigBlockFile
== 0)
2297 ULARGE_INTEGER size
;
2298 BYTE bigBlockBuffer
[BIG_BLOCK_SIZE
];
2301 * Initialize all header variables:
2302 * - The big block depot consists of one block and it is at block 0
2303 * - The directory table starts at block 1
2304 * - There is no small block depot
2306 memset( This
->bigBlockDepotStart
,
2308 sizeof(This
->bigBlockDepotStart
));
2310 This
->bigBlockDepotCount
= 1;
2311 This
->bigBlockDepotStart
[0] = 0;
2312 This
->rootStartBlock
= 1;
2313 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2314 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2315 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2316 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2317 This
->extBigBlockDepotCount
= 0;
2319 StorageImpl_SaveFileHeader(This
);
2322 * Add one block for the big block depot and one block for the directory table
2324 size
.u
.HighPart
= 0;
2325 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2326 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2329 * Initialize the big block depot
2331 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2332 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2333 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2334 StorageImpl_WriteBigBlock(This
, 0, bigBlockBuffer
);
2339 * Load the header for the file.
2341 hr
= StorageImpl_LoadFileHeader(This
);
2350 * There is no block depot cached yet.
2352 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2355 * Start searching for free blocks with block 0.
2357 This
->prevFreeBlock
= 0;
2360 * Create the block chain abstractions.
2362 if(!(This
->rootBlockChain
=
2363 BlockChainStream_Construct(This
, &This
->rootStartBlock
, DIRENTRY_NULL
)))
2365 hr
= STG_E_READFAULT
;
2369 if(!(This
->smallBlockDepotChain
=
2370 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2373 hr
= STG_E_READFAULT
;
2378 * Write the root storage entry (memory only)
2384 * Initialize the directory table
2386 memset(&rootEntry
, 0, sizeof(rootEntry
));
2387 MultiByteToWideChar( CP_ACP
, 0, rootEntryName
, -1, rootEntry
.name
,
2388 sizeof(rootEntry
.name
)/sizeof(WCHAR
) );
2389 rootEntry
.sizeOfNameString
= (strlenW(rootEntry
.name
)+1) * sizeof(WCHAR
);
2390 rootEntry
.stgType
= STGTY_ROOT
;
2391 rootEntry
.leftChild
= DIRENTRY_NULL
;
2392 rootEntry
.rightChild
= DIRENTRY_NULL
;
2393 rootEntry
.dirRootEntry
= DIRENTRY_NULL
;
2394 rootEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
2395 rootEntry
.size
.u
.HighPart
= 0;
2396 rootEntry
.size
.u
.LowPart
= 0;
2398 StorageImpl_WriteDirEntry(This
, 0, &rootEntry
);
2402 * Find the ID of the root storage.
2404 currentEntryRef
= 0;
2408 readSuccessful
= StorageImpl_ReadDirEntry(
2415 if ( (currentEntry
.sizeOfNameString
!= 0 ) &&
2416 (currentEntry
.stgType
== STGTY_ROOT
) )
2418 This
->base
.storageDirEntry
= currentEntryRef
;
2424 } while (readSuccessful
&& (This
->base
.storageDirEntry
== DIRENTRY_NULL
) );
2426 if (!readSuccessful
)
2428 hr
= STG_E_READFAULT
;
2433 * Create the block chain abstraction for the small block root chain.
2435 if(!(This
->smallBlockRootChain
=
2436 BlockChainStream_Construct(This
, NULL
, This
->base
.storageDirEntry
)))
2438 hr
= STG_E_READFAULT
;
2444 IStorage_Release((IStorage
*)This
);
2453 static void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2455 StorageImpl
*This
= (StorageImpl
*) iface
;
2456 TRACE("(%p)\n", This
);
2458 StorageBaseImpl_DeleteAll(&This
->base
);
2460 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2462 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2463 BlockChainStream_Destroy(This
->rootBlockChain
);
2464 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2466 if (This
->bigBlockFile
)
2467 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2468 HeapFree(GetProcessHeap(), 0, This
);
2471 /******************************************************************************
2472 * Storage32Impl_GetNextFreeBigBlock
2474 * Returns the index of the next free big block.
2475 * If the big block depot is filled, this method will enlarge it.
2478 static ULONG
StorageImpl_GetNextFreeBigBlock(
2481 ULONG depotBlockIndexPos
;
2482 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2484 ULONG depotBlockOffset
;
2485 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2486 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2488 ULONG freeBlock
= BLOCK_UNUSED
;
2490 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2491 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2494 * Scan the entire big block depot until we find a block marked free
2496 while (nextBlockIndex
!= BLOCK_UNUSED
)
2498 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2500 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2503 * Grow the primary depot.
2505 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2507 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2510 * Add a block depot.
2512 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2513 This
->bigBlockDepotCount
++;
2514 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2517 * Flag it as a block depot.
2519 StorageImpl_SetNextBlockInChain(This
,
2523 /* Save new header information.
2525 StorageImpl_SaveFileHeader(This
);
2530 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2532 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2535 * Grow the extended depot.
2537 ULONG extIndex
= BLOCK_UNUSED
;
2538 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2539 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2541 if (extBlockOffset
== 0)
2543 /* We need an extended block.
2545 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2546 This
->extBigBlockDepotCount
++;
2547 depotBlockIndexPos
= extIndex
+ 1;
2550 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2553 * Add a block depot and mark it in the extended block.
2555 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2556 This
->bigBlockDepotCount
++;
2557 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2559 /* Flag the block depot.
2561 StorageImpl_SetNextBlockInChain(This
,
2565 /* If necessary, flag the extended depot block.
2567 if (extIndex
!= BLOCK_UNUSED
)
2568 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2570 /* Save header information.
2572 StorageImpl_SaveFileHeader(This
);
2576 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2580 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2581 ( nextBlockIndex
!= BLOCK_UNUSED
))
2583 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2585 if (nextBlockIndex
== BLOCK_UNUSED
)
2587 freeBlock
= (depotIndex
* blocksPerDepot
) +
2588 (depotBlockOffset
/sizeof(ULONG
));
2591 depotBlockOffset
+= sizeof(ULONG
);
2596 depotBlockOffset
= 0;
2600 * make sure that the block physically exists before using it
2602 BIGBLOCKFILE_EnsureExists(This
->bigBlockFile
, freeBlock
);
2604 This
->prevFreeBlock
= freeBlock
;
2609 /******************************************************************************
2610 * Storage32Impl_AddBlockDepot
2612 * This will create a depot block, essentially it is a block initialized
2615 static void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2617 BYTE blockBuffer
[BIG_BLOCK_SIZE
];
2620 * Initialize blocks as free
2622 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2623 StorageImpl_WriteBigBlock(This
, blockIndex
, blockBuffer
);
2626 /******************************************************************************
2627 * Storage32Impl_GetExtDepotBlock
2629 * Returns the index of the block that corresponds to the specified depot
2630 * index. This method is only for depot indexes equal or greater than
2631 * COUNT_BBDEPOTINHEADER.
2633 static ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2635 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2636 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2637 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2638 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2639 ULONG blockIndex
= BLOCK_UNUSED
;
2640 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2642 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2644 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2645 return BLOCK_UNUSED
;
2647 while (extBlockCount
> 0)
2649 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2653 if (extBlockIndex
!= BLOCK_UNUSED
)
2654 StorageImpl_ReadDWordFromBigBlock(This
, extBlockIndex
,
2655 extBlockOffset
* sizeof(ULONG
), &blockIndex
);
2660 /******************************************************************************
2661 * Storage32Impl_SetExtDepotBlock
2663 * Associates the specified block index to the specified depot index.
2664 * This method is only for depot indexes equal or greater than
2665 * COUNT_BBDEPOTINHEADER.
2667 static void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
, ULONG blockIndex
)
2669 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2670 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2671 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2672 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2673 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2675 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2677 while (extBlockCount
> 0)
2679 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2683 if (extBlockIndex
!= BLOCK_UNUSED
)
2685 StorageImpl_WriteDWordToBigBlock(This
, extBlockIndex
,
2686 extBlockOffset
* sizeof(ULONG
),
2691 /******************************************************************************
2692 * Storage32Impl_AddExtBlockDepot
2694 * Creates an extended depot block.
2696 static ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2698 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2699 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2700 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2701 ULONG index
= BLOCK_UNUSED
;
2702 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2703 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2704 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2706 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2707 blocksPerDepotBlock
;
2709 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2712 * The first extended block.
2714 This
->extBigBlockDepotStart
= index
;
2720 * Follow the chain to the last one.
2722 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2724 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2728 * Add the new extended block to the chain.
2730 StorageImpl_WriteDWordToBigBlock(This
, nextExtBlock
, nextBlockOffset
,
2735 * Initialize this block.
2737 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2738 StorageImpl_WriteBigBlock(This
, index
, depotBuffer
);
2743 /******************************************************************************
2744 * Storage32Impl_FreeBigBlock
2746 * This method will flag the specified block as free in the big block depot.
2748 static void StorageImpl_FreeBigBlock(
2752 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2754 if (blockIndex
< This
->prevFreeBlock
)
2755 This
->prevFreeBlock
= blockIndex
;
2758 /************************************************************************
2759 * Storage32Impl_GetNextBlockInChain
2761 * This method will retrieve the block index of the next big block in
2764 * Params: This - Pointer to the Storage object.
2765 * blockIndex - Index of the block to retrieve the chain
2767 * nextBlockIndex - receives the return value.
2769 * Returns: This method returns the index of the next block in the chain.
2770 * It will return the constants:
2771 * BLOCK_SPECIAL - If the block given was not part of a
2773 * BLOCK_END_OF_CHAIN - If the block given was the last in
2775 * BLOCK_UNUSED - If the block given was not past of a chain
2777 * BLOCK_EXTBBDEPOT - This block is part of the extended
2780 * See Windows documentation for more details on IStorage methods.
2782 static HRESULT
StorageImpl_GetNextBlockInChain(
2785 ULONG
* nextBlockIndex
)
2787 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2788 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2789 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2790 BYTE depotBuffer
[BIG_BLOCK_SIZE
];
2792 ULONG depotBlockIndexPos
;
2795 *nextBlockIndex
= BLOCK_SPECIAL
;
2797 if(depotBlockCount
>= This
->bigBlockDepotCount
)
2799 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount
,
2800 This
->bigBlockDepotCount
);
2801 return STG_E_READFAULT
;
2805 * Cache the currently accessed depot block.
2807 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2809 This
->indexBlockDepotCached
= depotBlockCount
;
2811 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2813 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2818 * We have to look in the extended depot.
2820 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2823 success
= StorageImpl_ReadBigBlock(This
, depotBlockIndexPos
, depotBuffer
);
2826 return STG_E_READFAULT
;
2828 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2830 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
2831 This
->blockDepotCached
[index
] = *nextBlockIndex
;
2835 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2840 /******************************************************************************
2841 * Storage32Impl_GetNextExtendedBlock
2843 * Given an extended block this method will return the next extended block.
2846 * The last ULONG of an extended block is the block index of the next
2847 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2851 * - The index of the next extended block
2852 * - BLOCK_UNUSED: there is no next extended block.
2853 * - Any other return values denotes failure.
2855 static ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2857 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2858 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2860 StorageImpl_ReadDWordFromBigBlock(This
, blockIndex
, depotBlockOffset
,
2863 return nextBlockIndex
;
2866 /******************************************************************************
2867 * Storage32Impl_SetNextBlockInChain
2869 * This method will write the index of the specified block's next block
2870 * in the big block depot.
2872 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2875 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2876 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2877 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2880 static void StorageImpl_SetNextBlockInChain(
2885 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2886 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2887 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2888 ULONG depotBlockIndexPos
;
2890 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2891 assert(blockIndex
!= nextBlock
);
2893 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2895 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2900 * We have to look in the extended depot.
2902 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2905 StorageImpl_WriteDWordToBigBlock(This
, depotBlockIndexPos
, depotBlockOffset
,
2908 * Update the cached block depot, if necessary.
2910 if (depotBlockCount
== This
->indexBlockDepotCached
)
2912 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2916 /******************************************************************************
2917 * Storage32Impl_LoadFileHeader
2919 * This method will read in the file header, i.e. big block index -1.
2921 static HRESULT
StorageImpl_LoadFileHeader(
2924 HRESULT hr
= STG_E_FILENOTFOUND
;
2925 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
2931 * Get a pointer to the big block of data containing the header.
2933 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
2936 * Extract the information from the header.
2941 * Check for the "magic number" signature and return an error if it is not
2944 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2946 return STG_E_OLDFORMAT
;
2949 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2951 return STG_E_INVALIDHEADER
;
2954 StorageUtl_ReadWord(
2956 OFFSET_BIGBLOCKSIZEBITS
,
2957 &This
->bigBlockSizeBits
);
2959 StorageUtl_ReadWord(
2961 OFFSET_SMALLBLOCKSIZEBITS
,
2962 &This
->smallBlockSizeBits
);
2964 StorageUtl_ReadDWord(
2966 OFFSET_BBDEPOTCOUNT
,
2967 &This
->bigBlockDepotCount
);
2969 StorageUtl_ReadDWord(
2971 OFFSET_ROOTSTARTBLOCK
,
2972 &This
->rootStartBlock
);
2974 StorageUtl_ReadDWord(
2976 OFFSET_SBDEPOTSTART
,
2977 &This
->smallBlockDepotStart
);
2979 StorageUtl_ReadDWord(
2981 OFFSET_EXTBBDEPOTSTART
,
2982 &This
->extBigBlockDepotStart
);
2984 StorageUtl_ReadDWord(
2986 OFFSET_EXTBBDEPOTCOUNT
,
2987 &This
->extBigBlockDepotCount
);
2989 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2991 StorageUtl_ReadDWord(
2993 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2994 &(This
->bigBlockDepotStart
[index
]));
2998 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3000 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
3001 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
3004 * Right now, the code is making some assumptions about the size of the
3005 * blocks, just make sure they are what we're expecting.
3007 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
3008 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
3010 WARN("Broken OLE storage file\n");
3011 hr
= STG_E_INVALIDHEADER
;
3020 /******************************************************************************
3021 * Storage32Impl_SaveFileHeader
3023 * This method will save to the file the header, i.e. big block -1.
3025 static void StorageImpl_SaveFileHeader(
3028 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
3033 * Get a pointer to the big block of data containing the header.
3035 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3038 * If the block read failed, the file is probably new.
3043 * Initialize for all unknown fields.
3045 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3048 * Initialize the magic number.
3050 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3053 * And a bunch of things we don't know what they mean
3055 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3056 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3057 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3058 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3062 * Write the information to the header.
3064 StorageUtl_WriteWord(
3066 OFFSET_BIGBLOCKSIZEBITS
,
3067 This
->bigBlockSizeBits
);
3069 StorageUtl_WriteWord(
3071 OFFSET_SMALLBLOCKSIZEBITS
,
3072 This
->smallBlockSizeBits
);
3074 StorageUtl_WriteDWord(
3076 OFFSET_BBDEPOTCOUNT
,
3077 This
->bigBlockDepotCount
);
3079 StorageUtl_WriteDWord(
3081 OFFSET_ROOTSTARTBLOCK
,
3082 This
->rootStartBlock
);
3084 StorageUtl_WriteDWord(
3086 OFFSET_SBDEPOTSTART
,
3087 This
->smallBlockDepotStart
);
3089 StorageUtl_WriteDWord(
3091 OFFSET_SBDEPOTCOUNT
,
3092 This
->smallBlockDepotChain
?
3093 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3095 StorageUtl_WriteDWord(
3097 OFFSET_EXTBBDEPOTSTART
,
3098 This
->extBigBlockDepotStart
);
3100 StorageUtl_WriteDWord(
3102 OFFSET_EXTBBDEPOTCOUNT
,
3103 This
->extBigBlockDepotCount
);
3105 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3107 StorageUtl_WriteDWord(
3109 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3110 (This
->bigBlockDepotStart
[index
]));
3114 * Write the big block back to the file.
3116 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3119 /******************************************************************************
3120 * StorageImpl_ReadRawDirEntry
3122 * This method will read the raw data from a directory entry in the file.
3124 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3126 HRESULT
StorageImpl_ReadRawDirEntry(StorageImpl
*This
, ULONG index
, BYTE
*buffer
)
3128 ULARGE_INTEGER offset
;
3132 offset
.u
.HighPart
= 0;
3133 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3135 hr
= BlockChainStream_ReadAt(
3136 This
->rootBlockChain
,
3145 /******************************************************************************
3146 * StorageImpl_WriteRawDirEntry
3148 * This method will write the raw data from a directory entry in the file.
3150 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3152 HRESULT
StorageImpl_WriteRawDirEntry(StorageImpl
*This
, ULONG index
, const BYTE
*buffer
)
3154 ULARGE_INTEGER offset
;
3158 offset
.u
.HighPart
= 0;
3159 offset
.u
.LowPart
= index
* RAW_DIRENTRY_SIZE
;
3161 hr
= BlockChainStream_WriteAt(
3162 This
->rootBlockChain
,
3171 /******************************************************************************
3174 * Update raw directory entry data from the fields in newData.
3176 * buffer must be RAW_DIRENTRY_SIZE bytes long.
3178 void UpdateRawDirEntry(BYTE
*buffer
, const DirEntry
*newData
)
3180 memset(buffer
, 0, RAW_DIRENTRY_SIZE
);
3183 buffer
+ OFFSET_PS_NAME
,
3185 DIRENTRY_NAME_BUFFER_LEN
);
3187 memcpy(buffer
+ OFFSET_PS_STGTYPE
, &newData
->stgType
, 1);
3189 StorageUtl_WriteWord(
3191 OFFSET_PS_NAMELENGTH
,
3192 newData
->sizeOfNameString
);
3194 StorageUtl_WriteDWord(
3196 OFFSET_PS_LEFTCHILD
,
3197 newData
->leftChild
);
3199 StorageUtl_WriteDWord(
3201 OFFSET_PS_RIGHTCHILD
,
3202 newData
->rightChild
);
3204 StorageUtl_WriteDWord(
3207 newData
->dirRootEntry
);
3209 StorageUtl_WriteGUID(
3214 StorageUtl_WriteDWord(
3217 newData
->ctime
.dwLowDateTime
);
3219 StorageUtl_WriteDWord(
3221 OFFSET_PS_CTIMEHIGH
,
3222 newData
->ctime
.dwHighDateTime
);
3224 StorageUtl_WriteDWord(
3227 newData
->mtime
.dwLowDateTime
);
3229 StorageUtl_WriteDWord(
3231 OFFSET_PS_MTIMEHIGH
,
3232 newData
->ctime
.dwHighDateTime
);
3234 StorageUtl_WriteDWord(
3236 OFFSET_PS_STARTBLOCK
,
3237 newData
->startingBlock
);
3239 StorageUtl_WriteDWord(
3242 newData
->size
.u
.LowPart
);
3245 /******************************************************************************
3246 * Storage32Impl_ReadDirEntry
3248 * This method will read the specified directory entry.
3250 BOOL
StorageImpl_ReadDirEntry(
3255 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3258 readRes
= StorageImpl_ReadRawDirEntry(This
, index
, currentEntry
);
3260 if (SUCCEEDED(readRes
))
3262 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3265 (WCHAR
*)currentEntry
+OFFSET_PS_NAME
,
3266 DIRENTRY_NAME_BUFFER_LEN
);
3267 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3269 memcpy(&buffer
->stgType
, currentEntry
+ OFFSET_PS_STGTYPE
, 1);
3271 StorageUtl_ReadWord(
3273 OFFSET_PS_NAMELENGTH
,
3274 &buffer
->sizeOfNameString
);
3276 StorageUtl_ReadDWord(
3278 OFFSET_PS_LEFTCHILD
,
3279 &buffer
->leftChild
);
3281 StorageUtl_ReadDWord(
3283 OFFSET_PS_RIGHTCHILD
,
3284 &buffer
->rightChild
);
3286 StorageUtl_ReadDWord(
3289 &buffer
->dirRootEntry
);
3291 StorageUtl_ReadGUID(
3296 StorageUtl_ReadDWord(
3299 &buffer
->ctime
.dwLowDateTime
);
3301 StorageUtl_ReadDWord(
3303 OFFSET_PS_CTIMEHIGH
,
3304 &buffer
->ctime
.dwHighDateTime
);
3306 StorageUtl_ReadDWord(
3309 &buffer
->mtime
.dwLowDateTime
);
3311 StorageUtl_ReadDWord(
3313 OFFSET_PS_MTIMEHIGH
,
3314 &buffer
->mtime
.dwHighDateTime
);
3316 StorageUtl_ReadDWord(
3318 OFFSET_PS_STARTBLOCK
,
3319 &buffer
->startingBlock
);
3321 StorageUtl_ReadDWord(
3324 &buffer
->size
.u
.LowPart
);
3326 buffer
->size
.u
.HighPart
= 0;
3329 return SUCCEEDED(readRes
) ? TRUE
: FALSE
;
3332 /*********************************************************************
3333 * Write the specified directory entry to the file
3335 HRESULT
StorageImpl_WriteDirEntry(
3338 const DirEntry
* buffer
)
3340 BYTE currentEntry
[RAW_DIRENTRY_SIZE
];
3343 UpdateRawDirEntry(currentEntry
, buffer
);
3345 writeRes
= StorageImpl_WriteRawDirEntry(This
, index
, currentEntry
);
3349 static BOOL
StorageImpl_ReadBigBlock(
3354 ULARGE_INTEGER ulOffset
;
3357 ulOffset
.u
.HighPart
= 0;
3358 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3360 StorageImpl_ReadAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &read
);
3361 return (read
== This
->bigBlockSize
);
3364 static BOOL
StorageImpl_ReadDWordFromBigBlock(
3370 ULARGE_INTEGER ulOffset
;
3374 ulOffset
.u
.HighPart
= 0;
3375 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3376 ulOffset
.u
.LowPart
+= offset
;
3378 StorageImpl_ReadAt(This
, ulOffset
, &tmp
, sizeof(DWORD
), &read
);
3379 *value
= lendian32toh(tmp
);
3380 return (read
== sizeof(DWORD
));
3383 static BOOL
StorageImpl_WriteBigBlock(
3388 ULARGE_INTEGER ulOffset
;
3391 ulOffset
.u
.HighPart
= 0;
3392 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3394 StorageImpl_WriteAt(This
, ulOffset
, buffer
, This
->bigBlockSize
, &wrote
);
3395 return (wrote
== This
->bigBlockSize
);
3398 static BOOL
StorageImpl_WriteDWordToBigBlock(
3404 ULARGE_INTEGER ulOffset
;
3407 ulOffset
.u
.HighPart
= 0;
3408 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
);
3409 ulOffset
.u
.LowPart
+= offset
;
3411 value
= htole32(value
);
3412 StorageImpl_WriteAt(This
, ulOffset
, &value
, sizeof(DWORD
), &wrote
);
3413 return (wrote
== sizeof(DWORD
));
3416 /******************************************************************************
3417 * Storage32Impl_SmallBlocksToBigBlocks
3419 * This method will convert a small block chain to a big block chain.
3420 * The small block chain will be destroyed.
3422 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3424 SmallBlockChainStream
** ppsbChain
)
3426 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3427 ULARGE_INTEGER size
, offset
;
3428 ULONG cbRead
, cbWritten
;
3429 ULARGE_INTEGER cbTotalRead
;
3430 DirRef streamEntryRef
;
3431 HRESULT resWrite
= S_OK
;
3433 DirEntry streamEntry
;
3435 BlockChainStream
*bbTempChain
= NULL
;
3436 BlockChainStream
*bigBlockChain
= NULL
;
3439 * Create a temporary big block chain that doesn't have
3440 * an associated directory entry. This temporary chain will be
3441 * used to copy data from small blocks to big blocks.
3443 bbTempChain
= BlockChainStream_Construct(This
,
3446 if(!bbTempChain
) return NULL
;
3448 * Grow the big block chain.
3450 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3451 BlockChainStream_SetSize(bbTempChain
, size
);
3454 * Copy the contents of the small block chain to the big block chain
3455 * by small block size increments.
3457 offset
.u
.LowPart
= 0;
3458 offset
.u
.HighPart
= 0;
3459 cbTotalRead
.QuadPart
= 0;
3461 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3464 resRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3466 min(This
->smallBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3469 if (FAILED(resRead
))
3474 cbTotalRead
.QuadPart
+= cbRead
;
3476 resWrite
= BlockChainStream_WriteAt(bbTempChain
,
3482 if (FAILED(resWrite
))
3485 offset
.u
.LowPart
+= cbRead
;
3487 } while (cbTotalRead
.QuadPart
< size
.QuadPart
);
3488 HeapFree(GetProcessHeap(),0,buffer
);
3490 size
.u
.HighPart
= 0;
3493 if (FAILED(resRead
) || FAILED(resWrite
))
3495 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3496 BlockChainStream_SetSize(bbTempChain
, size
);
3497 BlockChainStream_Destroy(bbTempChain
);
3502 * Destroy the small block chain.
3504 streamEntryRef
= (*ppsbChain
)->ownerDirEntry
;
3505 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3506 SmallBlockChainStream_Destroy(*ppsbChain
);
3510 * Change the directory entry. This chain is now a big block chain
3511 * and it doesn't reside in the small blocks chain anymore.
3513 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3515 streamEntry
.startingBlock
= bbHeadOfChain
;
3517 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3520 * Destroy the temporary entryless big block chain.
3521 * Create a new big block chain associated with this entry.
3523 BlockChainStream_Destroy(bbTempChain
);
3524 bigBlockChain
= BlockChainStream_Construct(This
,
3528 return bigBlockChain
;
3531 /******************************************************************************
3532 * Storage32Impl_BigBlocksToSmallBlocks
3534 * This method will convert a big block chain to a small block chain.
3535 * The big block chain will be destroyed on success.
3537 SmallBlockChainStream
* Storage32Impl_BigBlocksToSmallBlocks(
3539 BlockChainStream
** ppbbChain
)
3541 ULARGE_INTEGER size
, offset
, cbTotalRead
;
3542 ULONG cbRead
, cbWritten
, sbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3543 DirRef streamEntryRef
;
3544 HRESULT resWrite
= S_OK
, resRead
;
3545 DirEntry streamEntry
;
3547 SmallBlockChainStream
* sbTempChain
;
3549 TRACE("%p %p\n", This
, ppbbChain
);
3551 sbTempChain
= SmallBlockChainStream_Construct(This
, &sbHeadOfChain
,
3557 size
= BlockChainStream_GetSize(*ppbbChain
);
3558 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3560 offset
.u
.HighPart
= 0;
3561 offset
.u
.LowPart
= 0;
3562 cbTotalRead
.QuadPart
= 0;
3563 buffer
= HeapAlloc(GetProcessHeap(), 0, This
->bigBlockSize
);
3566 resRead
= BlockChainStream_ReadAt(*ppbbChain
, offset
,
3567 min(This
->bigBlockSize
, size
.u
.LowPart
- offset
.u
.LowPart
),
3575 cbTotalRead
.QuadPart
+= cbRead
;
3577 resWrite
= SmallBlockChainStream_WriteAt(sbTempChain
, offset
,
3578 cbRead
, buffer
, &cbWritten
);
3580 if(FAILED(resWrite
))
3583 offset
.u
.LowPart
+= cbRead
;
3585 }while(cbTotalRead
.QuadPart
< size
.QuadPart
);
3586 HeapFree(GetProcessHeap(), 0, buffer
);
3588 size
.u
.HighPart
= 0;
3591 if(FAILED(resRead
) || FAILED(resWrite
))
3593 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead
, resWrite
);
3594 SmallBlockChainStream_SetSize(sbTempChain
, size
);
3595 SmallBlockChainStream_Destroy(sbTempChain
);
3599 /* destroy the original big block chain */
3600 streamEntryRef
= (*ppbbChain
)->ownerDirEntry
;
3601 BlockChainStream_SetSize(*ppbbChain
, size
);
3602 BlockChainStream_Destroy(*ppbbChain
);
3605 StorageImpl_ReadDirEntry(This
, streamEntryRef
, &streamEntry
);
3606 streamEntry
.startingBlock
= sbHeadOfChain
;
3607 StorageImpl_WriteDirEntry(This
, streamEntryRef
, &streamEntry
);
3609 SmallBlockChainStream_Destroy(sbTempChain
);
3610 return SmallBlockChainStream_Construct(This
, NULL
, streamEntryRef
);
3613 static void StorageInternalImpl_Invalidate( StorageInternalImpl
*This
)
3615 if (This
->base
.ancestorStorage
)
3617 TRACE("Storage invalidated (stg=%p)\n", This
);
3619 This
->base
.ancestorStorage
= NULL
;
3621 StorageBaseImpl_DeleteAll(&This
->base
);
3623 list_remove(&This
->ParentListEntry
);
3627 static void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
3629 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
3631 StorageInternalImpl_Invalidate(This
);
3633 HeapFree(GetProcessHeap(), 0, This
);
3636 static HRESULT
StorageInternalImpl_CreateDirEntry(StorageBaseImpl
*base
,
3637 const DirEntry
*newData
, DirRef
*index
)
3639 return StorageBaseImpl_CreateDirEntry(&base
->ancestorStorage
->base
,
3643 /******************************************************************************
3645 ** Storage32InternalImpl_Commit
3648 static HRESULT WINAPI
StorageInternalImpl_Commit(
3650 DWORD grfCommitFlags
) /* [in] */
3652 FIXME("(%p,%x): stub\n", iface
, grfCommitFlags
);
3656 /******************************************************************************
3658 ** Storage32InternalImpl_Revert
3661 static HRESULT WINAPI
StorageInternalImpl_Revert(
3664 FIXME("(%p): stub\n", iface
);
3668 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3670 IStorage_Release((IStorage
*)This
->parentStorage
);
3671 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3672 HeapFree(GetProcessHeap(), 0, This
);
3675 static HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3676 IEnumSTATSTG
* iface
,
3680 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3683 return E_INVALIDARG
;
3687 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
3688 IsEqualGUID(&IID_IEnumSTATSTG
, riid
))
3691 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
3695 return E_NOINTERFACE
;
3698 static ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3699 IEnumSTATSTG
* iface
)
3701 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3702 return InterlockedIncrement(&This
->ref
);
3705 static ULONG WINAPI
IEnumSTATSTGImpl_Release(
3706 IEnumSTATSTG
* iface
)
3708 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3712 newRef
= InterlockedDecrement(&This
->ref
);
3716 IEnumSTATSTGImpl_Destroy(This
);
3722 static HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3723 IEnumSTATSTG
* iface
,
3726 ULONG
* pceltFetched
)
3728 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3730 DirEntry currentEntry
;
3731 STATSTG
* currentReturnStruct
= rgelt
;
3732 ULONG objectFetched
= 0;
3733 DirRef currentSearchNode
;
3735 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3736 return E_INVALIDARG
;
3739 * To avoid the special case, get another pointer to a ULONG value if
3740 * the caller didn't supply one.
3742 if (pceltFetched
==0)
3743 pceltFetched
= &objectFetched
;
3746 * Start the iteration, we will iterate until we hit the end of the
3747 * linked list or until we hit the number of items to iterate through
3752 * Start with the node at the top of the stack.
3754 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3756 while ( ( *pceltFetched
< celt
) &&
3757 ( currentSearchNode
!=DIRENTRY_NULL
) )
3760 * Remove the top node from the stack
3762 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3765 * Read the entry from the storage.
3767 StorageImpl_ReadDirEntry(This
->parentStorage
,
3772 * Copy the information to the return buffer.
3774 StorageUtl_CopyDirEntryToSTATSTG(&This
->parentStorage
->base
,
3775 currentReturnStruct
,
3780 * Step to the next item in the iteration
3783 currentReturnStruct
++;
3786 * Push the next search node in the search stack.
3788 IEnumSTATSTGImpl_PushSearchNode(This
, currentEntry
.rightChild
);
3791 * continue the iteration.
3793 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3796 if (*pceltFetched
== celt
)
3803 static HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3804 IEnumSTATSTG
* iface
,
3807 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3809 DirEntry currentEntry
;
3810 ULONG objectFetched
= 0;
3811 DirRef currentSearchNode
;
3814 * Start with the node at the top of the stack.
3816 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3818 while ( (objectFetched
< celt
) &&
3819 (currentSearchNode
!=DIRENTRY_NULL
) )
3822 * Remove the top node from the stack
3824 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3827 * Read the entry from the storage.
3829 StorageImpl_ReadDirEntry(This
->parentStorage
,
3834 * Step to the next item in the iteration
3839 * Push the next search node in the search stack.
3841 IEnumSTATSTGImpl_PushSearchNode(This
, currentEntry
.rightChild
);
3844 * continue the iteration.
3846 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3849 if (objectFetched
== celt
)
3855 static HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3856 IEnumSTATSTG
* iface
)
3858 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3860 DirEntry storageEntry
;
3861 BOOL readSuccessful
;
3864 * Re-initialize the search stack to an empty stack
3866 This
->stackSize
= 0;
3869 * Read the storage entry from the top-level storage.
3871 readSuccessful
= StorageImpl_ReadDirEntry(
3872 This
->parentStorage
,
3873 This
->storageDirEntry
,
3878 assert(storageEntry
.sizeOfNameString
!=0);
3881 * Push the search node in the search stack.
3883 IEnumSTATSTGImpl_PushSearchNode(This
, storageEntry
.dirRootEntry
);
3889 static HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3890 IEnumSTATSTG
* iface
,
3891 IEnumSTATSTG
** ppenum
)
3893 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3895 IEnumSTATSTGImpl
* newClone
;
3898 * Perform a sanity check on the parameters.
3901 return E_INVALIDARG
;
3903 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3904 This
->storageDirEntry
);
3908 * The new clone enumeration must point to the same current node as
3911 newClone
->stackSize
= This
->stackSize
;
3912 newClone
->stackMaxSize
= This
->stackMaxSize
;
3913 newClone
->stackToVisit
=
3914 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3917 newClone
->stackToVisit
,
3919 sizeof(DirRef
) * newClone
->stackSize
);
3921 *ppenum
= (IEnumSTATSTG
*)newClone
;
3924 * Don't forget to nail down a reference to the clone before
3927 IEnumSTATSTGImpl_AddRef(*ppenum
);
3932 static void IEnumSTATSTGImpl_PushSearchNode(
3933 IEnumSTATSTGImpl
* This
,
3936 DirEntry storageEntry
;
3937 BOOL readSuccessful
;
3940 * First, make sure we're not trying to push an unexisting node.
3942 if (nodeToPush
==DIRENTRY_NULL
)
3946 * First push the node to the stack
3948 if (This
->stackSize
== This
->stackMaxSize
)
3950 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
3952 This
->stackToVisit
= HeapReAlloc(
3956 sizeof(DirRef
) * This
->stackMaxSize
);
3959 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
3963 * Read the storage entry from the top-level storage.
3965 readSuccessful
= StorageImpl_ReadDirEntry(
3966 This
->parentStorage
,
3972 assert(storageEntry
.sizeOfNameString
!=0);
3975 * Push the previous search node in the search stack.
3977 IEnumSTATSTGImpl_PushSearchNode(This
, storageEntry
.leftChild
);
3981 static DirRef
IEnumSTATSTGImpl_PopSearchNode(
3982 IEnumSTATSTGImpl
* This
,
3987 if (This
->stackSize
== 0)
3988 return DIRENTRY_NULL
;
3990 topNode
= This
->stackToVisit
[This
->stackSize
-1];
3999 * Virtual function table for the IEnumSTATSTGImpl class.
4001 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
4003 IEnumSTATSTGImpl_QueryInterface
,
4004 IEnumSTATSTGImpl_AddRef
,
4005 IEnumSTATSTGImpl_Release
,
4006 IEnumSTATSTGImpl_Next
,
4007 IEnumSTATSTGImpl_Skip
,
4008 IEnumSTATSTGImpl_Reset
,
4009 IEnumSTATSTGImpl_Clone
4012 /******************************************************************************
4013 ** IEnumSTATSTGImpl implementation
4016 static IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
4017 StorageImpl
* parentStorage
,
4018 DirRef storageDirEntry
)
4020 IEnumSTATSTGImpl
* newEnumeration
;
4022 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
4024 if (newEnumeration
!=0)
4027 * Set-up the virtual function table and reference count.
4029 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
4030 newEnumeration
->ref
= 0;
4033 * We want to nail-down the reference to the storage in case the
4034 * enumeration out-lives the storage in the client application.
4036 newEnumeration
->parentStorage
= parentStorage
;
4037 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
4039 newEnumeration
->storageDirEntry
= storageDirEntry
;
4042 * Initialize the search stack
4044 newEnumeration
->stackSize
= 0;
4045 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
4046 newEnumeration
->stackToVisit
=
4047 HeapAlloc(GetProcessHeap(), 0, sizeof(DirRef
)*ENUMSTATSGT_SIZE_INCREMENT
);
4050 * Make sure the current node of the iterator is the first one.
4052 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
4055 return newEnumeration
;
4059 * Virtual function table for the Storage32InternalImpl class.
4061 static const IStorageVtbl Storage32InternalImpl_Vtbl
=
4063 StorageBaseImpl_QueryInterface
,
4064 StorageBaseImpl_AddRef
,
4065 StorageBaseImpl_Release
,
4066 StorageBaseImpl_CreateStream
,
4067 StorageBaseImpl_OpenStream
,
4068 StorageBaseImpl_CreateStorage
,
4069 StorageBaseImpl_OpenStorage
,
4070 StorageBaseImpl_CopyTo
,
4071 StorageBaseImpl_MoveElementTo
,
4072 StorageInternalImpl_Commit
,
4073 StorageInternalImpl_Revert
,
4074 StorageBaseImpl_EnumElements
,
4075 StorageBaseImpl_DestroyElement
,
4076 StorageBaseImpl_RenameElement
,
4077 StorageBaseImpl_SetElementTimes
,
4078 StorageBaseImpl_SetClass
,
4079 StorageBaseImpl_SetStateBits
,
4080 StorageBaseImpl_Stat
4083 static const StorageBaseImplVtbl StorageInternalImpl_BaseVtbl
=
4085 StorageInternalImpl_Destroy
,
4086 StorageInternalImpl_CreateDirEntry
4089 /******************************************************************************
4090 ** Storage32InternalImpl implementation
4093 static StorageInternalImpl
* StorageInternalImpl_Construct(
4094 StorageImpl
* ancestorStorage
,
4096 DirRef storageDirEntry
)
4098 StorageInternalImpl
* newStorage
;
4100 newStorage
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(StorageInternalImpl
));
4104 list_init(&newStorage
->base
.strmHead
);
4106 list_init(&newStorage
->base
.storageHead
);
4109 * Initialize the virtual function table.
4111 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4112 newStorage
->base
.baseVtbl
= &StorageInternalImpl_BaseVtbl
;
4113 newStorage
->base
.openFlags
= (openFlags
& ~STGM_CREATE
);
4116 * Keep the ancestor storage pointer but do not nail a reference to it.
4118 newStorage
->base
.ancestorStorage
= ancestorStorage
;
4121 * Keep a reference to the directory entry of this storage
4123 newStorage
->base
.storageDirEntry
= storageDirEntry
;
4125 newStorage
->base
.create
= 0;
4133 /******************************************************************************
4134 ** StorageUtl implementation
4137 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4141 memcpy(&tmp
, buffer
+offset
, sizeof(WORD
));
4142 *value
= lendian16toh(tmp
);
4145 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4147 value
= htole16(value
);
4148 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4151 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4155 memcpy(&tmp
, buffer
+offset
, sizeof(DWORD
));
4156 *value
= lendian32toh(tmp
);
4159 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4161 value
= htole32(value
);
4162 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4165 void StorageUtl_ReadULargeInteger(const BYTE
* buffer
, ULONG offset
,
4166 ULARGE_INTEGER
* value
)
4168 #ifdef WORDS_BIGENDIAN
4171 memcpy(&tmp
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4172 value
->u
.LowPart
= htole32(tmp
.u
.HighPart
);
4173 value
->u
.HighPart
= htole32(tmp
.u
.LowPart
);
4175 memcpy(value
, buffer
+ offset
, sizeof(ULARGE_INTEGER
));
4179 void StorageUtl_WriteULargeInteger(BYTE
* buffer
, ULONG offset
,
4180 const ULARGE_INTEGER
*value
)
4182 #ifdef WORDS_BIGENDIAN
4185 tmp
.u
.LowPart
= htole32(value
->u
.HighPart
);
4186 tmp
.u
.HighPart
= htole32(value
->u
.LowPart
);
4187 memcpy(buffer
+ offset
, &tmp
, sizeof(ULARGE_INTEGER
));
4189 memcpy(buffer
+ offset
, value
, sizeof(ULARGE_INTEGER
));
4193 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4195 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4196 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4197 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4199 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4202 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4204 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4205 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4206 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4208 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4211 void StorageUtl_CopyDirEntryToSTATSTG(
4212 StorageBaseImpl
* storage
,
4213 STATSTG
* destination
,
4214 const DirEntry
* source
,
4219 if (source
->stgType
== STGTY_ROOT
)
4221 /* replace the name of root entry (often "Root Entry") by the file name */
4222 entryName
= storage
->filename
;
4226 entryName
= source
->name
;
4230 * The copy of the string occurs only when the flag is not set
4232 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4233 (entryName
== NULL
) ||
4234 (entryName
[0] == 0) )
4236 destination
->pwcsName
= 0;
4240 destination
->pwcsName
=
4241 CoTaskMemAlloc((lstrlenW(entryName
)+1)*sizeof(WCHAR
));
4243 strcpyW(destination
->pwcsName
, entryName
);
4246 switch (source
->stgType
)
4250 destination
->type
= STGTY_STORAGE
;
4253 destination
->type
= STGTY_STREAM
;
4256 destination
->type
= STGTY_STREAM
;
4260 destination
->cbSize
= source
->size
;
4262 currentReturnStruct->mtime = {0}; TODO
4263 currentReturnStruct->ctime = {0};
4264 currentReturnStruct->atime = {0};
4266 destination
->grfMode
= 0;
4267 destination
->grfLocksSupported
= 0;
4268 destination
->clsid
= source
->clsid
;
4269 destination
->grfStateBits
= 0;
4270 destination
->reserved
= 0;
4273 /******************************************************************************
4274 ** BlockChainStream implementation
4277 BlockChainStream
* BlockChainStream_Construct(
4278 StorageImpl
* parentStorage
,
4279 ULONG
* headOfStreamPlaceHolder
,
4282 BlockChainStream
* newStream
;
4285 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4287 newStream
->parentStorage
= parentStorage
;
4288 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4289 newStream
->ownerDirEntry
= dirEntry
;
4290 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4291 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4292 newStream
->numBlocks
= 0;
4294 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4296 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4298 newStream
->numBlocks
++;
4299 newStream
->tailIndex
= blockIndex
;
4301 if(FAILED(StorageImpl_GetNextBlockInChain(
4306 HeapFree(GetProcessHeap(), 0, newStream
);
4314 void BlockChainStream_Destroy(BlockChainStream
* This
)
4316 HeapFree(GetProcessHeap(), 0, This
);
4319 /******************************************************************************
4320 * BlockChainStream_GetHeadOfChain
4322 * Returns the head of this stream chain.
4323 * Some special chains don't have directory entries, their heads are kept in
4324 * This->headOfStreamPlaceHolder.
4327 static ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
4329 DirEntry chainEntry
;
4330 BOOL readSuccessful
;
4332 if (This
->headOfStreamPlaceHolder
!= 0)
4333 return *(This
->headOfStreamPlaceHolder
);
4335 if (This
->ownerDirEntry
!= DIRENTRY_NULL
)
4337 readSuccessful
= StorageImpl_ReadDirEntry(
4338 This
->parentStorage
,
4339 This
->ownerDirEntry
,
4344 return chainEntry
.startingBlock
;
4348 return BLOCK_END_OF_CHAIN
;
4351 /******************************************************************************
4352 * BlockChainStream_GetCount
4354 * Returns the number of blocks that comprises this chain.
4355 * This is not the size of the stream as the last block may not be full!
4358 static ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
4363 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4365 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4369 if(FAILED(StorageImpl_GetNextBlockInChain(
4370 This
->parentStorage
,
4379 /******************************************************************************
4380 * BlockChainStream_ReadAt
4382 * Reads a specified number of bytes from this chain at the specified offset.
4383 * bytesRead may be NULL.
4384 * Failure will be returned if the specified number of bytes has not been read.
4386 HRESULT
BlockChainStream_ReadAt(BlockChainStream
* This
,
4387 ULARGE_INTEGER offset
,
4392 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4393 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4394 ULONG bytesToReadInBuffer
;
4398 TRACE("(%p)-> %i %p %i %p\n",This
, offset
.u
.LowPart
, buffer
, size
, bytesRead
);
4401 * Find the first block in the stream that contains part of the buffer.
4403 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4404 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4405 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4407 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4408 This
->lastBlockNoInSequence
= blockNoInSequence
;
4412 ULONG temp
= blockNoInSequence
;
4414 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4415 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4416 This
->lastBlockNoInSequence
= temp
;
4419 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4421 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4422 return STG_E_DOCFILECORRUPT
;
4423 blockNoInSequence
--;
4426 if ((blockNoInSequence
> 0) && (blockIndex
== BLOCK_END_OF_CHAIN
))
4427 return STG_E_DOCFILECORRUPT
; /* We failed to find the starting block */
4429 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4432 * Start reading the buffer.
4435 bufferWalker
= buffer
;
4437 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4439 ULARGE_INTEGER ulOffset
;
4442 * Calculate how many bytes we can copy from this big block.
4444 bytesToReadInBuffer
=
4445 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4447 TRACE("block %i\n",blockIndex
);
4448 ulOffset
.u
.HighPart
= 0;
4449 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4452 StorageImpl_ReadAt(This
->parentStorage
,
4455 bytesToReadInBuffer
,
4458 * Step to the next big block.
4460 if( size
> bytesReadAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4461 return STG_E_DOCFILECORRUPT
;
4463 bufferWalker
+= bytesReadAt
;
4464 size
-= bytesReadAt
;
4465 *bytesRead
+= bytesReadAt
;
4466 offsetInBlock
= 0; /* There is no offset on the next block */
4468 if (bytesToReadInBuffer
!= bytesReadAt
)
4472 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
4475 /******************************************************************************
4476 * BlockChainStream_WriteAt
4478 * Writes the specified number of bytes to this chain at the specified offset.
4479 * Will fail if not all specified number of bytes have been written.
4481 HRESULT
BlockChainStream_WriteAt(BlockChainStream
* This
,
4482 ULARGE_INTEGER offset
,
4485 ULONG
* bytesWritten
)
4487 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4488 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4491 const BYTE
* bufferWalker
;
4494 * Find the first block in the stream that contains part of the buffer.
4496 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4497 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4498 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4500 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4501 This
->lastBlockNoInSequence
= blockNoInSequence
;
4505 ULONG temp
= blockNoInSequence
;
4507 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4508 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4509 This
->lastBlockNoInSequence
= temp
;
4512 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4514 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4516 return STG_E_DOCFILECORRUPT
;
4517 blockNoInSequence
--;
4520 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4522 /* BlockChainStream_SetSize should have already been called to ensure we have
4523 * enough blocks in the chain to write into */
4524 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4526 ERR("not enough blocks in chain to write data\n");
4527 return STG_E_DOCFILECORRUPT
;
4531 bufferWalker
= buffer
;
4533 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4535 ULARGE_INTEGER ulOffset
;
4536 DWORD bytesWrittenAt
;
4538 * Calculate how many bytes we can copy from this big block.
4541 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4543 TRACE("block %i\n",blockIndex
);
4544 ulOffset
.u
.HighPart
= 0;
4545 ulOffset
.u
.LowPart
= BLOCK_GetBigBlockOffset(blockIndex
) +
4548 StorageImpl_WriteAt(This
->parentStorage
,
4555 * Step to the next big block.
4557 if(size
> bytesWrittenAt
&& FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4559 return STG_E_DOCFILECORRUPT
;
4561 bufferWalker
+= bytesWrittenAt
;
4562 size
-= bytesWrittenAt
;
4563 *bytesWritten
+= bytesWrittenAt
;
4564 offsetInBlock
= 0; /* There is no offset on the next block */
4566 if (bytesWrittenAt
!= bytesToWrite
)
4570 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
4573 /******************************************************************************
4574 * BlockChainStream_Shrink
4576 * Shrinks this chain in the big block depot.
4578 static BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4579 ULARGE_INTEGER newSize
)
4581 ULONG blockIndex
, extraBlock
;
4586 * Reset the last accessed block cache.
4588 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
4589 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
4592 * Figure out how many blocks are needed to contain the new size
4594 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4596 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4599 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4602 * Go to the new end of chain
4604 while (count
< numBlocks
)
4606 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4612 /* Get the next block before marking the new end */
4613 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4617 /* Mark the new end of chain */
4618 StorageImpl_SetNextBlockInChain(
4619 This
->parentStorage
,
4621 BLOCK_END_OF_CHAIN
);
4623 This
->tailIndex
= blockIndex
;
4624 This
->numBlocks
= numBlocks
;
4627 * Mark the extra blocks as free
4629 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4631 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
4634 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4635 extraBlock
= blockIndex
;
4641 /******************************************************************************
4642 * BlockChainStream_Enlarge
4644 * Grows this chain in the big block depot.
4646 static BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4647 ULARGE_INTEGER newSize
)
4649 ULONG blockIndex
, currentBlock
;
4651 ULONG oldNumBlocks
= 0;
4653 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4656 * Empty chain. Create the head.
4658 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4660 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4661 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4663 BLOCK_END_OF_CHAIN
);
4665 if (This
->headOfStreamPlaceHolder
!= 0)
4667 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4671 DirEntry chainEntry
;
4672 assert(This
->ownerDirEntry
!= DIRENTRY_NULL
);
4674 StorageImpl_ReadDirEntry(
4675 This
->parentStorage
,
4676 This
->ownerDirEntry
,
4679 chainEntry
.startingBlock
= blockIndex
;
4681 StorageImpl_WriteDirEntry(
4682 This
->parentStorage
,
4683 This
->ownerDirEntry
,
4687 This
->tailIndex
= blockIndex
;
4688 This
->numBlocks
= 1;
4692 * Figure out how many blocks are needed to contain this stream
4694 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4696 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4700 * Go to the current end of chain
4702 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4704 currentBlock
= blockIndex
;
4706 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4709 currentBlock
= blockIndex
;
4711 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
4716 This
->tailIndex
= currentBlock
;
4719 currentBlock
= This
->tailIndex
;
4720 oldNumBlocks
= This
->numBlocks
;
4723 * Add new blocks to the chain
4725 if (oldNumBlocks
< newNumBlocks
)
4727 while (oldNumBlocks
< newNumBlocks
)
4729 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4731 StorageImpl_SetNextBlockInChain(
4732 This
->parentStorage
,
4736 StorageImpl_SetNextBlockInChain(
4737 This
->parentStorage
,
4739 BLOCK_END_OF_CHAIN
);
4741 currentBlock
= blockIndex
;
4745 This
->tailIndex
= blockIndex
;
4746 This
->numBlocks
= newNumBlocks
;
4752 /******************************************************************************
4753 * BlockChainStream_SetSize
4755 * Sets the size of this stream. The big block depot will be updated.
4756 * The file will grow if we grow the chain.
4758 * TODO: Free the actual blocks in the file when we shrink the chain.
4759 * Currently, the blocks are still in the file. So the file size
4760 * doesn't shrink even if we shrink streams.
4762 BOOL
BlockChainStream_SetSize(
4763 BlockChainStream
* This
,
4764 ULARGE_INTEGER newSize
)
4766 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4768 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
4771 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
4773 BlockChainStream_Shrink(This
, newSize
);
4777 BlockChainStream_Enlarge(This
, newSize
);
4783 /******************************************************************************
4784 * BlockChainStream_GetSize
4786 * Returns the size of this chain.
4787 * Will return the block count if this chain doesn't have a directory entry.
4789 static ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4791 DirEntry chainEntry
;
4793 if(This
->headOfStreamPlaceHolder
== NULL
)
4796 * This chain has a directory entry so use the size value from there.
4798 StorageImpl_ReadDirEntry(
4799 This
->parentStorage
,
4800 This
->ownerDirEntry
,
4803 return chainEntry
.size
;
4808 * this chain is a chain that does not have a directory entry, figure out the
4809 * size by making the product number of used blocks times the
4812 ULARGE_INTEGER result
;
4813 result
.u
.HighPart
= 0;
4816 BlockChainStream_GetCount(This
) *
4817 This
->parentStorage
->bigBlockSize
;
4823 /******************************************************************************
4824 ** SmallBlockChainStream implementation
4827 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4828 StorageImpl
* parentStorage
,
4829 ULONG
* headOfStreamPlaceHolder
,
4832 SmallBlockChainStream
* newStream
;
4834 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4836 newStream
->parentStorage
= parentStorage
;
4837 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4838 newStream
->ownerDirEntry
= dirEntry
;
4843 void SmallBlockChainStream_Destroy(
4844 SmallBlockChainStream
* This
)
4846 HeapFree(GetProcessHeap(), 0, This
);
4849 /******************************************************************************
4850 * SmallBlockChainStream_GetHeadOfChain
4852 * Returns the head of this chain of small blocks.
4854 static ULONG
SmallBlockChainStream_GetHeadOfChain(
4855 SmallBlockChainStream
* This
)
4857 DirEntry chainEntry
;
4858 BOOL readSuccessful
;
4860 if (This
->headOfStreamPlaceHolder
!= NULL
)
4861 return *(This
->headOfStreamPlaceHolder
);
4863 if (This
->ownerDirEntry
)
4865 readSuccessful
= StorageImpl_ReadDirEntry(
4866 This
->parentStorage
,
4867 This
->ownerDirEntry
,
4872 return chainEntry
.startingBlock
;
4877 return BLOCK_END_OF_CHAIN
;
4880 /******************************************************************************
4881 * SmallBlockChainStream_GetNextBlockInChain
4883 * Returns the index of the next small block in this chain.
4886 * - BLOCK_END_OF_CHAIN: end of this chain
4887 * - BLOCK_UNUSED: small block 'blockIndex' is free
4889 static HRESULT
SmallBlockChainStream_GetNextBlockInChain(
4890 SmallBlockChainStream
* This
,
4892 ULONG
* nextBlockInChain
)
4894 ULARGE_INTEGER offsetOfBlockInDepot
;
4899 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4901 offsetOfBlockInDepot
.u
.HighPart
= 0;
4902 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4905 * Read those bytes in the buffer from the small block file.
4907 res
= BlockChainStream_ReadAt(
4908 This
->parentStorage
->smallBlockDepotChain
,
4909 offsetOfBlockInDepot
,
4916 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
4923 /******************************************************************************
4924 * SmallBlockChainStream_SetNextBlockInChain
4926 * Writes the index of the next block of the specified block in the small
4928 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4929 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4931 static void SmallBlockChainStream_SetNextBlockInChain(
4932 SmallBlockChainStream
* This
,
4936 ULARGE_INTEGER offsetOfBlockInDepot
;
4940 offsetOfBlockInDepot
.u
.HighPart
= 0;
4941 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4943 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
4946 * Read those bytes in the buffer from the small block file.
4948 BlockChainStream_WriteAt(
4949 This
->parentStorage
->smallBlockDepotChain
,
4950 offsetOfBlockInDepot
,
4956 /******************************************************************************
4957 * SmallBlockChainStream_FreeBlock
4959 * Flag small block 'blockIndex' as free in the small block depot.
4961 static void SmallBlockChainStream_FreeBlock(
4962 SmallBlockChainStream
* This
,
4965 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4968 /******************************************************************************
4969 * SmallBlockChainStream_GetNextFreeBlock
4971 * Returns the index of a free small block. The small block depot will be
4972 * enlarged if necessary. The small block chain will also be enlarged if
4975 static ULONG
SmallBlockChainStream_GetNextFreeBlock(
4976 SmallBlockChainStream
* This
)
4978 ULARGE_INTEGER offsetOfBlockInDepot
;
4981 ULONG blockIndex
= 0;
4982 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4984 ULONG smallBlocksPerBigBlock
;
4986 offsetOfBlockInDepot
.u
.HighPart
= 0;
4989 * Scan the small block depot for a free block
4991 while (nextBlockIndex
!= BLOCK_UNUSED
)
4993 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4995 res
= BlockChainStream_ReadAt(
4996 This
->parentStorage
->smallBlockDepotChain
,
4997 offsetOfBlockInDepot
,
5003 * If we run out of space for the small block depot, enlarge it
5007 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
5009 if (nextBlockIndex
!= BLOCK_UNUSED
)
5015 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
5017 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
5018 ULONG nextBlock
, newsbdIndex
;
5019 BYTE smallBlockDepot
[BIG_BLOCK_SIZE
];
5021 nextBlock
= sbdIndex
;
5022 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
5024 sbdIndex
= nextBlock
;
5025 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
5028 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5029 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
5030 StorageImpl_SetNextBlockInChain(
5031 This
->parentStorage
,
5035 StorageImpl_SetNextBlockInChain(
5036 This
->parentStorage
,
5038 BLOCK_END_OF_CHAIN
);
5041 * Initialize all the small blocks to free
5043 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
5044 StorageImpl_WriteBigBlock(This
->parentStorage
, newsbdIndex
, smallBlockDepot
);
5049 * We have just created the small block depot.
5055 * Save it in the header
5057 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
5058 StorageImpl_SaveFileHeader(This
->parentStorage
);
5061 * And allocate the first big block that will contain small blocks
5064 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
5066 StorageImpl_SetNextBlockInChain(
5067 This
->parentStorage
,
5069 BLOCK_END_OF_CHAIN
);
5071 StorageImpl_ReadDirEntry(
5072 This
->parentStorage
,
5073 This
->parentStorage
->base
.storageDirEntry
,
5076 rootEntry
.startingBlock
= sbStartIndex
;
5077 rootEntry
.size
.u
.HighPart
= 0;
5078 rootEntry
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
5080 StorageImpl_WriteDirEntry(
5081 This
->parentStorage
,
5082 This
->parentStorage
->base
.storageDirEntry
,
5086 StorageImpl_SaveFileHeader(This
->parentStorage
);
5090 smallBlocksPerBigBlock
=
5091 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
5094 * Verify if we have to allocate big blocks to contain small blocks
5096 if (blockIndex
% smallBlocksPerBigBlock
== 0)
5099 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
5101 StorageImpl_ReadDirEntry(
5102 This
->parentStorage
,
5103 This
->parentStorage
->base
.storageDirEntry
,
5106 if (rootEntry
.size
.u
.LowPart
<
5107 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
5109 rootEntry
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
5111 BlockChainStream_SetSize(
5112 This
->parentStorage
->smallBlockRootChain
,
5115 StorageImpl_WriteDirEntry(
5116 This
->parentStorage
,
5117 This
->parentStorage
->base
.storageDirEntry
,
5125 /******************************************************************************
5126 * SmallBlockChainStream_ReadAt
5128 * Reads a specified number of bytes from this chain at the specified offset.
5129 * bytesRead may be NULL.
5130 * Failure will be returned if the specified number of bytes has not been read.
5132 HRESULT
SmallBlockChainStream_ReadAt(
5133 SmallBlockChainStream
* This
,
5134 ULARGE_INTEGER offset
,
5140 ULARGE_INTEGER offsetInBigBlockFile
;
5141 ULONG blockNoInSequence
=
5142 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5144 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5145 ULONG bytesToReadInBuffer
;
5147 ULONG bytesReadFromBigBlockFile
;
5151 * This should never happen on a small block file.
5153 assert(offset
.u
.HighPart
==0);
5156 * Find the first block in the stream that contains part of the buffer.
5158 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5160 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5162 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5165 blockNoInSequence
--;
5169 * Start reading the buffer.
5172 bufferWalker
= buffer
;
5174 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5177 * Calculate how many bytes we can copy from this small block.
5179 bytesToReadInBuffer
=
5180 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5183 * Calculate the offset of the small block in the small block file.
5185 offsetInBigBlockFile
.u
.HighPart
= 0;
5186 offsetInBigBlockFile
.u
.LowPart
=
5187 blockIndex
* This
->parentStorage
->smallBlockSize
;
5189 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5192 * Read those bytes in the buffer from the small block file.
5193 * The small block has already been identified so it shouldn't fail
5194 * unless the file is corrupt.
5196 rc
= BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5197 offsetInBigBlockFile
,
5198 bytesToReadInBuffer
,
5200 &bytesReadFromBigBlockFile
);
5206 * Step to the next big block.
5208 rc
= SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
);
5210 return STG_E_DOCFILECORRUPT
;
5212 bufferWalker
+= bytesReadFromBigBlockFile
;
5213 size
-= bytesReadFromBigBlockFile
;
5214 *bytesRead
+= bytesReadFromBigBlockFile
;
5215 offsetInBlock
= (offsetInBlock
+ bytesReadFromBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5218 return (size
== 0) ? S_OK
: STG_E_READFAULT
;
5221 /******************************************************************************
5222 * SmallBlockChainStream_WriteAt
5224 * Writes the specified number of bytes to this chain at the specified offset.
5225 * Will fail if not all specified number of bytes have been written.
5227 HRESULT
SmallBlockChainStream_WriteAt(
5228 SmallBlockChainStream
* This
,
5229 ULARGE_INTEGER offset
,
5232 ULONG
* bytesWritten
)
5234 ULARGE_INTEGER offsetInBigBlockFile
;
5235 ULONG blockNoInSequence
=
5236 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5238 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5239 ULONG bytesToWriteInBuffer
;
5241 ULONG bytesWrittenToBigBlockFile
;
5242 const BYTE
* bufferWalker
;
5246 * This should never happen on a small block file.
5248 assert(offset
.u
.HighPart
==0);
5251 * Find the first block in the stream that contains part of the buffer.
5253 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5255 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5257 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5258 return STG_E_DOCFILECORRUPT
;
5259 blockNoInSequence
--;
5263 * Start writing the buffer.
5266 bufferWalker
= buffer
;
5267 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5270 * Calculate how many bytes we can copy to this small block.
5272 bytesToWriteInBuffer
=
5273 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5276 * Calculate the offset of the small block in the small block file.
5278 offsetInBigBlockFile
.u
.HighPart
= 0;
5279 offsetInBigBlockFile
.u
.LowPart
=
5280 blockIndex
* This
->parentStorage
->smallBlockSize
;
5282 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5285 * Write those bytes in the buffer to the small block file.
5287 res
= BlockChainStream_WriteAt(
5288 This
->parentStorage
->smallBlockRootChain
,
5289 offsetInBigBlockFile
,
5290 bytesToWriteInBuffer
,
5292 &bytesWrittenToBigBlockFile
);
5297 * Step to the next big block.
5299 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5302 bufferWalker
+= bytesWrittenToBigBlockFile
;
5303 size
-= bytesWrittenToBigBlockFile
;
5304 *bytesWritten
+= bytesWrittenToBigBlockFile
;
5305 offsetInBlock
= (offsetInBlock
+ bytesWrittenToBigBlockFile
) % This
->parentStorage
->smallBlockSize
;
5308 return (size
== 0) ? S_OK
: STG_E_WRITEFAULT
;
5311 /******************************************************************************
5312 * SmallBlockChainStream_Shrink
5314 * Shrinks this chain in the small block depot.
5316 static BOOL
SmallBlockChainStream_Shrink(
5317 SmallBlockChainStream
* This
,
5318 ULARGE_INTEGER newSize
)
5320 ULONG blockIndex
, extraBlock
;
5324 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5326 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5329 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5332 * Go to the new end of chain
5334 while (count
< numBlocks
)
5336 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5343 * If the count is 0, we have a special case, the head of the chain was
5348 DirEntry chainEntry
;
5350 StorageImpl_ReadDirEntry(This
->parentStorage
,
5351 This
->ownerDirEntry
,
5354 chainEntry
.startingBlock
= BLOCK_END_OF_CHAIN
;
5356 StorageImpl_WriteDirEntry(This
->parentStorage
,
5357 This
->ownerDirEntry
,
5361 * We start freeing the chain at the head block.
5363 extraBlock
= blockIndex
;
5367 /* Get the next block before marking the new end */
5368 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5372 /* Mark the new end of chain */
5373 SmallBlockChainStream_SetNextBlockInChain(
5376 BLOCK_END_OF_CHAIN
);
5380 * Mark the extra blocks as free
5382 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5384 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
5387 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
5388 extraBlock
= blockIndex
;
5394 /******************************************************************************
5395 * SmallBlockChainStream_Enlarge
5397 * Grows this chain in the small block depot.
5399 static BOOL
SmallBlockChainStream_Enlarge(
5400 SmallBlockChainStream
* This
,
5401 ULARGE_INTEGER newSize
)
5403 ULONG blockIndex
, currentBlock
;
5405 ULONG oldNumBlocks
= 0;
5407 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5410 * Empty chain. Create the head.
5412 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5414 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5415 SmallBlockChainStream_SetNextBlockInChain(
5418 BLOCK_END_OF_CHAIN
);
5420 if (This
->headOfStreamPlaceHolder
!= NULL
)
5422 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
5426 DirEntry chainEntry
;
5428 StorageImpl_ReadDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
5431 chainEntry
.startingBlock
= blockIndex
;
5433 StorageImpl_WriteDirEntry(This
->parentStorage
, This
->ownerDirEntry
,
5438 currentBlock
= blockIndex
;
5441 * Figure out how many blocks are needed to contain this stream
5443 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5445 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5449 * Go to the current end of chain
5451 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5454 currentBlock
= blockIndex
;
5455 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
5460 * Add new blocks to the chain
5462 while (oldNumBlocks
< newNumBlocks
)
5464 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5465 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5467 SmallBlockChainStream_SetNextBlockInChain(
5470 BLOCK_END_OF_CHAIN
);
5472 currentBlock
= blockIndex
;
5479 /******************************************************************************
5480 * SmallBlockChainStream_SetSize
5482 * Sets the size of this stream.
5483 * The file will grow if we grow the chain.
5485 * TODO: Free the actual blocks in the file when we shrink the chain.
5486 * Currently, the blocks are still in the file. So the file size
5487 * doesn't shrink even if we shrink streams.
5489 BOOL
SmallBlockChainStream_SetSize(
5490 SmallBlockChainStream
* This
,
5491 ULARGE_INTEGER newSize
)
5493 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5495 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5498 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5500 SmallBlockChainStream_Shrink(This
, newSize
);
5504 SmallBlockChainStream_Enlarge(This
, newSize
);
5510 /******************************************************************************
5511 * SmallBlockChainStream_GetCount
5513 * Returns the number of small blocks that comprises this chain.
5514 * This is not the size of the stream as the last block may not be full!
5517 static ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
5522 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5524 while(blockIndex
!= BLOCK_END_OF_CHAIN
)
5528 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
,
5529 blockIndex
, &blockIndex
)))
5536 /******************************************************************************
5537 * SmallBlockChainStream_GetSize
5539 * Returns the size of this chain.
5541 static ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5543 DirEntry chainEntry
;
5545 if(This
->headOfStreamPlaceHolder
!= NULL
)
5547 ULARGE_INTEGER result
;
5548 result
.u
.HighPart
= 0;
5550 result
.u
.LowPart
= SmallBlockChainStream_GetCount(This
) *
5551 This
->parentStorage
->smallBlockSize
;
5556 StorageImpl_ReadDirEntry(
5557 This
->parentStorage
,
5558 This
->ownerDirEntry
,
5561 return chainEntry
.size
;
5564 /******************************************************************************
5565 * StgCreateDocfile [OLE32.@]
5566 * Creates a new compound file storage object
5569 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5570 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5571 * reserved [ ?] unused?, usually 0
5572 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5575 * S_OK if the file was successfully created
5576 * some STG_E_ value if error
5578 * if pwcsName is NULL, create file with new unique name
5579 * the function can returns
5580 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5583 HRESULT WINAPI
StgCreateDocfile(
5587 IStorage
**ppstgOpen
)
5589 StorageImpl
* newStorage
= 0;
5590 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5591 HRESULT hr
= STG_E_INVALIDFLAG
;
5595 DWORD fileAttributes
;
5596 WCHAR tempFileName
[MAX_PATH
];
5598 TRACE("(%s, %x, %d, %p)\n",
5599 debugstr_w(pwcsName
), grfMode
,
5600 reserved
, ppstgOpen
);
5603 return STG_E_INVALIDPOINTER
;
5605 return STG_E_INVALIDPARAMETER
;
5607 /* if no share mode given then DENY_NONE is the default */
5608 if (STGM_SHARE_MODE(grfMode
) == 0)
5609 grfMode
|= STGM_SHARE_DENY_NONE
;
5611 if ( FAILED( validateSTGM(grfMode
) ))
5614 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5615 switch(STGM_ACCESS_MODE(grfMode
))
5618 case STGM_READWRITE
:
5624 /* in direct mode, can only use SHARE_EXCLUSIVE */
5625 if (!(grfMode
& STGM_TRANSACTED
) && (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
))
5628 /* but in transacted mode, any share mode is valid */
5631 * Generate a unique name.
5635 WCHAR tempPath
[MAX_PATH
];
5636 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5638 memset(tempPath
, 0, sizeof(tempPath
));
5639 memset(tempFileName
, 0, sizeof(tempFileName
));
5641 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5644 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5645 pwcsName
= tempFileName
;
5648 hr
= STG_E_INSUFFICIENTMEMORY
;
5652 creationMode
= TRUNCATE_EXISTING
;
5656 creationMode
= GetCreationModeFromSTGM(grfMode
);
5660 * Interpret the STGM value grfMode
5662 shareMode
= FILE_SHARE_READ
| FILE_SHARE_WRITE
;
5663 accessMode
= GetAccessModeFromSTGM(grfMode
);
5665 if (grfMode
& STGM_DELETEONRELEASE
)
5666 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5668 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5670 if (STGM_SHARE_MODE(grfMode
) && !(grfMode
& STGM_SHARE_DENY_NONE
))
5671 FIXME("Storage share mode not implemented.\n");
5673 if (grfMode
& STGM_TRANSACTED
)
5674 FIXME("Transacted mode not implemented.\n");
5678 hFile
= CreateFileW(pwcsName
,
5686 if (hFile
== INVALID_HANDLE_VALUE
)
5688 if(GetLastError() == ERROR_FILE_EXISTS
)
5689 hr
= STG_E_FILEALREADYEXISTS
;
5696 * Allocate and initialize the new IStorage32object.
5698 hr
= StorageImpl_Construct(
5713 * Get an "out" pointer for the caller.
5715 *ppstgOpen
= (IStorage
*)newStorage
;
5718 TRACE("<-- %p r = %08x\n", *ppstgOpen
, hr
);
5723 /******************************************************************************
5724 * StgCreateStorageEx [OLE32.@]
5726 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5728 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5729 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5731 if (stgfmt
!= STGFMT_FILE
&& grfAttrs
!= 0)
5733 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5734 return STG_E_INVALIDPARAMETER
;
5737 if (stgfmt
== STGFMT_FILE
&& grfAttrs
!= 0 && grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5739 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5740 return STG_E_INVALIDPARAMETER
;
5743 if (stgfmt
== STGFMT_FILE
)
5745 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5746 return STG_E_INVALIDPARAMETER
;
5749 if (stgfmt
== STGFMT_STORAGE
|| stgfmt
== STGFMT_DOCFILE
)
5751 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5752 return StgCreateDocfile(pwcsName
, grfMode
, 0, (IStorage
**)ppObjectOpen
);
5755 ERR("Invalid stgfmt argument\n");
5756 return STG_E_INVALIDPARAMETER
;
5759 /******************************************************************************
5760 * StgCreatePropSetStg [OLE32.@]
5762 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
5763 IPropertySetStorage
**ppPropSetStg
)
5767 TRACE("(%p, 0x%x, %p)\n", pstg
, reserved
, ppPropSetStg
);
5769 hr
= STG_E_INVALIDPARAMETER
;
5771 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
5772 (void**)ppPropSetStg
);
5776 /******************************************************************************
5777 * StgOpenStorageEx [OLE32.@]
5779 HRESULT WINAPI
StgOpenStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5781 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5782 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5784 if (stgfmt
!= STGFMT_DOCFILE
&& grfAttrs
!= 0)
5786 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5787 return STG_E_INVALIDPARAMETER
;
5793 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5794 return STG_E_INVALIDPARAMETER
;
5796 case STGFMT_STORAGE
:
5799 case STGFMT_DOCFILE
:
5800 if (grfAttrs
&& grfAttrs
!= FILE_FLAG_NO_BUFFERING
)
5802 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5803 return STG_E_INVALIDPARAMETER
;
5805 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5809 WARN("STGFMT_ANY assuming storage\n");
5813 return STG_E_INVALIDPARAMETER
;
5816 return StgOpenStorage(pwcsName
, NULL
, grfMode
, NULL
, 0, (IStorage
**)ppObjectOpen
);
5820 /******************************************************************************
5821 * StgOpenStorage [OLE32.@]
5823 HRESULT WINAPI
StgOpenStorage(
5824 const OLECHAR
*pwcsName
,
5825 IStorage
*pstgPriority
,
5829 IStorage
**ppstgOpen
)
5831 StorageImpl
* newStorage
= 0;
5836 WCHAR fullname
[MAX_PATH
];
5838 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5839 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
5840 snbExclude
, reserved
, ppstgOpen
);
5844 hr
= STG_E_INVALIDNAME
;
5850 hr
= STG_E_INVALIDPOINTER
;
5856 hr
= STG_E_INVALIDPARAMETER
;
5860 if (grfMode
& STGM_PRIORITY
)
5862 if (grfMode
& (STGM_TRANSACTED
|STGM_SIMPLE
|STGM_NOSCRATCH
|STGM_NOSNAPSHOT
))
5863 return STG_E_INVALIDFLAG
;
5864 if (grfMode
& STGM_DELETEONRELEASE
)
5865 return STG_E_INVALIDFUNCTION
;
5866 if(STGM_ACCESS_MODE(grfMode
) != STGM_READ
)
5867 return STG_E_INVALIDFLAG
;
5868 grfMode
&= ~0xf0; /* remove the existing sharing mode */
5869 grfMode
|= STGM_SHARE_DENY_NONE
;
5871 /* STGM_PRIORITY stops other IStorage objects on the same file from
5872 * committing until the STGM_PRIORITY IStorage is closed. it also
5873 * stops non-transacted mode StgOpenStorage calls with write access from
5874 * succeeding. obviously, both of these cannot be achieved through just
5875 * file share flags */
5876 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5880 * Validate the sharing mode
5882 if (!(grfMode
& (STGM_TRANSACTED
|STGM_PRIORITY
)))
5883 switch(STGM_SHARE_MODE(grfMode
))
5885 case STGM_SHARE_EXCLUSIVE
:
5886 case STGM_SHARE_DENY_WRITE
:
5889 hr
= STG_E_INVALIDFLAG
;
5893 if ( FAILED( validateSTGM(grfMode
) ) ||
5894 (grfMode
&STGM_CREATE
))
5896 hr
= STG_E_INVALIDFLAG
;
5900 /* shared reading requires transacted mode */
5901 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
5902 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
5903 !(grfMode
&STGM_TRANSACTED
) )
5905 hr
= STG_E_INVALIDFLAG
;
5910 * Interpret the STGM value grfMode
5912 shareMode
= GetShareModeFromSTGM(grfMode
);
5913 accessMode
= GetAccessModeFromSTGM(grfMode
);
5917 hFile
= CreateFileW( pwcsName
,
5922 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5925 if (hFile
==INVALID_HANDLE_VALUE
)
5927 DWORD last_error
= GetLastError();
5933 case ERROR_FILE_NOT_FOUND
:
5934 hr
= STG_E_FILENOTFOUND
;
5937 case ERROR_PATH_NOT_FOUND
:
5938 hr
= STG_E_PATHNOTFOUND
;
5941 case ERROR_ACCESS_DENIED
:
5942 case ERROR_WRITE_PROTECT
:
5943 hr
= STG_E_ACCESSDENIED
;
5946 case ERROR_SHARING_VIOLATION
:
5947 hr
= STG_E_SHAREVIOLATION
;
5958 * Refuse to open the file if it's too small to be a structured storage file
5959 * FIXME: verify the file when reading instead of here
5961 if (GetFileSize(hFile
, NULL
) < 0x100)
5964 hr
= STG_E_FILEALREADYEXISTS
;
5969 * Allocate and initialize the new IStorage32object.
5971 hr
= StorageImpl_Construct(
5983 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5985 if(hr
== STG_E_INVALIDHEADER
)
5986 hr
= STG_E_FILEALREADYEXISTS
;
5990 /* prepare the file name string given in lieu of the root property name */
5991 GetFullPathNameW(pwcsName
, MAX_PATH
, fullname
, NULL
);
5992 memcpy(newStorage
->base
.filename
, fullname
, DIRENTRY_NAME_BUFFER_LEN
);
5993 newStorage
->base
.filename
[DIRENTRY_NAME_BUFFER_LEN
-1] = '\0';
5996 * Get an "out" pointer for the caller.
5998 *ppstgOpen
= (IStorage
*)newStorage
;
6001 TRACE("<-- %08x, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
6005 /******************************************************************************
6006 * StgCreateDocfileOnILockBytes [OLE32.@]
6008 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
6012 IStorage
** ppstgOpen
)
6014 StorageImpl
* newStorage
= 0;
6017 if ((ppstgOpen
== 0) || (plkbyt
== 0))
6018 return STG_E_INVALIDPOINTER
;
6021 * Allocate and initialize the new IStorage object.
6023 hr
= StorageImpl_Construct(
6038 * Get an "out" pointer for the caller.
6040 *ppstgOpen
= (IStorage
*)newStorage
;
6045 /******************************************************************************
6046 * StgOpenStorageOnILockBytes [OLE32.@]
6048 HRESULT WINAPI
StgOpenStorageOnILockBytes(
6050 IStorage
*pstgPriority
,
6054 IStorage
**ppstgOpen
)
6056 StorageImpl
* newStorage
= 0;
6059 if ((plkbyt
== 0) || (ppstgOpen
== 0))
6060 return STG_E_INVALIDPOINTER
;
6062 if ( FAILED( validateSTGM(grfMode
) ))
6063 return STG_E_INVALIDFLAG
;
6068 * Allocate and initialize the new IStorage object.
6070 hr
= StorageImpl_Construct(
6085 * Get an "out" pointer for the caller.
6087 *ppstgOpen
= (IStorage
*)newStorage
;
6092 /******************************************************************************
6093 * StgSetTimes [ole32.@]
6094 * StgSetTimes [OLE32.@]
6098 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
6099 FILETIME
const *patime
, FILETIME
const *pmtime
)
6101 IStorage
*stg
= NULL
;
6104 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
6106 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
6110 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
6111 IStorage_Release(stg
);
6117 /******************************************************************************
6118 * StgIsStorageILockBytes [OLE32.@]
6120 * Determines if the ILockBytes contains a storage object.
6122 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
6125 ULARGE_INTEGER offset
;
6127 offset
.u
.HighPart
= 0;
6128 offset
.u
.LowPart
= 0;
6130 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
6132 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
6138 /******************************************************************************
6139 * WriteClassStg [OLE32.@]
6141 * This method will store the specified CLSID in the specified storage object
6143 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
6148 return E_INVALIDARG
;
6151 return STG_E_INVALIDPOINTER
;
6153 hRes
= IStorage_SetClass(pStg
, rclsid
);
6158 /***********************************************************************
6159 * ReadClassStg (OLE32.@)
6161 * This method reads the CLSID previously written to a storage object with
6162 * the WriteClassStg.
6165 * pstg [I] IStorage pointer
6166 * pclsid [O] Pointer to where the CLSID is written
6170 * Failure: HRESULT code.
6172 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
6177 TRACE("(%p, %p)\n", pstg
, pclsid
);
6179 if(!pstg
|| !pclsid
)
6180 return E_INVALIDARG
;
6183 * read a STATSTG structure (contains the clsid) from the storage
6185 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_NONAME
);
6188 *pclsid
=pstatstg
.clsid
;
6193 /***********************************************************************
6194 * OleLoadFromStream (OLE32.@)
6196 * This function loads an object from stream
6198 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6202 LPPERSISTSTREAM xstm
;
6204 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6206 res
=ReadClassStm(pStm
,&clsid
);
6209 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6212 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6214 IUnknown_Release((IUnknown
*)*ppvObj
);
6217 res
=IPersistStream_Load(xstm
,pStm
);
6218 IPersistStream_Release(xstm
);
6219 /* FIXME: all refcounts ok at this point? I think they should be:
6222 * xstm : 0 (released)
6227 /***********************************************************************
6228 * OleSaveToStream (OLE32.@)
6230 * This function saves an object with the IPersistStream interface on it
6231 * to the specified stream.
6233 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6239 TRACE("(%p,%p)\n",pPStm
,pStm
);
6241 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6243 if (SUCCEEDED(res
)){
6245 res
=WriteClassStm(pStm
,&clsid
);
6249 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6252 TRACE("Finished Save\n");
6256 /****************************************************************************
6257 * This method validate a STGM parameter that can contain the values below
6259 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6260 * The stgm values contained in 0xffff0000 are bitmasks.
6262 * STGM_DIRECT 0x00000000
6263 * STGM_TRANSACTED 0x00010000
6264 * STGM_SIMPLE 0x08000000
6266 * STGM_READ 0x00000000
6267 * STGM_WRITE 0x00000001
6268 * STGM_READWRITE 0x00000002
6270 * STGM_SHARE_DENY_NONE 0x00000040
6271 * STGM_SHARE_DENY_READ 0x00000030
6272 * STGM_SHARE_DENY_WRITE 0x00000020
6273 * STGM_SHARE_EXCLUSIVE 0x00000010
6275 * STGM_PRIORITY 0x00040000
6276 * STGM_DELETEONRELEASE 0x04000000
6278 * STGM_CREATE 0x00001000
6279 * STGM_CONVERT 0x00020000
6280 * STGM_FAILIFTHERE 0x00000000
6282 * STGM_NOSCRATCH 0x00100000
6283 * STGM_NOSNAPSHOT 0x00200000
6285 static HRESULT
validateSTGM(DWORD stgm
)
6287 DWORD access
= STGM_ACCESS_MODE(stgm
);
6288 DWORD share
= STGM_SHARE_MODE(stgm
);
6289 DWORD create
= STGM_CREATE_MODE(stgm
);
6291 if (stgm
&~STGM_KNOWN_FLAGS
)
6293 ERR("unknown flags %08x\n", stgm
);
6301 case STGM_READWRITE
:
6309 case STGM_SHARE_DENY_NONE
:
6310 case STGM_SHARE_DENY_READ
:
6311 case STGM_SHARE_DENY_WRITE
:
6312 case STGM_SHARE_EXCLUSIVE
:
6321 case STGM_FAILIFTHERE
:
6328 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6330 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
6334 * STGM_CREATE | STGM_CONVERT
6335 * if both are false, STGM_FAILIFTHERE is set to TRUE
6337 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
6341 * STGM_NOSCRATCH requires STGM_TRANSACTED
6343 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
6347 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6348 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6350 if ( (stgm
& STGM_NOSNAPSHOT
) &&
6351 (!(stgm
& STGM_TRANSACTED
) ||
6352 share
== STGM_SHARE_EXCLUSIVE
||
6353 share
== STGM_SHARE_DENY_WRITE
) )
6359 /****************************************************************************
6360 * GetShareModeFromSTGM
6362 * This method will return a share mode flag from a STGM value.
6363 * The STGM value is assumed valid.
6365 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
6367 switch (STGM_SHARE_MODE(stgm
))
6369 case STGM_SHARE_DENY_NONE
:
6370 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6371 case STGM_SHARE_DENY_READ
:
6372 return FILE_SHARE_WRITE
;
6373 case STGM_SHARE_DENY_WRITE
:
6374 return FILE_SHARE_READ
;
6375 case STGM_SHARE_EXCLUSIVE
:
6378 ERR("Invalid share mode!\n");
6383 /****************************************************************************
6384 * GetAccessModeFromSTGM
6386 * This method will return an access mode flag from a STGM value.
6387 * The STGM value is assumed valid.
6389 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
6391 switch (STGM_ACCESS_MODE(stgm
))
6394 return GENERIC_READ
;
6396 case STGM_READWRITE
:
6397 return GENERIC_READ
| GENERIC_WRITE
;
6399 ERR("Invalid access mode!\n");
6404 /****************************************************************************
6405 * GetCreationModeFromSTGM
6407 * This method will return a creation mode flag from a STGM value.
6408 * The STGM value is assumed valid.
6410 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
6412 switch(STGM_CREATE_MODE(stgm
))
6415 return CREATE_ALWAYS
;
6417 FIXME("STGM_CONVERT not implemented!\n");
6419 case STGM_FAILIFTHERE
:
6422 ERR("Invalid create mode!\n");
6428 /*************************************************************************
6429 * OLECONVERT_LoadOLE10 [Internal]
6431 * Loads the OLE10 STREAM to memory
6434 * pOleStream [I] The OLESTREAM
6435 * pData [I] Data Structure for the OLESTREAM Data
6439 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6440 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6443 * This function is used by OleConvertOLESTREAMToIStorage only.
6445 * Memory allocated for pData must be freed by the caller
6447 static HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
6450 HRESULT hRes
= S_OK
;
6454 pData
->pData
= NULL
;
6455 pData
->pstrOleObjFileName
= NULL
;
6457 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
6460 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6461 if(dwSize
!= sizeof(pData
->dwOleID
))
6463 hRes
= CONVERT10_E_OLESTREAM_GET
;
6465 else if(pData
->dwOleID
!= OLESTREAM_ID
)
6467 hRes
= CONVERT10_E_OLESTREAM_FMT
;
6478 /* Get the TypeID... more info needed for this field */
6479 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6480 if(dwSize
!= sizeof(pData
->dwTypeID
))
6482 hRes
= CONVERT10_E_OLESTREAM_GET
;
6487 if(pData
->dwTypeID
!= 0)
6489 /* Get the length of the OleTypeName */
6490 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6491 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6493 hRes
= CONVERT10_E_OLESTREAM_GET
;
6498 if(pData
->dwOleTypeNameLength
> 0)
6500 /* Get the OleTypeName */
6501 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6502 if(dwSize
!= pData
->dwOleTypeNameLength
)
6504 hRes
= CONVERT10_E_OLESTREAM_GET
;
6510 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6511 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6513 hRes
= CONVERT10_E_OLESTREAM_GET
;
6517 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
6518 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
6519 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
6520 if(pData
->pstrOleObjFileName
)
6522 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->pstrOleObjFileName
, pData
->dwOleObjFileNameLength
);
6523 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6525 hRes
= CONVERT10_E_OLESTREAM_GET
;
6529 hRes
= CONVERT10_E_OLESTREAM_GET
;
6534 /* Get the Width of the Metafile */
6535 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6536 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6538 hRes
= CONVERT10_E_OLESTREAM_GET
;
6542 /* Get the Height of the Metafile */
6543 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6544 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6546 hRes
= CONVERT10_E_OLESTREAM_GET
;
6552 /* Get the Length of the Data */
6553 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6554 if(dwSize
!= sizeof(pData
->dwDataLength
))
6556 hRes
= CONVERT10_E_OLESTREAM_GET
;
6560 if(hRes
== S_OK
) /* I don't know what this 8 byte information is. We have to figure out */
6562 if(!bStrem1
) /* if it is a second OLE stream data */
6564 pData
->dwDataLength
-= 8;
6565 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, pData
->strUnknown
, sizeof(pData
->strUnknown
));
6566 if(dwSize
!= sizeof(pData
->strUnknown
))
6568 hRes
= CONVERT10_E_OLESTREAM_GET
;
6574 if(pData
->dwDataLength
> 0)
6576 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6578 /* Get Data (ex. IStorage, Metafile, or BMP) */
6581 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6582 if(dwSize
!= pData
->dwDataLength
)
6584 hRes
= CONVERT10_E_OLESTREAM_GET
;
6589 hRes
= CONVERT10_E_OLESTREAM_GET
;
6598 /*************************************************************************
6599 * OLECONVERT_SaveOLE10 [Internal]
6601 * Saves the OLE10 STREAM From memory
6604 * pData [I] Data Structure for the OLESTREAM Data
6605 * pOleStream [I] The OLESTREAM to save
6609 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6612 * This function is used by OleConvertIStorageToOLESTREAM only.
6615 static HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6618 HRESULT hRes
= S_OK
;
6622 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6623 if(dwSize
!= sizeof(pData
->dwOleID
))
6625 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6630 /* Set the TypeID */
6631 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6632 if(dwSize
!= sizeof(pData
->dwTypeID
))
6634 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6638 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
6640 /* Set the Length of the OleTypeName */
6641 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6642 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6644 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6649 if(pData
->dwOleTypeNameLength
> 0)
6651 /* Set the OleTypeName */
6652 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6653 if(dwSize
!= pData
->dwOleTypeNameLength
)
6655 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6662 /* Set the width of the Metafile */
6663 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6664 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6666 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6672 /* Set the height of the Metafile */
6673 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6674 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6676 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6682 /* Set the length of the Data */
6683 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6684 if(dwSize
!= sizeof(pData
->dwDataLength
))
6686 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6692 if(pData
->dwDataLength
> 0)
6694 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6695 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
6696 if(dwSize
!= pData
->dwDataLength
)
6698 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6706 /*************************************************************************
6707 * OLECONVERT_GetOLE20FromOLE10[Internal]
6709 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6710 * opens it, and copies the content to the dest IStorage for
6711 * OleConvertOLESTREAMToIStorage
6715 * pDestStorage [I] The IStorage to copy the data to
6716 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6717 * nBufferLength [I] The size of the buffer
6726 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, const BYTE
*pBuffer
, DWORD nBufferLength
)
6730 IStorage
*pTempStorage
;
6731 DWORD dwNumOfBytesWritten
;
6732 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6733 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6735 /* Create a temp File */
6736 GetTempPathW(MAX_PATH
, wstrTempDir
);
6737 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6738 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
6740 if(hFile
!= INVALID_HANDLE_VALUE
)
6742 /* Write IStorage Data to File */
6743 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
6746 /* Open and copy temp storage to the Dest Storage */
6747 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
6750 hRes
= IStorage_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
6751 IStorage_Release(pTempStorage
);
6753 DeleteFileW(wstrTempFile
);
6758 /*************************************************************************
6759 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6761 * Saves the OLE10 STREAM From memory
6764 * pStorage [I] The Src IStorage to copy
6765 * pData [I] The Dest Memory to write to.
6768 * The size in bytes allocated for pData
6771 * Memory allocated for pData must be freed by the caller
6773 * Used by OleConvertIStorageToOLESTREAM only.
6776 static DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
6780 DWORD nDataLength
= 0;
6781 IStorage
*pTempStorage
;
6782 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6783 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6787 /* Create temp Storage */
6788 GetTempPathW(MAX_PATH
, wstrTempDir
);
6789 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6790 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
6794 /* Copy Src Storage to the Temp Storage */
6795 IStorage_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
6796 IStorage_Release(pTempStorage
);
6798 /* Open Temp Storage as a file and copy to memory */
6799 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
6800 if(hFile
!= INVALID_HANDLE_VALUE
)
6802 nDataLength
= GetFileSize(hFile
, NULL
);
6803 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
6804 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
6807 DeleteFileW(wstrTempFile
);
6812 /*************************************************************************
6813 * OLECONVERT_CreateOleStream [Internal]
6815 * Creates the "\001OLE" stream in the IStorage if necessary.
6818 * pStorage [I] Dest storage to create the stream in
6824 * This function is used by OleConvertOLESTREAMToIStorage only.
6826 * This stream is still unknown, MS Word seems to have extra data
6827 * but since the data is stored in the OLESTREAM there should be
6828 * no need to recreate the stream. If the stream is manually
6829 * deleted it will create it with this default data.
6832 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
6836 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
6837 BYTE pOleStreamHeader
[] =
6839 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6840 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6841 0x00, 0x00, 0x00, 0x00
6844 /* Create stream if not present */
6845 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6846 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6850 /* Write default Data */
6851 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
6852 IStream_Release(pStream
);
6856 /* write a string to a stream, preceded by its length */
6857 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
6864 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
6865 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
6870 str
= CoTaskMemAlloc( len
);
6871 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
6872 r
= IStream_Write( stm
, str
, len
, NULL
);
6873 CoTaskMemFree( str
);
6877 /* read a string preceded by its length from a stream */
6878 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
6881 DWORD len
, count
= 0;
6885 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
6888 if( count
!= sizeof(len
) )
6889 return E_OUTOFMEMORY
;
6891 TRACE("%d bytes\n",len
);
6893 str
= CoTaskMemAlloc( len
);
6895 return E_OUTOFMEMORY
;
6897 r
= IStream_Read( stm
, str
, len
, &count
);
6902 CoTaskMemFree( str
);
6903 return E_OUTOFMEMORY
;
6906 TRACE("Read string %s\n",debugstr_an(str
,len
));
6908 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
6909 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
6911 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
6912 CoTaskMemFree( str
);
6920 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
6921 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
6925 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6927 static const BYTE unknown1
[12] =
6928 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6929 0xFF, 0xFF, 0xFF, 0xFF};
6930 static const BYTE unknown2
[16] =
6931 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6934 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
6935 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
6936 debugstr_w(szProgIDName
));
6938 /* Create a CompObj stream */
6939 r
= IStorage_CreateStream(pstg
, szwStreamName
,
6940 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
6944 /* Write CompObj Structure to stream */
6945 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
6947 if( SUCCEEDED( r
) )
6948 r
= WriteClassStm( pstm
, clsid
);
6950 if( SUCCEEDED( r
) )
6951 r
= STREAM_WriteString( pstm
, lpszUserType
);
6952 if( SUCCEEDED( r
) )
6953 r
= STREAM_WriteString( pstm
, szClipName
);
6954 if( SUCCEEDED( r
) )
6955 r
= STREAM_WriteString( pstm
, szProgIDName
);
6956 if( SUCCEEDED( r
) )
6957 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
6959 IStream_Release( pstm
);
6964 /***********************************************************************
6965 * WriteFmtUserTypeStg (OLE32.@)
6967 HRESULT WINAPI
WriteFmtUserTypeStg(
6968 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
6971 WCHAR szwClipName
[0x40];
6972 CLSID clsid
= CLSID_NULL
;
6973 LPWSTR wstrProgID
= NULL
;
6976 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
6978 /* get the clipboard format name */
6979 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
)/sizeof(szwClipName
[0]) );
6982 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
6984 /* FIXME: There's room to save a CLSID and its ProgID, but
6985 the CLSID is not looked up in the registry and in all the
6986 tests I wrote it was CLSID_NULL. Where does it come from?
6989 /* get the real program ID. This may fail, but that's fine */
6990 ProgIDFromCLSID(&clsid
, &wstrProgID
);
6992 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
6994 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
6995 lpszUserType
, szwClipName
, wstrProgID
);
6997 CoTaskMemFree(wstrProgID
);
7003 /******************************************************************************
7004 * ReadFmtUserTypeStg [OLE32.@]
7006 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
7010 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
7011 unsigned char unknown1
[12];
7012 unsigned char unknown2
[16];
7014 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
7017 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
7019 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
7020 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
7023 WARN("Failed to open stream r = %08x\n", r
);
7027 /* read the various parts of the structure */
7028 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
7029 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
7031 r
= ReadClassStm( stm
, &clsid
);
7035 r
= STREAM_ReadString( stm
, &szCLSIDName
);
7039 r
= STREAM_ReadString( stm
, &szOleTypeName
);
7043 r
= STREAM_ReadString( stm
, &szProgIDName
);
7047 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
7048 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
7051 /* ok, success... now we just need to store what we found */
7053 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
7054 CoTaskMemFree( szOleTypeName
);
7056 if( lplpszUserType
)
7057 *lplpszUserType
= szCLSIDName
;
7058 CoTaskMemFree( szProgIDName
);
7061 IStream_Release( stm
);
7067 /*************************************************************************
7068 * OLECONVERT_CreateCompObjStream [Internal]
7070 * Creates a "\001CompObj" is the destination IStorage if necessary.
7073 * pStorage [I] The dest IStorage to create the CompObj Stream
7075 * strOleTypeName [I] The ProgID
7079 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7082 * This function is used by OleConvertOLESTREAMToIStorage only.
7084 * The stream data is stored in the OLESTREAM and there should be
7085 * no need to recreate the stream. If the stream is manually
7086 * deleted it will attempt to create it by querying the registry.
7090 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
7093 HRESULT hStorageRes
, hRes
= S_OK
;
7094 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
7095 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7096 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
7098 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7099 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
7101 /* Initialize the CompObj structure */
7102 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
7103 memcpy(IStorageCompObj
.byUnknown1
, pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
7104 memcpy(IStorageCompObj
.byUnknown2
, pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
7107 /* Create a CompObj stream if it doesn't exist */
7108 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7109 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7110 if(hStorageRes
== S_OK
)
7112 /* copy the OleTypeName to the compobj struct */
7113 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
7114 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
7116 /* copy the OleTypeName to the compobj struct */
7117 /* Note: in the test made, these were Identical */
7118 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
7119 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
7122 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
7123 bufferW
, OLESTREAM_MAX_STR_LEN
);
7124 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
7130 /* Get the CLSID Default Name from the Registry */
7131 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
7132 if(hErr
== ERROR_SUCCESS
)
7134 char strTemp
[OLESTREAM_MAX_STR_LEN
];
7135 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
7136 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, (LONG
*) &(IStorageCompObj
.dwCLSIDNameLength
));
7137 if(hErr
== ERROR_SUCCESS
)
7139 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
7145 /* Write CompObj Structure to stream */
7146 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
7148 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
7150 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
7151 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
7153 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
7155 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
7156 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
7158 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
7160 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
7161 if(IStorageCompObj
.dwProgIDNameLength
> 0)
7163 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
7165 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
7166 IStream_Release(pStream
);
7172 /*************************************************************************
7173 * OLECONVERT_CreateOlePresStream[Internal]
7175 * Creates the "\002OlePres000" Stream with the Metafile data
7178 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7179 * dwExtentX [I] Width of the Metafile
7180 * dwExtentY [I] Height of the Metafile
7181 * pData [I] Metafile data
7182 * dwDataLength [I] Size of the Metafile data
7186 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7189 * This function is used by OleConvertOLESTREAMToIStorage only.
7192 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
7196 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7197 BYTE pOlePresStreamHeader
[] =
7199 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7200 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7201 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7202 0x00, 0x00, 0x00, 0x00
7205 BYTE pOlePresStreamHeaderEmpty
[] =
7207 0x00, 0x00, 0x00, 0x00,
7208 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7209 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7210 0x00, 0x00, 0x00, 0x00
7213 /* Create the OlePres000 Stream */
7214 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7215 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7220 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7222 memset(&OlePres
, 0, sizeof(OlePres
));
7223 /* Do we have any metafile data to save */
7224 if(dwDataLength
> 0)
7226 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7227 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7231 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7232 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7234 /* Set width and height of the metafile */
7235 OlePres
.dwExtentX
= dwExtentX
;
7236 OlePres
.dwExtentY
= -dwExtentY
;
7238 /* Set Data and Length */
7239 if(dwDataLength
> sizeof(METAFILEPICT16
))
7241 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7242 OlePres
.pData
= &(pData
[8]);
7244 /* Save OlePres000 Data to Stream */
7245 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7246 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7247 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7248 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7249 if(OlePres
.dwSize
> 0)
7251 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7253 IStream_Release(pStream
);
7257 /*************************************************************************
7258 * OLECONVERT_CreateOle10NativeStream [Internal]
7260 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7263 * pStorage [I] Dest storage to create the stream in
7264 * pData [I] Ole10 Native Data (ex. bmp)
7265 * dwDataLength [I] Size of the Ole10 Native Data
7271 * This function is used by OleConvertOLESTREAMToIStorage only.
7273 * Might need to verify the data and return appropriate error message
7276 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, const BYTE
*pData
, DWORD dwDataLength
)
7280 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7282 /* Create the Ole10Native Stream */
7283 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7284 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7288 /* Write info to stream */
7289 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7290 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7291 IStream_Release(pStream
);
7296 /*************************************************************************
7297 * OLECONVERT_GetOLE10ProgID [Internal]
7299 * Finds the ProgID (or OleTypeID) from the IStorage
7302 * pStorage [I] The Src IStorage to get the ProgID
7303 * strProgID [I] the ProgID string to get
7304 * dwSize [I] the size of the string
7308 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7311 * This function is used by OleConvertIStorageToOLESTREAM only.
7315 static HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7319 LARGE_INTEGER iSeekPos
;
7320 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7321 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7323 /* Open the CompObj Stream */
7324 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7325 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7329 /*Get the OleType from the CompObj Stream */
7330 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
7331 iSeekPos
.u
.HighPart
= 0;
7333 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7334 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
7335 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
7336 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7337 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
7338 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
7339 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7341 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
7344 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7346 IStream_Release(pStream
);
7351 LPOLESTR wstrProgID
;
7353 /* Get the OleType from the registry */
7354 REFCLSID clsid
= &(stat
.clsid
);
7355 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
7356 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
7359 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7366 /*************************************************************************
7367 * OLECONVERT_GetOle10PresData [Internal]
7369 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7372 * pStorage [I] Src IStroage
7373 * pOleStream [I] Dest OleStream Mem Struct
7379 * This function is used by OleConvertIStorageToOLESTREAM only.
7381 * Memory allocated for pData must be freed by the caller
7385 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7390 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7392 /* Initialize Default data for OLESTREAM */
7393 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7394 pOleStreamData
[0].dwTypeID
= 2;
7395 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7396 pOleStreamData
[1].dwTypeID
= 0;
7397 pOleStreamData
[0].dwMetaFileWidth
= 0;
7398 pOleStreamData
[0].dwMetaFileHeight
= 0;
7399 pOleStreamData
[0].pData
= NULL
;
7400 pOleStreamData
[1].pData
= NULL
;
7402 /* Open Ole10Native Stream */
7403 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7404 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7408 /* Read Size and Data */
7409 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
7410 if(pOleStreamData
->dwDataLength
> 0)
7412 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
7413 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
7415 IStream_Release(pStream
);
7421 /*************************************************************************
7422 * OLECONVERT_GetOle20PresData[Internal]
7424 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7427 * pStorage [I] Src IStroage
7428 * pOleStreamData [I] Dest OleStream Mem Struct
7434 * This function is used by OleConvertIStorageToOLESTREAM only.
7436 * Memory allocated for pData must be freed by the caller
7438 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7442 OLECONVERT_ISTORAGE_OLEPRES olePress
;
7443 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7445 /* Initialize Default data for OLESTREAM */
7446 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7447 pOleStreamData
[0].dwTypeID
= 2;
7448 pOleStreamData
[0].dwMetaFileWidth
= 0;
7449 pOleStreamData
[0].dwMetaFileHeight
= 0;
7450 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
7451 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7452 pOleStreamData
[1].dwTypeID
= 0;
7453 pOleStreamData
[1].dwOleTypeNameLength
= 0;
7454 pOleStreamData
[1].strOleTypeName
[0] = 0;
7455 pOleStreamData
[1].dwMetaFileWidth
= 0;
7456 pOleStreamData
[1].dwMetaFileHeight
= 0;
7457 pOleStreamData
[1].pData
= NULL
;
7458 pOleStreamData
[1].dwDataLength
= 0;
7461 /* Open OlePress000 stream */
7462 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7463 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7466 LARGE_INTEGER iSeekPos
;
7467 METAFILEPICT16 MetaFilePict
;
7468 static const char strMetafilePictName
[] = "METAFILEPICT";
7470 /* Set the TypeID for a Metafile */
7471 pOleStreamData
[1].dwTypeID
= 5;
7473 /* Set the OleTypeName to Metafile */
7474 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
7475 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
7477 iSeekPos
.u
.HighPart
= 0;
7478 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
7480 /* Get Presentation Data */
7481 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7482 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
7483 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
7484 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
7486 /*Set width and Height */
7487 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
7488 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
7489 if(olePress
.dwSize
> 0)
7492 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
7494 /* Set MetaFilePict struct */
7495 MetaFilePict
.mm
= 8;
7496 MetaFilePict
.xExt
= olePress
.dwExtentX
;
7497 MetaFilePict
.yExt
= olePress
.dwExtentY
;
7498 MetaFilePict
.hMF
= 0;
7500 /* Get Metafile Data */
7501 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
7502 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
7503 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
7505 IStream_Release(pStream
);
7509 /*************************************************************************
7510 * OleConvertOLESTREAMToIStorage [OLE32.@]
7515 * DVTARGETDEVICE parameter is not handled
7516 * Still unsure of some mem fields for OLE 10 Stream
7517 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7518 * and "\001OLE" streams
7521 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
7522 LPOLESTREAM pOleStream
,
7524 const DVTARGETDEVICE
* ptd
)
7528 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7530 TRACE("%p %p %p\n", pOleStream
, pstg
, ptd
);
7532 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7536 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7539 if(pstg
== NULL
|| pOleStream
== NULL
)
7541 hRes
= E_INVALIDARG
;
7546 /* Load the OLESTREAM to Memory */
7547 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7552 /* Load the OLESTREAM to Memory (part 2)*/
7553 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
7559 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
7561 /* Do we have the IStorage Data in the OLESTREAM */
7562 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
7564 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7565 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
7569 /* It must be an original OLE 1.0 source */
7570 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7575 /* It must be an original OLE 1.0 source */
7576 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7579 /* Create CompObj Stream if necessary */
7580 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
7583 /*Create the Ole Stream if necessary */
7584 OLECONVERT_CreateOleStream(pstg
);
7589 /* Free allocated memory */
7590 for(i
=0; i
< 2; i
++)
7592 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7593 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
7594 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
7599 /*************************************************************************
7600 * OleConvertIStorageToOLESTREAM [OLE32.@]
7607 * Still unsure of some mem fields for OLE 10 Stream
7608 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7609 * and "\001OLE" streams.
7612 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
7614 LPOLESTREAM pOleStream
)
7617 HRESULT hRes
= S_OK
;
7619 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7620 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7622 TRACE("%p %p\n", pstg
, pOleStream
);
7624 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7626 if(pstg
== NULL
|| pOleStream
== NULL
)
7628 hRes
= E_INVALIDARG
;
7632 /* Get the ProgID */
7633 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7634 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7638 /* Was it originally Ole10 */
7639 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7642 IStream_Release(pStream
);
7643 /* Get Presentation Data for Ole10Native */
7644 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7648 /* Get Presentation Data (OLE20) */
7649 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7652 /* Save OLESTREAM */
7653 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
7656 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
7661 /* Free allocated memory */
7662 for(i
=0; i
< 2; i
++)
7664 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7670 /***********************************************************************
7671 * GetConvertStg (OLE32.@)
7673 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7674 FIXME("unimplemented stub!\n");
7678 /******************************************************************************
7679 * StgIsStorageFile [OLE32.@]
7680 * Verify if the file contains a storage object
7686 * S_OK if file has magic bytes as a storage object
7687 * S_FALSE if file is not storage
7690 StgIsStorageFile(LPCOLESTR fn
)
7696 TRACE("%s\n", debugstr_w(fn
));
7697 hf
= CreateFileW(fn
, GENERIC_READ
,
7698 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
7699 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7701 if (hf
== INVALID_HANDLE_VALUE
)
7702 return STG_E_FILENOTFOUND
;
7704 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
7706 WARN(" unable to read file\n");
7713 if (bytes_read
!= 8) {
7714 WARN(" too short\n");
7718 if (!memcmp(magic
,STORAGE_magic
,8)) {
7723 WARN(" -> Invalid header.\n");
7727 /***********************************************************************
7728 * WriteClassStm (OLE32.@)
7730 * Writes a CLSID to a stream.
7733 * pStm [I] Stream to write to.
7734 * rclsid [I] CLSID to write.
7738 * Failure: HRESULT code.
7740 HRESULT WINAPI
WriteClassStm(IStream
*pStm
,REFCLSID rclsid
)
7742 TRACE("(%p,%p)\n",pStm
,rclsid
);
7744 if (!pStm
|| !rclsid
)
7745 return E_INVALIDARG
;
7747 return IStream_Write(pStm
,rclsid
,sizeof(CLSID
),NULL
);
7750 /***********************************************************************
7751 * ReadClassStm (OLE32.@)
7753 * Reads a CLSID from a stream.
7756 * pStm [I] Stream to read from.
7757 * rclsid [O] CLSID to read.
7761 * Failure: HRESULT code.
7763 HRESULT WINAPI
ReadClassStm(IStream
*pStm
,CLSID
*pclsid
)
7768 TRACE("(%p,%p)\n",pStm
,pclsid
);
7770 if (!pStm
|| !pclsid
)
7771 return E_INVALIDARG
;
7773 /* clear the output args */
7774 *pclsid
= CLSID_NULL
;
7776 res
= IStream_Read(pStm
,(void*)pclsid
,sizeof(CLSID
),&nbByte
);
7781 if (nbByte
!= sizeof(CLSID
))
7782 return STG_E_READFAULT
;