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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
42 #include "wine/unicode.h"
43 #include "wine/debug.h"
45 #include "storage32.h"
46 #include "ole2.h" /* For Write/ReadClassStm */
49 #include "wine/wingdi16.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(storage
);
55 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
56 #define OLESTREAM_ID 0x501
57 #define OLESTREAM_MAX_STR_LEN 255
59 static const char rootPropertyName
[] = "Root Entry";
62 /* OLESTREAM memory structure to use for Get and Put Routines */
63 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
68 DWORD dwOleTypeNameLength
;
69 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
70 CHAR
*pstrOleObjFileName
;
71 DWORD dwOleObjFileNameLength
;
72 DWORD dwMetaFileWidth
;
73 DWORD dwMetaFileHeight
;
74 CHAR strUnknown
[8]; /* don't know what is this 8 byts information in OLE stream. */
77 }OLECONVERT_OLESTREAM_DATA
;
79 /* CompObj Stream structure */
80 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
85 DWORD dwCLSIDNameLength
;
86 CHAR strCLSIDName
[OLESTREAM_MAX_STR_LEN
];
87 DWORD dwOleTypeNameLength
;
88 CHAR strOleTypeName
[OLESTREAM_MAX_STR_LEN
];
89 DWORD dwProgIDNameLength
;
90 CHAR strProgIDName
[OLESTREAM_MAX_STR_LEN
];
92 }OLECONVERT_ISTORAGE_COMPOBJ
;
95 /* Ole Presention Stream structure */
96 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
104 }OLECONVERT_ISTORAGE_OLEPRES
;
108 /***********************************************************************
109 * Forward declaration of internal functions used by the method DestroyElement
111 static HRESULT
deleteStorageProperty(
112 StorageImpl
*parentStorage
,
113 ULONG foundPropertyIndexToDelete
,
114 StgProperty propertyToDelete
);
116 static HRESULT
deleteStreamProperty(
117 StorageImpl
*parentStorage
,
118 ULONG foundPropertyIndexToDelete
,
119 StgProperty propertyToDelete
);
121 static HRESULT
findPlaceholder(
122 StorageImpl
*storage
,
123 ULONG propertyIndexToStore
,
124 ULONG storagePropertyIndex
,
127 static HRESULT
adjustPropertyChain(
129 StgProperty propertyToDelete
,
130 StgProperty parentProperty
,
131 ULONG parentPropertyId
,
134 /***********************************************************************
135 * Declaration of the functions used to manipulate StgProperty
138 static ULONG
getFreeProperty(
139 StorageImpl
*storage
);
141 static void updatePropertyChain(
142 StorageImpl
*storage
,
143 ULONG newPropertyIndex
,
144 StgProperty newProperty
);
146 static LONG
propertyNameCmp(
147 const OLECHAR
*newProperty
,
148 const OLECHAR
*currentProperty
);
151 /***********************************************************************
152 * Declaration of miscellaneous functions...
154 static HRESULT
validateSTGM(DWORD stgmValue
);
156 static DWORD
GetShareModeFromSTGM(DWORD stgm
);
157 static DWORD
GetAccessModeFromSTGM(DWORD stgm
);
158 static DWORD
GetCreationModeFromSTGM(DWORD stgm
);
160 extern IPropertySetStorageVtbl IPropertySetStorage_Vtbl
;
164 /************************************************************************
165 ** Storage32BaseImpl implementatiion
168 /************************************************************************
169 * Storage32BaseImpl_QueryInterface (IUnknown)
171 * This method implements the common QueryInterface for all IStorage32
172 * implementations contained in this file.
174 * See Windows documentation for more details on IUnknown methods.
176 HRESULT WINAPI
StorageBaseImpl_QueryInterface(
181 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
183 * Perform a sanity check on the parameters.
185 if ( (This
==0) || (ppvObject
==0) )
189 * Initialize the return parameter.
194 * Compare the riid with the interface IDs implemented by this object.
196 if (memcmp(&IID_IUnknown
, riid
, sizeof(IID_IUnknown
)) == 0)
198 *ppvObject
= (IStorage
*)This
;
200 else if (memcmp(&IID_IStorage
, riid
, sizeof(IID_IStorage
)) == 0)
202 *ppvObject
= (IStorage
*)This
;
204 else if (memcmp(&IID_IPropertySetStorage
, riid
, sizeof(IID_IPropertySetStorage
)) == 0)
206 *ppvObject
= (IStorage
*)&This
->pssVtbl
;
210 * Check that we obtained an interface.
213 return E_NOINTERFACE
;
216 * Query Interface always increases the reference count by one when it is
219 IStorage_AddRef(iface
);
224 /************************************************************************
225 * Storage32BaseImpl_AddRef (IUnknown)
227 * This method implements the common AddRef for all IStorage32
228 * implementations contained in this file.
230 * See Windows documentation for more details on IUnknown methods.
232 ULONG WINAPI
StorageBaseImpl_AddRef(
235 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
236 ULONG ref
= InterlockedIncrement(&This
->ref
);
238 TRACE("(%p) AddRef to %ld\n", This
, ref
);
243 /************************************************************************
244 * Storage32BaseImpl_Release (IUnknown)
246 * This method implements the common Release for all IStorage32
247 * implementations contained in this file.
249 * See Windows documentation for more details on IUnknown methods.
251 ULONG WINAPI
StorageBaseImpl_Release(
254 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
256 * Decrease the reference count on this object.
258 ULONG ref
= InterlockedDecrement(&This
->ref
);
260 TRACE("(%p) ReleaseRef to %ld\n", This
, ref
);
263 * If the reference count goes down to 0, perform suicide.
268 * Since we are using a system of base-classes, we want to call the
269 * destructor of the appropriate derived class. To do this, we are
270 * using virtual functions to implement the destructor.
272 This
->v_destructor(This
);
278 /************************************************************************
279 * Storage32BaseImpl_OpenStream (IStorage)
281 * This method will open the specified stream object from the current storage.
283 * See Windows documentation for more details on IStorage methods.
285 HRESULT WINAPI
StorageBaseImpl_OpenStream(
287 const OLECHAR
* pwcsName
, /* [string][in] */
288 void* reserved1
, /* [unique][in] */
289 DWORD grfMode
, /* [in] */
290 DWORD reserved2
, /* [in] */
291 IStream
** ppstm
) /* [out] */
293 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
294 IEnumSTATSTGImpl
* propertyEnumeration
;
295 StgStreamImpl
* newStream
;
296 StgProperty currentProperty
;
297 ULONG foundPropertyIndex
;
298 HRESULT res
= STG_E_UNKNOWN
;
300 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
301 iface
, debugstr_w(pwcsName
), reserved1
, grfMode
, reserved2
, ppstm
);
304 * Perform a sanity check on the parameters.
306 if ( (pwcsName
==NULL
) || (ppstm
==0) )
313 * Initialize the out parameter
318 * Validate the STGM flags
320 if ( FAILED( validateSTGM(grfMode
) ))
322 res
= STG_E_INVALIDFLAG
;
329 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
330 (grfMode
& STGM_DELETEONRELEASE
) ||
331 (grfMode
& STGM_TRANSACTED
) )
333 res
= STG_E_INVALIDFUNCTION
;
338 * Create a property enumeration to search the properties
340 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
341 This
->ancestorStorage
,
342 This
->rootPropertySetIndex
);
345 * Search the enumeration for the property with the given name
347 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
353 * Delete the property enumeration since we don't need it anymore
355 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
358 * If it was found, construct the stream object and return a pointer to it.
360 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
361 (currentProperty
.propertyType
==PROPTYPE_STREAM
) )
363 newStream
= StgStreamImpl_Construct(This
, grfMode
, foundPropertyIndex
);
367 newStream
->grfMode
= grfMode
;
368 *ppstm
= (IStream
*)newStream
;
371 * Since we are returning a pointer to the interface, we have to
372 * nail down the reference.
374 IStream_AddRef(*ppstm
);
384 res
= STG_E_FILENOTFOUND
;
388 TRACE("<-- IStream %p\n", *ppstm
);
389 TRACE("<-- %08lx\n", res
);
393 /************************************************************************
394 * Storage32BaseImpl_OpenStorage (IStorage)
396 * This method will open a new storage object from the current storage.
398 * See Windows documentation for more details on IStorage methods.
400 HRESULT WINAPI
StorageBaseImpl_OpenStorage(
402 const OLECHAR
* pwcsName
, /* [string][unique][in] */
403 IStorage
* pstgPriority
, /* [unique][in] */
404 DWORD grfMode
, /* [in] */
405 SNB snbExclude
, /* [unique][in] */
406 DWORD reserved
, /* [in] */
407 IStorage
** ppstg
) /* [out] */
409 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
410 StorageInternalImpl
* newStorage
;
411 IEnumSTATSTGImpl
* propertyEnumeration
;
412 StgProperty currentProperty
;
413 ULONG foundPropertyIndex
;
414 HRESULT res
= STG_E_UNKNOWN
;
416 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
417 iface
, debugstr_w(pwcsName
), pstgPriority
,
418 grfMode
, snbExclude
, reserved
, ppstg
);
421 * Perform a sanity check on the parameters.
423 if ( (This
==0) || (pwcsName
==NULL
) || (ppstg
==0) )
430 if (snbExclude
!= NULL
)
432 res
= STG_E_INVALIDPARAMETER
;
437 * Validate the STGM flags
439 if ( FAILED( validateSTGM(grfMode
) ))
441 res
= STG_E_INVALIDFLAG
;
448 if ( STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
||
449 (grfMode
& STGM_DELETEONRELEASE
) ||
450 (grfMode
& STGM_PRIORITY
) )
452 res
= STG_E_INVALIDFUNCTION
;
457 * Initialize the out parameter
462 * Create a property enumeration to search the properties
464 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
465 This
->ancestorStorage
,
466 This
->rootPropertySetIndex
);
469 * Search the enumeration for the property with the given name
471 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(
477 * Delete the property enumeration since we don't need it anymore
479 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
482 * If it was found, construct the stream object and return a pointer to it.
484 if ( (foundPropertyIndex
!=PROPERTY_NULL
) &&
485 (currentProperty
.propertyType
==PROPTYPE_STORAGE
) )
488 * Construct a new Storage object
490 newStorage
= StorageInternalImpl_Construct(
491 This
->ancestorStorage
,
496 *ppstg
= (IStorage
*)newStorage
;
499 * Since we are returning a pointer to the interface,
500 * we have to nail down the reference.
502 StorageBaseImpl_AddRef(*ppstg
);
508 res
= STG_E_INSUFFICIENTMEMORY
;
512 res
= STG_E_FILENOTFOUND
;
515 TRACE("<-- %08lx\n", res
);
519 /************************************************************************
520 * Storage32BaseImpl_EnumElements (IStorage)
522 * This method will create an enumerator object that can be used to
523 * retrieve informatino about all the properties in the storage object.
525 * See Windows documentation for more details on IStorage methods.
527 HRESULT WINAPI
StorageBaseImpl_EnumElements(
529 DWORD reserved1
, /* [in] */
530 void* reserved2
, /* [size_is][unique][in] */
531 DWORD reserved3
, /* [in] */
532 IEnumSTATSTG
** ppenum
) /* [out] */
534 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
535 IEnumSTATSTGImpl
* newEnum
;
537 TRACE("(%p, %ld, %p, %ld, %p)\n",
538 iface
, reserved1
, reserved2
, reserved3
, ppenum
);
541 * Perform a sanity check on the parameters.
543 if ( (This
==0) || (ppenum
==0))
547 * Construct the enumerator.
549 newEnum
= IEnumSTATSTGImpl_Construct(
550 This
->ancestorStorage
,
551 This
->rootPropertySetIndex
);
555 *ppenum
= (IEnumSTATSTG
*)newEnum
;
558 * Don't forget to nail down a reference to the new object before
561 IEnumSTATSTG_AddRef(*ppenum
);
566 return E_OUTOFMEMORY
;
569 /************************************************************************
570 * Storage32BaseImpl_Stat (IStorage)
572 * This method will retrieve information about this storage object.
574 * See Windows documentation for more details on IStorage methods.
576 HRESULT WINAPI
StorageBaseImpl_Stat(
578 STATSTG
* pstatstg
, /* [out] */
579 DWORD grfStatFlag
) /* [in] */
581 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
582 StgProperty curProperty
;
584 HRESULT res
= STG_E_UNKNOWN
;
586 TRACE("(%p, %p, %lx)\n",
587 iface
, pstatstg
, grfStatFlag
);
590 * Perform a sanity check on the parameters.
592 if ( (This
==0) || (pstatstg
==0))
599 * Read the information from the property.
601 readSuccessful
= StorageImpl_ReadProperty(
602 This
->ancestorStorage
,
603 This
->rootPropertySetIndex
,
608 StorageUtl_CopyPropertyToSTATSTG(
622 TRACE("<-- STATSTG: pwcsName: %s, type: %ld, cbSize.Low/High: %ld/%ld, grfMode: %08lx, grfLocksSupported: %ld, grfStateBits: %08lx\n", debugstr_w(pstatstg
->pwcsName
), pstatstg
->type
, pstatstg
->cbSize
.u
.LowPart
, pstatstg
->cbSize
.u
.HighPart
, pstatstg
->grfMode
, pstatstg
->grfLocksSupported
, pstatstg
->grfStateBits
);
624 TRACE("<-- %08lx\n", res
);
628 /************************************************************************
629 * Storage32BaseImpl_RenameElement (IStorage)
631 * This method will rename the specified element.
633 * See Windows documentation for more details on IStorage methods.
635 * Implementation notes: The method used to rename consists of creating a clone
636 * of the deleted StgProperty object setting it with the new name and to
637 * perform a DestroyElement of the old StgProperty.
639 HRESULT WINAPI
StorageBaseImpl_RenameElement(
641 const OLECHAR
* pwcsOldName
, /* [in] */
642 const OLECHAR
* pwcsNewName
) /* [in] */
644 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
645 IEnumSTATSTGImpl
* propertyEnumeration
;
646 StgProperty currentProperty
;
647 ULONG foundPropertyIndex
;
649 TRACE("(%p, %s, %s)\n",
650 iface
, debugstr_w(pwcsOldName
), debugstr_w(pwcsNewName
));
653 * Create a property enumeration to search the properties
655 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
656 This
->rootPropertySetIndex
);
659 * Search the enumeration for the new property name
661 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
665 if (foundPropertyIndex
!= PROPERTY_NULL
)
668 * There is already a property with the new name
670 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
671 return STG_E_FILEALREADYEXISTS
;
674 IEnumSTATSTG_Reset((IEnumSTATSTG
*)propertyEnumeration
);
677 * Search the enumeration for the old property name
679 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
684 * Delete the property enumeration since we don't need it anymore
686 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
688 if (foundPropertyIndex
!= PROPERTY_NULL
)
690 StgProperty renamedProperty
;
691 ULONG renamedPropertyIndex
;
694 * Setup a new property for the renamed property
696 renamedProperty
.sizeOfNameString
=
697 ( lstrlenW(pwcsNewName
)+1 ) * sizeof(WCHAR
);
699 if (renamedProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
700 return STG_E_INVALIDNAME
;
702 strcpyW(renamedProperty
.name
, pwcsNewName
);
704 renamedProperty
.propertyType
= currentProperty
.propertyType
;
705 renamedProperty
.startingBlock
= currentProperty
.startingBlock
;
706 renamedProperty
.size
.u
.LowPart
= currentProperty
.size
.u
.LowPart
;
707 renamedProperty
.size
.u
.HighPart
= currentProperty
.size
.u
.HighPart
;
709 renamedProperty
.previousProperty
= PROPERTY_NULL
;
710 renamedProperty
.nextProperty
= PROPERTY_NULL
;
713 * Bring the dirProperty link in case it is a storage and in which
714 * case the renamed storage elements don't require to be reorganized.
716 renamedProperty
.dirProperty
= currentProperty
.dirProperty
;
718 /* call CoFileTime to get the current time
719 renamedProperty.timeStampS1
720 renamedProperty.timeStampD1
721 renamedProperty.timeStampS2
722 renamedProperty.timeStampD2
723 renamedProperty.propertyUniqueID
727 * Obtain a free property in the property chain
729 renamedPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
732 * Save the new property into the new property spot
734 StorageImpl_WriteProperty(
735 This
->ancestorStorage
,
736 renamedPropertyIndex
,
740 * Find a spot in the property chain for our newly created property.
744 renamedPropertyIndex
,
748 * At this point the renamed property has been inserted in the tree,
749 * now, before to Destroy the old property we must zeroed it's dirProperty
750 * otherwise the DestroyProperty below will zap it all and we do not want
752 * Also, we fake that the old property is a storage so the DestroyProperty
753 * will not do a SetSize(0) on the stream data.
755 * This means that we need to tweek the StgProperty if it is a stream or a
758 StorageImpl_ReadProperty(This
->ancestorStorage
,
762 currentProperty
.dirProperty
= PROPERTY_NULL
;
763 currentProperty
.propertyType
= PROPTYPE_STORAGE
;
764 StorageImpl_WriteProperty(
765 This
->ancestorStorage
,
770 * Invoke Destroy to get rid of the ole property and automatically redo
771 * the linking of it's previous and next members...
773 IStorage_DestroyElement((IStorage
*)This
->ancestorStorage
, pwcsOldName
);
779 * There is no property with the old name
781 return STG_E_FILENOTFOUND
;
787 /************************************************************************
788 * Storage32BaseImpl_CreateStream (IStorage)
790 * This method will create a stream object within this storage
792 * See Windows documentation for more details on IStorage methods.
794 HRESULT WINAPI
StorageBaseImpl_CreateStream(
796 const OLECHAR
* pwcsName
, /* [string][in] */
797 DWORD grfMode
, /* [in] */
798 DWORD reserved1
, /* [in] */
799 DWORD reserved2
, /* [in] */
800 IStream
** ppstm
) /* [out] */
802 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
803 IEnumSTATSTGImpl
* propertyEnumeration
;
804 StgStreamImpl
* newStream
;
805 StgProperty currentProperty
, newStreamProperty
;
806 ULONG foundPropertyIndex
, newPropertyIndex
;
808 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
809 iface
, debugstr_w(pwcsName
), grfMode
,
810 reserved1
, reserved2
, ppstm
);
813 * Validate parameters
816 return STG_E_INVALIDPOINTER
;
819 return STG_E_INVALIDNAME
;
821 if (reserved1
|| reserved2
)
822 return STG_E_INVALIDPARAMETER
;
825 * Validate the STGM flags
827 if ( FAILED( validateSTGM(grfMode
) ))
828 return STG_E_INVALIDFLAG
;
830 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
831 return STG_E_INVALIDFLAG
;
836 if ((grfMode
& STGM_DELETEONRELEASE
) ||
837 (grfMode
& STGM_TRANSACTED
))
838 return STG_E_INVALIDFUNCTION
;
841 * Initialize the out parameter
846 * Create a property enumeration to search the properties
848 propertyEnumeration
= IEnumSTATSTGImpl_Construct(This
->ancestorStorage
,
849 This
->rootPropertySetIndex
);
851 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
855 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
857 if (foundPropertyIndex
!= PROPERTY_NULL
)
860 * An element with this name already exists
862 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
864 IStorage_DestroyElement(iface
, pwcsName
);
867 return STG_E_FILEALREADYEXISTS
;
871 * memset the empty property
873 memset(&newStreamProperty
, 0, sizeof(StgProperty
));
875 newStreamProperty
.sizeOfNameString
=
876 ( lstrlenW(pwcsName
)+1 ) * sizeof(WCHAR
);
878 if (newStreamProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
879 return STG_E_INVALIDNAME
;
881 strcpyW(newStreamProperty
.name
, pwcsName
);
883 newStreamProperty
.propertyType
= PROPTYPE_STREAM
;
884 newStreamProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
885 newStreamProperty
.size
.u
.LowPart
= 0;
886 newStreamProperty
.size
.u
.HighPart
= 0;
888 newStreamProperty
.previousProperty
= PROPERTY_NULL
;
889 newStreamProperty
.nextProperty
= PROPERTY_NULL
;
890 newStreamProperty
.dirProperty
= PROPERTY_NULL
;
892 /* call CoFileTime to get the current time
893 newStreamProperty.timeStampS1
894 newStreamProperty.timeStampD1
895 newStreamProperty.timeStampS2
896 newStreamProperty.timeStampD2
899 /* newStreamProperty.propertyUniqueID */
902 * Get a free property or create a new one
904 newPropertyIndex
= getFreeProperty(This
->ancestorStorage
);
907 * Save the new property into the new property spot
909 StorageImpl_WriteProperty(
910 This
->ancestorStorage
,
915 * Find a spot in the property chain for our newly created property.
923 * Open the stream to return it.
925 newStream
= StgStreamImpl_Construct(This
, grfMode
, newPropertyIndex
);
929 *ppstm
= (IStream
*)newStream
;
932 * Since we are returning a pointer to the interface, we have to nail down
935 IStream_AddRef(*ppstm
);
939 return STG_E_INSUFFICIENTMEMORY
;
945 /************************************************************************
946 * Storage32BaseImpl_SetClass (IStorage)
948 * This method will write the specified CLSID in the property of this
951 * See Windows documentation for more details on IStorage methods.
953 HRESULT WINAPI
StorageBaseImpl_SetClass(
955 REFCLSID clsid
) /* [in] */
957 StorageBaseImpl
*This
= (StorageBaseImpl
*)iface
;
958 HRESULT hRes
= E_FAIL
;
959 StgProperty curProperty
;
962 TRACE("(%p, %p)\n", iface
, clsid
);
964 success
= StorageImpl_ReadProperty(This
->ancestorStorage
,
965 This
->rootPropertySetIndex
,
969 curProperty
.propertyUniqueID
= *clsid
;
971 success
= StorageImpl_WriteProperty(This
->ancestorStorage
,
972 This
->rootPropertySetIndex
,
981 /************************************************************************
982 ** Storage32Impl implementation
985 /************************************************************************
986 * Storage32Impl_CreateStorage (IStorage)
988 * This method will create the storage object within the provided storage.
990 * See Windows documentation for more details on IStorage methods.
992 HRESULT WINAPI
StorageImpl_CreateStorage(
994 const OLECHAR
*pwcsName
, /* [string][in] */
995 DWORD grfMode
, /* [in] */
996 DWORD reserved1
, /* [in] */
997 DWORD reserved2
, /* [in] */
998 IStorage
**ppstg
) /* [out] */
1000 StorageImpl
* const This
=(StorageImpl
*)iface
;
1002 IEnumSTATSTGImpl
*propertyEnumeration
;
1003 StgProperty currentProperty
;
1004 StgProperty newProperty
;
1005 ULONG foundPropertyIndex
;
1006 ULONG newPropertyIndex
;
1009 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1010 iface
, debugstr_w(pwcsName
), grfMode
,
1011 reserved1
, reserved2
, ppstg
);
1014 * Validate parameters
1017 return STG_E_INVALIDPOINTER
;
1020 return STG_E_INVALIDNAME
;
1023 * Validate the STGM flags
1025 if ( FAILED( validateSTGM(grfMode
) ) ||
1026 (grfMode
& STGM_DELETEONRELEASE
) )
1027 return STG_E_INVALIDFLAG
;
1030 * Initialize the out parameter
1035 * Create a property enumeration and search the properties
1037 propertyEnumeration
= IEnumSTATSTGImpl_Construct( This
->base
.ancestorStorage
,
1038 This
->base
.rootPropertySetIndex
);
1040 foundPropertyIndex
= IEnumSTATSTGImpl_FindProperty(propertyEnumeration
,
1043 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1045 if (foundPropertyIndex
!= PROPERTY_NULL
)
1048 * An element with this name already exists
1050 if (STGM_CREATE_MODE(grfMode
) == STGM_CREATE
)
1051 IStorage_DestroyElement(iface
, pwcsName
);
1053 return STG_E_FILEALREADYEXISTS
;
1057 * memset the empty property
1059 memset(&newProperty
, 0, sizeof(StgProperty
));
1061 newProperty
.sizeOfNameString
= (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
);
1063 if (newProperty
.sizeOfNameString
> PROPERTY_NAME_BUFFER_LEN
)
1064 return STG_E_INVALIDNAME
;
1066 strcpyW(newProperty
.name
, pwcsName
);
1068 newProperty
.propertyType
= PROPTYPE_STORAGE
;
1069 newProperty
.startingBlock
= BLOCK_END_OF_CHAIN
;
1070 newProperty
.size
.u
.LowPart
= 0;
1071 newProperty
.size
.u
.HighPart
= 0;
1073 newProperty
.previousProperty
= PROPERTY_NULL
;
1074 newProperty
.nextProperty
= PROPERTY_NULL
;
1075 newProperty
.dirProperty
= PROPERTY_NULL
;
1077 /* call CoFileTime to get the current time
1078 newProperty.timeStampS1
1079 newProperty.timeStampD1
1080 newProperty.timeStampS2
1081 newProperty.timeStampD2
1084 /* newStorageProperty.propertyUniqueID */
1087 * Obtain a free property in the property chain
1089 newPropertyIndex
= getFreeProperty(This
->base
.ancestorStorage
);
1092 * Save the new property into the new property spot
1094 StorageImpl_WriteProperty(
1095 This
->base
.ancestorStorage
,
1100 * Find a spot in the property chain for our newly created property.
1102 updatePropertyChain(
1108 * Open it to get a pointer to return.
1110 hr
= IStorage_OpenStorage(
1112 (const OLECHAR
*)pwcsName
,
1119 if( (hr
!= S_OK
) || (*ppstg
== NULL
))
1129 /***************************************************************************
1133 * Get a free property or create a new one.
1135 static ULONG
getFreeProperty(
1136 StorageImpl
*storage
)
1138 ULONG currentPropertyIndex
= 0;
1139 ULONG newPropertyIndex
= PROPERTY_NULL
;
1140 BOOL readSuccessful
= TRUE
;
1141 StgProperty currentProperty
;
1146 * Start by reading the root property
1148 readSuccessful
= StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1149 currentPropertyIndex
,
1153 if (currentProperty
.sizeOfNameString
== 0)
1156 * The property existis and is available, we found it.
1158 newPropertyIndex
= currentPropertyIndex
;
1164 * We exhausted the property list, we will create more space below
1166 newPropertyIndex
= currentPropertyIndex
;
1168 currentPropertyIndex
++;
1170 } while (newPropertyIndex
== PROPERTY_NULL
);
1173 * grow the property chain
1175 if (! readSuccessful
)
1177 StgProperty emptyProperty
;
1178 ULARGE_INTEGER newSize
;
1179 ULONG propertyIndex
;
1180 ULONG lastProperty
= 0;
1181 ULONG blockCount
= 0;
1184 * obtain the new count of property blocks
1186 blockCount
= BlockChainStream_GetCount(
1187 storage
->base
.ancestorStorage
->rootBlockChain
)+1;
1190 * initialize the size used by the property stream
1192 newSize
.u
.HighPart
= 0;
1193 newSize
.u
.LowPart
= storage
->bigBlockSize
* blockCount
;
1196 * add a property block to the property chain
1198 BlockChainStream_SetSize(storage
->base
.ancestorStorage
->rootBlockChain
, newSize
);
1201 * memset the empty property in order to initialize the unused newly
1204 memset(&emptyProperty
, 0, sizeof(StgProperty
));
1209 lastProperty
= storage
->bigBlockSize
/ PROPSET_BLOCK_SIZE
* blockCount
;
1212 propertyIndex
= newPropertyIndex
;
1213 propertyIndex
< lastProperty
;
1216 StorageImpl_WriteProperty(
1217 storage
->base
.ancestorStorage
,
1223 return newPropertyIndex
;
1226 /****************************************************************************
1230 * Case insensitive comparaison of StgProperty.name by first considering
1233 * Returns <0 when newPrpoerty < currentProperty
1234 * >0 when newPrpoerty > currentProperty
1235 * 0 when newPrpoerty == currentProperty
1237 static LONG
propertyNameCmp(
1238 const OLECHAR
*newProperty
,
1239 const OLECHAR
*currentProperty
)
1241 LONG diff
= lstrlenW(newProperty
) - lstrlenW(currentProperty
);
1246 * We compare the string themselves only when they are of the same length
1248 diff
= lstrcmpiW( newProperty
, currentProperty
);
1254 /****************************************************************************
1258 * Properly link this new element in the property chain.
1260 static void updatePropertyChain(
1261 StorageImpl
*storage
,
1262 ULONG newPropertyIndex
,
1263 StgProperty newProperty
)
1265 StgProperty currentProperty
;
1268 * Read the root property
1270 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1271 storage
->base
.rootPropertySetIndex
,
1274 if (currentProperty
.dirProperty
!= PROPERTY_NULL
)
1277 * The root storage contains some element, therefore, start the research
1278 * for the appropriate location.
1281 ULONG current
, next
, previous
, currentPropertyId
;
1284 * Keep the StgProperty sequence number of the storage first property
1286 currentPropertyId
= currentProperty
.dirProperty
;
1291 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1292 currentProperty
.dirProperty
,
1295 previous
= currentProperty
.previousProperty
;
1296 next
= currentProperty
.nextProperty
;
1297 current
= currentPropertyId
;
1301 LONG diff
= propertyNameCmp( newProperty
.name
, currentProperty
.name
);
1305 if (previous
!= PROPERTY_NULL
)
1307 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1314 currentProperty
.previousProperty
= newPropertyIndex
;
1315 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1323 if (next
!= PROPERTY_NULL
)
1325 StorageImpl_ReadProperty(storage
->base
.ancestorStorage
,
1332 currentProperty
.nextProperty
= newPropertyIndex
;
1333 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1342 * Trying to insert an item with the same name in the
1343 * subtree structure.
1348 previous
= currentProperty
.previousProperty
;
1349 next
= currentProperty
.nextProperty
;
1355 * The root storage is empty, link the new property to it's dir property
1357 currentProperty
.dirProperty
= newPropertyIndex
;
1358 StorageImpl_WriteProperty(storage
->base
.ancestorStorage
,
1359 storage
->base
.rootPropertySetIndex
,
1365 /*************************************************************************
1368 HRESULT WINAPI
StorageImpl_CopyTo(
1370 DWORD ciidExclude
, /* [in] */
1371 const IID
* rgiidExclude
, /* [size_is][unique][in] */
1372 SNB snbExclude
, /* [unique][in] */
1373 IStorage
* pstgDest
) /* [unique][in] */
1375 IEnumSTATSTG
*elements
= 0;
1376 STATSTG curElement
, strStat
;
1378 IStorage
*pstgTmp
, *pstgChild
;
1379 IStream
*pstrTmp
, *pstrChild
;
1381 if ((ciidExclude
!= 0) || (rgiidExclude
!= NULL
) || (snbExclude
!= NULL
))
1382 FIXME("Exclude option not implemented\n");
1384 TRACE("(%p, %ld, %p, %p, %p)\n",
1385 iface
, ciidExclude
, rgiidExclude
,
1386 snbExclude
, pstgDest
);
1389 * Perform a sanity check
1391 if ( pstgDest
== 0 )
1392 return STG_E_INVALIDPOINTER
;
1395 * Enumerate the elements
1397 hr
= IStorage_EnumElements( iface
, 0, 0, 0, &elements
);
1405 IStorage_Stat( iface
, &curElement
, STATFLAG_NONAME
);
1406 IStorage_SetClass( pstgDest
, &curElement
.clsid
);
1411 * Obtain the next element
1413 hr
= IEnumSTATSTG_Next( elements
, 1, &curElement
, NULL
);
1415 if ( hr
== S_FALSE
)
1417 hr
= S_OK
; /* done, every element has been copied */
1421 if (curElement
.type
== STGTY_STORAGE
)
1424 * open child source storage
1426 hr
= IStorage_OpenStorage( iface
, curElement
.pwcsName
, NULL
,
1427 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1428 NULL
, 0, &pstgChild
);
1434 * Check if destination storage is not a child of the source
1435 * storage, which will cause an infinite loop
1437 if (pstgChild
== pstgDest
)
1439 IEnumSTATSTG_Release(elements
);
1441 return STG_E_ACCESSDENIED
;
1445 * create a new storage in destination storage
1447 hr
= IStorage_CreateStorage( pstgDest
, curElement
.pwcsName
,
1448 STGM_FAILIFTHERE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1452 * if it already exist, don't create a new one use this one
1454 if (hr
== STG_E_FILEALREADYEXISTS
)
1456 hr
= IStorage_OpenStorage( pstgDest
, curElement
.pwcsName
, NULL
,
1457 STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1458 NULL
, 0, &pstgTmp
);
1466 * do the copy recursively
1468 hr
= IStorage_CopyTo( pstgChild
, ciidExclude
, rgiidExclude
,
1469 snbExclude
, pstgTmp
);
1471 IStorage_Release( pstgTmp
);
1472 IStorage_Release( pstgChild
);
1474 else if (curElement
.type
== STGTY_STREAM
)
1477 * create a new stream in destination storage. If the stream already
1478 * exist, it will be deleted and a new one will be created.
1480 hr
= IStorage_CreateStream( pstgDest
, curElement
.pwcsName
,
1481 STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
,
1488 * open child stream storage
1490 hr
= IStorage_OpenStream( iface
, curElement
.pwcsName
, NULL
,
1491 STGM_READ
|STGM_SHARE_EXCLUSIVE
,
1498 * Get the size of the source stream
1500 IStream_Stat( pstrChild
, &strStat
, STATFLAG_NONAME
);
1503 * Set the size of the destination stream.
1505 IStream_SetSize(pstrTmp
, strStat
.cbSize
);
1510 hr
= IStream_CopyTo( pstrChild
, pstrTmp
, strStat
.cbSize
,
1513 IStream_Release( pstrTmp
);
1514 IStream_Release( pstrChild
);
1518 WARN("unknown element type: %ld\n", curElement
.type
);
1521 } while (hr
== S_OK
);
1526 IEnumSTATSTG_Release(elements
);
1531 /*************************************************************************
1532 * MoveElementTo (IStorage)
1534 HRESULT WINAPI
StorageImpl_MoveElementTo(
1536 const OLECHAR
*pwcsName
, /* [string][in] */
1537 IStorage
*pstgDest
, /* [unique][in] */
1538 const OLECHAR
*pwcsNewName
,/* [string][in] */
1539 DWORD grfFlags
) /* [in] */
1541 FIXME("not implemented!\n");
1545 /*************************************************************************
1548 HRESULT WINAPI
StorageImpl_Commit(
1550 DWORD grfCommitFlags
)/* [in] */
1552 FIXME("(%ld): stub!\n", grfCommitFlags
);
1556 /*************************************************************************
1559 HRESULT WINAPI
StorageImpl_Revert(
1562 FIXME("not implemented!\n");
1566 /*************************************************************************
1567 * DestroyElement (IStorage)
1569 * Stategy: This implementation is build this way for simplicity not for speed.
1570 * I always delete the top most element of the enumeration and adjust
1571 * the deleted element pointer all the time. This takes longer to
1572 * do but allow to reinvoke DestroyElement whenever we encounter a
1573 * storage object. The optimisation reside in the usage of another
1574 * enumeration stategy that would give all the leaves of a storage
1575 * first. (postfix order)
1577 HRESULT WINAPI
StorageImpl_DestroyElement(
1579 const OLECHAR
*pwcsName
)/* [string][in] */
1581 StorageImpl
* const This
=(StorageImpl
*)iface
;
1583 IEnumSTATSTGImpl
* propertyEnumeration
;
1586 StgProperty propertyToDelete
;
1587 StgProperty parentProperty
;
1588 ULONG foundPropertyIndexToDelete
;
1589 ULONG typeOfRelation
;
1590 ULONG parentPropertyId
;
1593 iface
, debugstr_w(pwcsName
));
1596 * Perform a sanity check on the parameters.
1599 return STG_E_INVALIDPOINTER
;
1602 * Create a property enumeration to search the property with the given name
1604 propertyEnumeration
= IEnumSTATSTGImpl_Construct(
1605 This
->base
.ancestorStorage
,
1606 This
->base
.rootPropertySetIndex
);
1608 foundPropertyIndexToDelete
= IEnumSTATSTGImpl_FindProperty(
1609 propertyEnumeration
,
1613 IEnumSTATSTGImpl_Destroy(propertyEnumeration
);
1615 if ( foundPropertyIndexToDelete
== PROPERTY_NULL
)
1617 return STG_E_FILENOTFOUND
;
1621 * Find the parent property of the property to delete (the one that
1622 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1623 * the parent is This. Otherwise, the parent is one of it's sibling...
1627 * First, read This's StgProperty..
1629 res
= StorageImpl_ReadProperty(
1630 This
->base
.ancestorStorage
,
1631 This
->base
.rootPropertySetIndex
,
1637 * Second, check to see if by any chance the actual storage (This) is not
1638 * the parent of the property to delete... We never know...
1640 if ( parentProperty
.dirProperty
== foundPropertyIndexToDelete
)
1643 * Set data as it would have been done in the else part...
1645 typeOfRelation
= PROPERTY_RELATION_DIR
;
1646 parentPropertyId
= This
->base
.rootPropertySetIndex
;
1651 * Create a property enumeration to search the parent properties, and
1652 * delete it once done.
1654 IEnumSTATSTGImpl
* propertyEnumeration2
;
1656 propertyEnumeration2
= IEnumSTATSTGImpl_Construct(
1657 This
->base
.ancestorStorage
,
1658 This
->base
.rootPropertySetIndex
);
1660 typeOfRelation
= IEnumSTATSTGImpl_FindParentProperty(
1661 propertyEnumeration2
,
1662 foundPropertyIndexToDelete
,
1666 IEnumSTATSTGImpl_Destroy(propertyEnumeration2
);
1669 if ( propertyToDelete
.propertyType
== PROPTYPE_STORAGE
)
1671 hr
= deleteStorageProperty(
1673 foundPropertyIndexToDelete
,
1676 else if ( propertyToDelete
.propertyType
== PROPTYPE_STREAM
)
1678 hr
= deleteStreamProperty(
1680 foundPropertyIndexToDelete
,
1688 * Adjust the property chain
1690 hr
= adjustPropertyChain(
1701 /************************************************************************
1702 * StorageImpl_Stat (IStorage)
1704 * This method will retrieve information about this storage object.
1706 * See Windows documentation for more details on IStorage methods.
1708 HRESULT WINAPI
StorageImpl_Stat( IStorage
* iface
,
1709 STATSTG
* pstatstg
, /* [out] */
1710 DWORD grfStatFlag
) /* [in] */
1712 StorageImpl
* const This
= (StorageImpl
*)iface
;
1713 HRESULT result
= StorageBaseImpl_Stat( iface
, pstatstg
, grfStatFlag
);
1715 if ( !FAILED(result
) && ((grfStatFlag
& STATFLAG_NONAME
) == 0) && This
->pwcsName
)
1717 CoTaskMemFree(pstatstg
->pwcsName
);
1718 pstatstg
->pwcsName
= CoTaskMemAlloc((lstrlenW(This
->pwcsName
)+1)*sizeof(WCHAR
));
1719 strcpyW(pstatstg
->pwcsName
, This
->pwcsName
);
1727 /*********************************************************************
1731 * Perform the deletion of a complete storage node
1734 static HRESULT
deleteStorageProperty(
1735 StorageImpl
*parentStorage
,
1736 ULONG indexOfPropertyToDelete
,
1737 StgProperty propertyToDelete
)
1739 IEnumSTATSTG
*elements
= 0;
1740 IStorage
*childStorage
= 0;
1741 STATSTG currentElement
;
1743 HRESULT destroyHr
= S_OK
;
1746 * Open the storage and enumerate it
1748 hr
= StorageBaseImpl_OpenStorage(
1749 (IStorage
*)parentStorage
,
1750 propertyToDelete
.name
,
1752 STGM_SHARE_EXCLUSIVE
,
1763 * Enumerate the elements
1765 IStorage_EnumElements( childStorage
, 0, 0, 0, &elements
);
1770 * Obtain the next element
1772 hr
= IEnumSTATSTG_Next(elements
, 1, ¤tElement
, NULL
);
1775 destroyHr
= StorageImpl_DestroyElement(
1776 (IStorage
*)childStorage
,
1777 (OLECHAR
*)currentElement
.pwcsName
);
1779 CoTaskMemFree(currentElement
.pwcsName
);
1783 * We need to Reset the enumeration every time because we delete elements
1784 * and the enumeration could be invalid
1786 IEnumSTATSTG_Reset(elements
);
1788 } while ((hr
== S_OK
) && (destroyHr
== S_OK
));
1791 * Invalidate the property by zeroing it's name member.
1793 propertyToDelete
.sizeOfNameString
= 0;
1795 StorageImpl_WriteProperty(parentStorage
->base
.ancestorStorage
,
1796 indexOfPropertyToDelete
,
1799 IStorage_Release(childStorage
);
1800 IEnumSTATSTG_Release(elements
);
1805 /*********************************************************************
1809 * Perform the deletion of a stream node
1812 static HRESULT
deleteStreamProperty(
1813 StorageImpl
*parentStorage
,
1814 ULONG indexOfPropertyToDelete
,
1815 StgProperty propertyToDelete
)
1819 ULARGE_INTEGER size
;
1821 size
.u
.HighPart
= 0;
1824 hr
= StorageBaseImpl_OpenStream(
1825 (IStorage
*)parentStorage
,
1826 (OLECHAR
*)propertyToDelete
.name
,
1828 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
,
1840 hr
= IStream_SetSize(pis
, size
);
1848 * Release the stream object.
1850 IStream_Release(pis
);
1853 * Invalidate the property by zeroing it's name member.
1855 propertyToDelete
.sizeOfNameString
= 0;
1858 * Here we should re-read the property so we get the updated pointer
1859 * but since we are here to zap it, I don't do it...
1861 StorageImpl_WriteProperty(
1862 parentStorage
->base
.ancestorStorage
,
1863 indexOfPropertyToDelete
,
1869 /*********************************************************************
1873 * Finds a placeholder for the StgProperty within the Storage
1876 static HRESULT
findPlaceholder(
1877 StorageImpl
*storage
,
1878 ULONG propertyIndexToStore
,
1879 ULONG storePropertyIndex
,
1882 StgProperty storeProperty
;
1887 * Read the storage property
1889 res
= StorageImpl_ReadProperty(
1890 storage
->base
.ancestorStorage
,
1899 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1901 if (storeProperty
.previousProperty
!= PROPERTY_NULL
)
1903 return findPlaceholder(
1905 propertyIndexToStore
,
1906 storeProperty
.previousProperty
,
1911 storeProperty
.previousProperty
= propertyIndexToStore
;
1914 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
1916 if (storeProperty
.nextProperty
!= PROPERTY_NULL
)
1918 return findPlaceholder(
1920 propertyIndexToStore
,
1921 storeProperty
.nextProperty
,
1926 storeProperty
.nextProperty
= propertyIndexToStore
;
1929 else if (typeOfRelation
== PROPERTY_RELATION_DIR
)
1931 if (storeProperty
.dirProperty
!= PROPERTY_NULL
)
1933 return findPlaceholder(
1935 propertyIndexToStore
,
1936 storeProperty
.dirProperty
,
1941 storeProperty
.dirProperty
= propertyIndexToStore
;
1945 hr
= StorageImpl_WriteProperty(
1946 storage
->base
.ancestorStorage
,
1958 /*************************************************************************
1962 * This method takes the previous and the next property link of a property
1963 * to be deleted and find them a place in the Storage.
1965 static HRESULT
adjustPropertyChain(
1967 StgProperty propertyToDelete
,
1968 StgProperty parentProperty
,
1969 ULONG parentPropertyId
,
1972 ULONG newLinkProperty
= PROPERTY_NULL
;
1973 BOOL needToFindAPlaceholder
= FALSE
;
1974 ULONG storeNode
= PROPERTY_NULL
;
1975 ULONG toStoreNode
= PROPERTY_NULL
;
1976 INT relationType
= 0;
1980 if (typeOfRelation
== PROPERTY_RELATION_PREVIOUS
)
1982 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
1985 * Set the parent previous to the property to delete previous
1987 newLinkProperty
= propertyToDelete
.previousProperty
;
1989 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
1992 * We also need to find a storage for the other link, setup variables
1993 * to do this at the end...
1995 needToFindAPlaceholder
= TRUE
;
1996 storeNode
= propertyToDelete
.previousProperty
;
1997 toStoreNode
= propertyToDelete
.nextProperty
;
1998 relationType
= PROPERTY_RELATION_NEXT
;
2001 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2004 * Set the parent previous to the property to delete next
2006 newLinkProperty
= propertyToDelete
.nextProperty
;
2010 * Link it for real...
2012 parentProperty
.previousProperty
= newLinkProperty
;
2015 else if (typeOfRelation
== PROPERTY_RELATION_NEXT
)
2017 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2020 * Set the parent next to the property to delete next previous
2022 newLinkProperty
= propertyToDelete
.previousProperty
;
2024 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2027 * We also need to find a storage for the other link, setup variables
2028 * to do this at the end...
2030 needToFindAPlaceholder
= TRUE
;
2031 storeNode
= propertyToDelete
.previousProperty
;
2032 toStoreNode
= propertyToDelete
.nextProperty
;
2033 relationType
= PROPERTY_RELATION_NEXT
;
2036 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2039 * Set the parent next to the property to delete next
2041 newLinkProperty
= propertyToDelete
.nextProperty
;
2045 * Link it for real...
2047 parentProperty
.nextProperty
= newLinkProperty
;
2049 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2051 if (propertyToDelete
.previousProperty
!= PROPERTY_NULL
)
2054 * Set the parent dir to the property to delete previous
2056 newLinkProperty
= propertyToDelete
.previousProperty
;
2058 if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2061 * We also need to find a storage for the other link, setup variables
2062 * to do this at the end...
2064 needToFindAPlaceholder
= TRUE
;
2065 storeNode
= propertyToDelete
.previousProperty
;
2066 toStoreNode
= propertyToDelete
.nextProperty
;
2067 relationType
= PROPERTY_RELATION_NEXT
;
2070 else if (propertyToDelete
.nextProperty
!= PROPERTY_NULL
)
2073 * Set the parent dir to the property to delete next
2075 newLinkProperty
= propertyToDelete
.nextProperty
;
2079 * Link it for real...
2081 parentProperty
.dirProperty
= newLinkProperty
;
2085 * Write back the parent property
2087 res
= StorageImpl_WriteProperty(
2088 This
->base
.ancestorStorage
,
2097 * If a placeholder is required for the other link, then, find one and
2098 * get out of here...
2100 if (needToFindAPlaceholder
)
2102 hr
= findPlaceholder(
2113 /******************************************************************************
2114 * SetElementTimes (IStorage)
2116 HRESULT WINAPI
StorageImpl_SetElementTimes(
2118 const OLECHAR
*pwcsName
,/* [string][in] */
2119 const FILETIME
*pctime
, /* [in] */
2120 const FILETIME
*patime
, /* [in] */
2121 const FILETIME
*pmtime
) /* [in] */
2123 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName
));
2127 /******************************************************************************
2128 * SetStateBits (IStorage)
2130 HRESULT WINAPI
StorageImpl_SetStateBits(
2132 DWORD grfStateBits
,/* [in] */
2133 DWORD grfMask
) /* [in] */
2135 FIXME("not implemented!\n");
2140 * Virtual function table for the IStorage32Impl class.
2142 static IStorageVtbl Storage32Impl_Vtbl
=
2144 StorageBaseImpl_QueryInterface
,
2145 StorageBaseImpl_AddRef
,
2146 StorageBaseImpl_Release
,
2147 StorageBaseImpl_CreateStream
,
2148 StorageBaseImpl_OpenStream
,
2149 StorageImpl_CreateStorage
,
2150 StorageBaseImpl_OpenStorage
,
2152 StorageImpl_MoveElementTo
,
2155 StorageBaseImpl_EnumElements
,
2156 StorageImpl_DestroyElement
,
2157 StorageBaseImpl_RenameElement
,
2158 StorageImpl_SetElementTimes
,
2159 StorageBaseImpl_SetClass
,
2160 StorageImpl_SetStateBits
,
2164 HRESULT
StorageImpl_Construct(
2174 StgProperty currentProperty
;
2175 BOOL readSuccessful
;
2176 ULONG currentPropertyIndex
;
2178 if ( FAILED( validateSTGM(openFlags
) ))
2179 return STG_E_INVALIDFLAG
;
2181 memset(This
, 0, sizeof(StorageImpl
));
2184 * Initialize the virtual function table.
2186 This
->base
.lpVtbl
= &Storage32Impl_Vtbl
;
2187 This
->base
.pssVtbl
= &IPropertySetStorage_Vtbl
;
2188 This
->base
.v_destructor
= &StorageImpl_Destroy
;
2191 * This is the top-level storage so initialize the ancestor pointer
2194 This
->base
.ancestorStorage
= This
;
2197 * Initialize the physical support of the storage.
2199 This
->hFile
= hFile
;
2202 * Store copy of file path.
2205 This
->pwcsName
= HeapAlloc(GetProcessHeap(), 0,
2206 (lstrlenW(pwcsName
)+1)*sizeof(WCHAR
));
2207 if (!This
->pwcsName
)
2208 return STG_E_INSUFFICIENTMEMORY
;
2209 strcpyW(This
->pwcsName
, pwcsName
);
2213 * Initialize the big block cache.
2215 This
->bigBlockSize
= DEF_BIG_BLOCK_SIZE
;
2216 This
->smallBlockSize
= DEF_SMALL_BLOCK_SIZE
;
2217 This
->bigBlockFile
= BIGBLOCKFILE_Construct(hFile
,
2223 if (This
->bigBlockFile
== 0)
2228 ULARGE_INTEGER size
;
2229 BYTE
* bigBlockBuffer
;
2232 * Initialize all header variables:
2233 * - The big block depot consists of one block and it is at block 0
2234 * - The properties start at block 1
2235 * - There is no small block depot
2237 memset( This
->bigBlockDepotStart
,
2239 sizeof(This
->bigBlockDepotStart
));
2241 This
->bigBlockDepotCount
= 1;
2242 This
->bigBlockDepotStart
[0] = 0;
2243 This
->rootStartBlock
= 1;
2244 This
->smallBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2245 This
->bigBlockSizeBits
= DEF_BIG_BLOCK_SIZE_BITS
;
2246 This
->smallBlockSizeBits
= DEF_SMALL_BLOCK_SIZE_BITS
;
2247 This
->extBigBlockDepotStart
= BLOCK_END_OF_CHAIN
;
2248 This
->extBigBlockDepotCount
= 0;
2250 StorageImpl_SaveFileHeader(This
);
2253 * Add one block for the big block depot and one block for the properties
2255 size
.u
.HighPart
= 0;
2256 size
.u
.LowPart
= This
->bigBlockSize
* 3;
2257 BIGBLOCKFILE_SetSize(This
->bigBlockFile
, size
);
2260 * Initialize the big block depot
2262 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, 0);
2263 memset(bigBlockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2264 StorageUtl_WriteDWord(bigBlockBuffer
, 0, BLOCK_SPECIAL
);
2265 StorageUtl_WriteDWord(bigBlockBuffer
, sizeof(ULONG
), BLOCK_END_OF_CHAIN
);
2266 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
2271 * Load the header for the file.
2273 hr
= StorageImpl_LoadFileHeader(This
);
2277 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2284 * There is no block depot cached yet.
2286 This
->indexBlockDepotCached
= 0xFFFFFFFF;
2289 * Start searching for free blocks with block 0.
2291 This
->prevFreeBlock
= 0;
2294 * Create the block chain abstractions.
2296 if(!(This
->rootBlockChain
=
2297 BlockChainStream_Construct(This
, &This
->rootStartBlock
, PROPERTY_NULL
)))
2298 return STG_E_READFAULT
;
2300 if(!(This
->smallBlockDepotChain
=
2301 BlockChainStream_Construct(This
, &This
->smallBlockDepotStart
,
2303 return STG_E_READFAULT
;
2306 * Write the root property
2310 StgProperty rootProp
;
2312 * Initialize the property chain
2314 memset(&rootProp
, 0, sizeof(rootProp
));
2315 MultiByteToWideChar( CP_ACP
, 0, rootPropertyName
, -1, rootProp
.name
,
2316 sizeof(rootProp
.name
)/sizeof(WCHAR
) );
2317 rootProp
.sizeOfNameString
= (strlenW(rootProp
.name
)+1) * sizeof(WCHAR
);
2318 rootProp
.propertyType
= PROPTYPE_ROOT
;
2319 rootProp
.previousProperty
= PROPERTY_NULL
;
2320 rootProp
.nextProperty
= PROPERTY_NULL
;
2321 rootProp
.dirProperty
= PROPERTY_NULL
;
2322 rootProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
2323 rootProp
.size
.u
.HighPart
= 0;
2324 rootProp
.size
.u
.LowPart
= 0;
2326 StorageImpl_WriteProperty(This
, 0, &rootProp
);
2330 * Find the ID of the root in the property sets.
2332 currentPropertyIndex
= 0;
2336 readSuccessful
= StorageImpl_ReadProperty(
2338 currentPropertyIndex
,
2343 if ( (currentProperty
.sizeOfNameString
!= 0 ) &&
2344 (currentProperty
.propertyType
== PROPTYPE_ROOT
) )
2346 This
->base
.rootPropertySetIndex
= currentPropertyIndex
;
2350 currentPropertyIndex
++;
2352 } while (readSuccessful
&& (This
->base
.rootPropertySetIndex
== PROPERTY_NULL
) );
2354 if (!readSuccessful
)
2357 return STG_E_READFAULT
;
2361 * Create the block chain abstraction for the small block root chain.
2363 if(!(This
->smallBlockRootChain
=
2364 BlockChainStream_Construct(This
, NULL
, This
->base
.rootPropertySetIndex
)))
2365 return STG_E_READFAULT
;
2370 void StorageImpl_Destroy(StorageBaseImpl
* iface
)
2372 StorageImpl
*This
= (StorageImpl
*) iface
;
2373 TRACE("(%p)\n", This
);
2375 HeapFree(GetProcessHeap(), 0, This
->pwcsName
);
2377 BlockChainStream_Destroy(This
->smallBlockRootChain
);
2378 BlockChainStream_Destroy(This
->rootBlockChain
);
2379 BlockChainStream_Destroy(This
->smallBlockDepotChain
);
2381 BIGBLOCKFILE_Destructor(This
->bigBlockFile
);
2385 /******************************************************************************
2386 * Storage32Impl_GetNextFreeBigBlock
2388 * Returns the index of the next free big block.
2389 * If the big block depot is filled, this method will enlarge it.
2392 ULONG
StorageImpl_GetNextFreeBigBlock(
2395 ULONG depotBlockIndexPos
;
2397 ULONG depotBlockOffset
;
2398 ULONG blocksPerDepot
= This
->bigBlockSize
/ sizeof(ULONG
);
2399 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2401 ULONG freeBlock
= BLOCK_UNUSED
;
2403 depotIndex
= This
->prevFreeBlock
/ blocksPerDepot
;
2404 depotBlockOffset
= (This
->prevFreeBlock
% blocksPerDepot
) * sizeof(ULONG
);
2407 * Scan the entire big block depot until we find a block marked free
2409 while (nextBlockIndex
!= BLOCK_UNUSED
)
2411 if (depotIndex
< COUNT_BBDEPOTINHEADER
)
2413 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotIndex
];
2416 * Grow the primary depot.
2418 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2420 depotBlockIndexPos
= depotIndex
*blocksPerDepot
;
2423 * Add a block depot.
2425 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2426 This
->bigBlockDepotCount
++;
2427 This
->bigBlockDepotStart
[depotIndex
] = depotBlockIndexPos
;
2430 * Flag it as a block depot.
2432 StorageImpl_SetNextBlockInChain(This
,
2436 /* Save new header information.
2438 StorageImpl_SaveFileHeader(This
);
2443 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotIndex
);
2445 if (depotBlockIndexPos
== BLOCK_UNUSED
)
2448 * Grow the extended depot.
2450 ULONG extIndex
= BLOCK_UNUSED
;
2451 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2452 ULONG extBlockOffset
= numExtBlocks
% (blocksPerDepot
- 1);
2454 if (extBlockOffset
== 0)
2456 /* We need an extended block.
2458 extIndex
= Storage32Impl_AddExtBlockDepot(This
);
2459 This
->extBigBlockDepotCount
++;
2460 depotBlockIndexPos
= extIndex
+ 1;
2463 depotBlockIndexPos
= depotIndex
* blocksPerDepot
;
2466 * Add a block depot and mark it in the extended block.
2468 Storage32Impl_AddBlockDepot(This
, depotBlockIndexPos
);
2469 This
->bigBlockDepotCount
++;
2470 Storage32Impl_SetExtDepotBlock(This
, depotIndex
, depotBlockIndexPos
);
2472 /* Flag the block depot.
2474 StorageImpl_SetNextBlockInChain(This
,
2478 /* If necessary, flag the extended depot block.
2480 if (extIndex
!= BLOCK_UNUSED
)
2481 StorageImpl_SetNextBlockInChain(This
, extIndex
, BLOCK_EXTBBDEPOT
);
2483 /* Save header information.
2485 StorageImpl_SaveFileHeader(This
);
2489 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2491 if (depotBuffer
!= 0)
2493 while ( ( (depotBlockOffset
/sizeof(ULONG
) ) < blocksPerDepot
) &&
2494 ( nextBlockIndex
!= BLOCK_UNUSED
))
2496 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2498 if (nextBlockIndex
== BLOCK_UNUSED
)
2500 freeBlock
= (depotIndex
* blocksPerDepot
) +
2501 (depotBlockOffset
/sizeof(ULONG
));
2504 depotBlockOffset
+= sizeof(ULONG
);
2507 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2511 depotBlockOffset
= 0;
2514 This
->prevFreeBlock
= freeBlock
;
2519 /******************************************************************************
2520 * Storage32Impl_AddBlockDepot
2522 * This will create a depot block, essentially it is a block initialized
2525 void Storage32Impl_AddBlockDepot(StorageImpl
* This
, ULONG blockIndex
)
2529 blockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
2532 * Initialize blocks as free
2534 memset(blockBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2536 StorageImpl_ReleaseBigBlock(This
, blockBuffer
);
2539 /******************************************************************************
2540 * Storage32Impl_GetExtDepotBlock
2542 * Returns the index of the block that corresponds to the specified depot
2543 * index. This method is only for depot indexes equal or greater than
2544 * COUNT_BBDEPOTINHEADER.
2546 ULONG
Storage32Impl_GetExtDepotBlock(StorageImpl
* This
, ULONG depotIndex
)
2548 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2549 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2550 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2551 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2552 ULONG blockIndex
= BLOCK_UNUSED
;
2553 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2555 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2557 if (This
->extBigBlockDepotStart
== BLOCK_END_OF_CHAIN
)
2558 return BLOCK_UNUSED
;
2560 while (extBlockCount
> 0)
2562 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2566 if (extBlockIndex
!= BLOCK_UNUSED
)
2570 depotBuffer
= StorageImpl_GetROBigBlock(This
, extBlockIndex
);
2572 if (depotBuffer
!= 0)
2574 StorageUtl_ReadDWord(depotBuffer
,
2575 extBlockOffset
* sizeof(ULONG
),
2578 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2585 /******************************************************************************
2586 * Storage32Impl_SetExtDepotBlock
2588 * Associates the specified block index to the specified depot index.
2589 * This method is only for depot indexes equal or greater than
2590 * COUNT_BBDEPOTINHEADER.
2592 void Storage32Impl_SetExtDepotBlock(StorageImpl
* This
,
2596 ULONG depotBlocksPerExtBlock
= (This
->bigBlockSize
/ sizeof(ULONG
)) - 1;
2597 ULONG numExtBlocks
= depotIndex
- COUNT_BBDEPOTINHEADER
;
2598 ULONG extBlockCount
= numExtBlocks
/ depotBlocksPerExtBlock
;
2599 ULONG extBlockOffset
= numExtBlocks
% depotBlocksPerExtBlock
;
2600 ULONG extBlockIndex
= This
->extBigBlockDepotStart
;
2602 assert(depotIndex
>= COUNT_BBDEPOTINHEADER
);
2604 while (extBlockCount
> 0)
2606 extBlockIndex
= Storage32Impl_GetNextExtendedBlock(This
, extBlockIndex
);
2610 if (extBlockIndex
!= BLOCK_UNUSED
)
2614 depotBuffer
= StorageImpl_GetBigBlock(This
, extBlockIndex
);
2616 if (depotBuffer
!= 0)
2618 StorageUtl_WriteDWord(depotBuffer
,
2619 extBlockOffset
* sizeof(ULONG
),
2622 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2627 /******************************************************************************
2628 * Storage32Impl_AddExtBlockDepot
2630 * Creates an extended depot block.
2632 ULONG
Storage32Impl_AddExtBlockDepot(StorageImpl
* This
)
2634 ULONG numExtBlocks
= This
->extBigBlockDepotCount
;
2635 ULONG nextExtBlock
= This
->extBigBlockDepotStart
;
2636 BYTE
* depotBuffer
= NULL
;
2637 ULONG index
= BLOCK_UNUSED
;
2638 ULONG nextBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2639 ULONG blocksPerDepotBlock
= This
->bigBlockSize
/ sizeof(ULONG
);
2640 ULONG depotBlocksPerExtBlock
= blocksPerDepotBlock
- 1;
2642 index
= (COUNT_BBDEPOTINHEADER
+ (numExtBlocks
* depotBlocksPerExtBlock
)) *
2643 blocksPerDepotBlock
;
2645 if ((numExtBlocks
== 0) && (nextExtBlock
== BLOCK_END_OF_CHAIN
))
2648 * The first extended block.
2650 This
->extBigBlockDepotStart
= index
;
2656 * Follow the chain to the last one.
2658 for (i
= 0; i
< (numExtBlocks
- 1); i
++)
2660 nextExtBlock
= Storage32Impl_GetNextExtendedBlock(This
, nextExtBlock
);
2664 * Add the new extended block to the chain.
2666 depotBuffer
= StorageImpl_GetBigBlock(This
, nextExtBlock
);
2667 StorageUtl_WriteDWord(depotBuffer
, nextBlockOffset
, index
);
2668 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2672 * Initialize this block.
2674 depotBuffer
= StorageImpl_GetBigBlock(This
, index
);
2675 memset(depotBuffer
, BLOCK_UNUSED
, This
->bigBlockSize
);
2676 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2681 /******************************************************************************
2682 * Storage32Impl_FreeBigBlock
2684 * This method will flag the specified block as free in the big block depot.
2686 void StorageImpl_FreeBigBlock(
2690 StorageImpl_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
2692 if (blockIndex
< This
->prevFreeBlock
)
2693 This
->prevFreeBlock
= blockIndex
;
2696 /************************************************************************
2697 * Storage32Impl_GetNextBlockInChain
2699 * This method will retrieve the block index of the next big block in
2702 * Params: This - Pointer to the Storage object.
2703 * blockIndex - Index of the block to retrieve the chain
2705 * nextBlockIndex - receives the return value.
2707 * Returns: This method returns the index of the next block in the chain.
2708 * It will return the constants:
2709 * BLOCK_SPECIAL - If the block given was not part of a
2711 * BLOCK_END_OF_CHAIN - If the block given was the last in
2713 * BLOCK_UNUSED - If the block given was not past of a chain
2715 * BLOCK_EXTBBDEPOT - This block is part of the extended
2718 * See Windows documentation for more details on IStorage methods.
2720 HRESULT
StorageImpl_GetNextBlockInChain(
2723 ULONG
* nextBlockIndex
)
2725 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2726 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2727 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2729 ULONG depotBlockIndexPos
;
2732 *nextBlockIndex
= BLOCK_SPECIAL
;
2734 if(depotBlockCount
>= This
->bigBlockDepotCount
)
2736 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount
,
2737 This
->bigBlockDepotCount
);
2738 return STG_E_READFAULT
;
2742 * Cache the currently accessed depot block.
2744 if (depotBlockCount
!= This
->indexBlockDepotCached
)
2746 This
->indexBlockDepotCached
= depotBlockCount
;
2748 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2750 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2755 * We have to look in the extended depot.
2757 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2760 depotBuffer
= StorageImpl_GetROBigBlock(This
, depotBlockIndexPos
);
2763 return STG_E_READFAULT
;
2765 for (index
= 0; index
< NUM_BLOCKS_PER_DEPOT_BLOCK
; index
++)
2767 StorageUtl_ReadDWord(depotBuffer
, index
*sizeof(ULONG
), nextBlockIndex
);
2768 This
->blockDepotCached
[index
] = *nextBlockIndex
;
2770 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2773 *nextBlockIndex
= This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)];
2778 /******************************************************************************
2779 * Storage32Impl_GetNextExtendedBlock
2781 * Given an extended block this method will return the next extended block.
2784 * The last ULONG of an extended block is the block index of the next
2785 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2789 * - The index of the next extended block
2790 * - BLOCK_UNUSED: there is no next extended block.
2791 * - Any other return values denotes failure.
2793 ULONG
Storage32Impl_GetNextExtendedBlock(StorageImpl
* This
, ULONG blockIndex
)
2795 ULONG nextBlockIndex
= BLOCK_SPECIAL
;
2796 ULONG depotBlockOffset
= This
->bigBlockSize
- sizeof(ULONG
);
2799 depotBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
2803 StorageUtl_ReadDWord(depotBuffer
, depotBlockOffset
, &nextBlockIndex
);
2805 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2808 return nextBlockIndex
;
2811 /******************************************************************************
2812 * Storage32Impl_SetNextBlockInChain
2814 * This method will write the index of the specified block's next block
2815 * in the big block depot.
2817 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2820 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2821 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2822 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2825 void StorageImpl_SetNextBlockInChain(
2830 ULONG offsetInDepot
= blockIndex
* sizeof (ULONG
);
2831 ULONG depotBlockCount
= offsetInDepot
/ This
->bigBlockSize
;
2832 ULONG depotBlockOffset
= offsetInDepot
% This
->bigBlockSize
;
2833 ULONG depotBlockIndexPos
;
2836 assert(depotBlockCount
< This
->bigBlockDepotCount
);
2837 assert(blockIndex
!= nextBlock
);
2839 if (depotBlockCount
< COUNT_BBDEPOTINHEADER
)
2841 depotBlockIndexPos
= This
->bigBlockDepotStart
[depotBlockCount
];
2846 * We have to look in the extended depot.
2848 depotBlockIndexPos
= Storage32Impl_GetExtDepotBlock(This
, depotBlockCount
);
2851 depotBuffer
= StorageImpl_GetBigBlock(This
, depotBlockIndexPos
);
2855 StorageUtl_WriteDWord(depotBuffer
, depotBlockOffset
, nextBlock
);
2856 StorageImpl_ReleaseBigBlock(This
, depotBuffer
);
2860 * Update the cached block depot, if necessary.
2862 if (depotBlockCount
== This
->indexBlockDepotCached
)
2864 This
->blockDepotCached
[depotBlockOffset
/sizeof(ULONG
)] = nextBlock
;
2868 /******************************************************************************
2869 * Storage32Impl_LoadFileHeader
2871 * This method will read in the file header, i.e. big block index -1.
2873 HRESULT
StorageImpl_LoadFileHeader(
2876 HRESULT hr
= STG_E_FILENOTFOUND
;
2877 void* headerBigBlock
= NULL
;
2881 * Get a pointer to the big block of data containing the header.
2883 headerBigBlock
= StorageImpl_GetROBigBlock(This
, -1);
2886 * Extract the information from the header.
2888 if (headerBigBlock
!=0)
2891 * Check for the "magic number" signature and return an error if it is not
2894 if (memcmp(headerBigBlock
, STORAGE_oldmagic
, sizeof(STORAGE_oldmagic
))==0)
2896 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2897 return STG_E_OLDFORMAT
;
2900 if (memcmp(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
))!=0)
2902 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2903 return STG_E_INVALIDHEADER
;
2906 StorageUtl_ReadWord(
2908 OFFSET_BIGBLOCKSIZEBITS
,
2909 &This
->bigBlockSizeBits
);
2911 StorageUtl_ReadWord(
2913 OFFSET_SMALLBLOCKSIZEBITS
,
2914 &This
->smallBlockSizeBits
);
2916 StorageUtl_ReadDWord(
2918 OFFSET_BBDEPOTCOUNT
,
2919 &This
->bigBlockDepotCount
);
2921 StorageUtl_ReadDWord(
2923 OFFSET_ROOTSTARTBLOCK
,
2924 &This
->rootStartBlock
);
2926 StorageUtl_ReadDWord(
2928 OFFSET_SBDEPOTSTART
,
2929 &This
->smallBlockDepotStart
);
2931 StorageUtl_ReadDWord(
2933 OFFSET_EXTBBDEPOTSTART
,
2934 &This
->extBigBlockDepotStart
);
2936 StorageUtl_ReadDWord(
2938 OFFSET_EXTBBDEPOTCOUNT
,
2939 &This
->extBigBlockDepotCount
);
2941 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
2943 StorageUtl_ReadDWord(
2945 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
2946 &(This
->bigBlockDepotStart
[index
]));
2950 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2954 This
->bigBlockSize
= 0x000000001 << (DWORD
)This
->bigBlockSizeBits
;
2955 This
->smallBlockSize
= 0x000000001 << (DWORD
)This
->smallBlockSizeBits
;
2959 This
->bigBlockSize
= 0x000000001 >> (DWORD
)This
->bigBlockSizeBits
;
2960 This
->smallBlockSize
= 0x000000001 >> (DWORD
)This
->smallBlockSizeBits
;
2964 * Right now, the code is making some assumptions about the size of the
2965 * blocks, just make sure they are what we're expecting.
2967 if (This
->bigBlockSize
!= DEF_BIG_BLOCK_SIZE
||
2968 This
->smallBlockSize
!= DEF_SMALL_BLOCK_SIZE
)
2970 WARN("Broken OLE storage file\n");
2971 hr
= STG_E_INVALIDHEADER
;
2977 * Release the block.
2979 StorageImpl_ReleaseBigBlock(This
, headerBigBlock
);
2985 /******************************************************************************
2986 * Storage32Impl_SaveFileHeader
2988 * This method will save to the file the header, i.e. big block -1.
2990 void StorageImpl_SaveFileHeader(
2993 BYTE headerBigBlock
[BIG_BLOCK_SIZE
];
2998 * Get a pointer to the big block of data containing the header.
3000 success
= StorageImpl_ReadBigBlock(This
, -1, headerBigBlock
);
3003 * If the block read failed, the file is probably new.
3008 * Initialize for all unknown fields.
3010 memset(headerBigBlock
, 0, BIG_BLOCK_SIZE
);
3013 * Initialize the magic number.
3015 memcpy(headerBigBlock
, STORAGE_magic
, sizeof(STORAGE_magic
));
3018 * And a bunch of things we don't know what they mean
3020 StorageUtl_WriteWord(headerBigBlock
, 0x18, 0x3b);
3021 StorageUtl_WriteWord(headerBigBlock
, 0x1a, 0x3);
3022 StorageUtl_WriteWord(headerBigBlock
, 0x1c, (WORD
)-2);
3023 StorageUtl_WriteDWord(headerBigBlock
, 0x38, (DWORD
)0x1000);
3027 * Write the information to the header.
3029 StorageUtl_WriteWord(
3031 OFFSET_BIGBLOCKSIZEBITS
,
3032 This
->bigBlockSizeBits
);
3034 StorageUtl_WriteWord(
3036 OFFSET_SMALLBLOCKSIZEBITS
,
3037 This
->smallBlockSizeBits
);
3039 StorageUtl_WriteDWord(
3041 OFFSET_BBDEPOTCOUNT
,
3042 This
->bigBlockDepotCount
);
3044 StorageUtl_WriteDWord(
3046 OFFSET_ROOTSTARTBLOCK
,
3047 This
->rootStartBlock
);
3049 StorageUtl_WriteDWord(
3051 OFFSET_SBDEPOTSTART
,
3052 This
->smallBlockDepotStart
);
3054 StorageUtl_WriteDWord(
3056 OFFSET_SBDEPOTCOUNT
,
3057 This
->smallBlockDepotChain
?
3058 BlockChainStream_GetCount(This
->smallBlockDepotChain
) : 0);
3060 StorageUtl_WriteDWord(
3062 OFFSET_EXTBBDEPOTSTART
,
3063 This
->extBigBlockDepotStart
);
3065 StorageUtl_WriteDWord(
3067 OFFSET_EXTBBDEPOTCOUNT
,
3068 This
->extBigBlockDepotCount
);
3070 for (index
= 0; index
< COUNT_BBDEPOTINHEADER
; index
++)
3072 StorageUtl_WriteDWord(
3074 OFFSET_BBDEPOTSTART
+ (sizeof(ULONG
)*index
),
3075 (This
->bigBlockDepotStart
[index
]));
3079 * Write the big block back to the file.
3081 StorageImpl_WriteBigBlock(This
, -1, headerBigBlock
);
3084 /******************************************************************************
3085 * Storage32Impl_ReadProperty
3087 * This method will read the specified property from the property chain.
3089 BOOL
StorageImpl_ReadProperty(
3092 StgProperty
* buffer
)
3094 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3095 ULARGE_INTEGER offsetInPropSet
;
3096 BOOL readSuccessful
;
3099 offsetInPropSet
.u
.HighPart
= 0;
3100 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3102 readSuccessful
= BlockChainStream_ReadAt(
3103 This
->rootBlockChain
,
3111 /* replace the name of root entry (often "Root Entry") by the file name */
3112 WCHAR
*propName
= (index
== This
->base
.rootPropertySetIndex
) ?
3113 This
->filename
: (WCHAR
*)currentProperty
+OFFSET_PS_NAME
;
3115 memset(buffer
->name
, 0, sizeof(buffer
->name
));
3119 PROPERTY_NAME_BUFFER_LEN
);
3120 TRACE("storage name: %s\n", debugstr_w(buffer
->name
));
3122 memcpy(&buffer
->propertyType
, currentProperty
+ OFFSET_PS_PROPERTYTYPE
, 1);
3124 StorageUtl_ReadWord(
3126 OFFSET_PS_NAMELENGTH
,
3127 &buffer
->sizeOfNameString
);
3129 StorageUtl_ReadDWord(
3131 OFFSET_PS_PREVIOUSPROP
,
3132 &buffer
->previousProperty
);
3134 StorageUtl_ReadDWord(
3137 &buffer
->nextProperty
);
3139 StorageUtl_ReadDWord(
3142 &buffer
->dirProperty
);
3144 StorageUtl_ReadGUID(
3147 &buffer
->propertyUniqueID
);
3149 StorageUtl_ReadDWord(
3152 &buffer
->timeStampS1
);
3154 StorageUtl_ReadDWord(
3157 &buffer
->timeStampD1
);
3159 StorageUtl_ReadDWord(
3162 &buffer
->timeStampS2
);
3164 StorageUtl_ReadDWord(
3167 &buffer
->timeStampD2
);
3169 StorageUtl_ReadDWord(
3171 OFFSET_PS_STARTBLOCK
,
3172 &buffer
->startingBlock
);
3174 StorageUtl_ReadDWord(
3177 &buffer
->size
.u
.LowPart
);
3179 buffer
->size
.u
.HighPart
= 0;
3182 return readSuccessful
;
3185 /*********************************************************************
3186 * Write the specified property into the property chain
3188 BOOL
StorageImpl_WriteProperty(
3191 StgProperty
* buffer
)
3193 BYTE currentProperty
[PROPSET_BLOCK_SIZE
];
3194 ULARGE_INTEGER offsetInPropSet
;
3195 BOOL writeSuccessful
;
3198 offsetInPropSet
.u
.HighPart
= 0;
3199 offsetInPropSet
.u
.LowPart
= index
* PROPSET_BLOCK_SIZE
;
3201 memset(currentProperty
, 0, PROPSET_BLOCK_SIZE
);
3204 currentProperty
+ OFFSET_PS_NAME
,
3206 PROPERTY_NAME_BUFFER_LEN
);
3208 memcpy(currentProperty
+ OFFSET_PS_PROPERTYTYPE
, &buffer
->propertyType
, 1);
3210 StorageUtl_WriteWord(
3212 OFFSET_PS_NAMELENGTH
,
3213 buffer
->sizeOfNameString
);
3215 StorageUtl_WriteDWord(
3217 OFFSET_PS_PREVIOUSPROP
,
3218 buffer
->previousProperty
);
3220 StorageUtl_WriteDWord(
3223 buffer
->nextProperty
);
3225 StorageUtl_WriteDWord(
3228 buffer
->dirProperty
);
3230 StorageUtl_WriteGUID(
3233 &buffer
->propertyUniqueID
);
3235 StorageUtl_WriteDWord(
3238 buffer
->timeStampS1
);
3240 StorageUtl_WriteDWord(
3243 buffer
->timeStampD1
);
3245 StorageUtl_WriteDWord(
3248 buffer
->timeStampS2
);
3250 StorageUtl_WriteDWord(
3253 buffer
->timeStampD2
);
3255 StorageUtl_WriteDWord(
3257 OFFSET_PS_STARTBLOCK
,
3258 buffer
->startingBlock
);
3260 StorageUtl_WriteDWord(
3263 buffer
->size
.u
.LowPart
);
3265 writeSuccessful
= BlockChainStream_WriteAt(This
->rootBlockChain
,
3270 return writeSuccessful
;
3273 BOOL
StorageImpl_ReadBigBlock(
3278 void* bigBlockBuffer
;
3280 bigBlockBuffer
= StorageImpl_GetROBigBlock(This
, blockIndex
);
3282 if (bigBlockBuffer
!=0)
3284 memcpy(buffer
, bigBlockBuffer
, This
->bigBlockSize
);
3286 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3294 BOOL
StorageImpl_WriteBigBlock(
3299 void* bigBlockBuffer
;
3301 bigBlockBuffer
= StorageImpl_GetBigBlock(This
, blockIndex
);
3303 if (bigBlockBuffer
!=0)
3305 memcpy(bigBlockBuffer
, buffer
, This
->bigBlockSize
);
3307 StorageImpl_ReleaseBigBlock(This
, bigBlockBuffer
);
3315 void* StorageImpl_GetROBigBlock(
3319 return BIGBLOCKFILE_GetROBigBlock(This
->bigBlockFile
, blockIndex
);
3322 void* StorageImpl_GetBigBlock(
3326 return BIGBLOCKFILE_GetBigBlock(This
->bigBlockFile
, blockIndex
);
3329 void StorageImpl_ReleaseBigBlock(
3333 BIGBLOCKFILE_ReleaseBigBlock(This
->bigBlockFile
, pBigBlock
);
3336 /******************************************************************************
3337 * Storage32Impl_SmallBlocksToBigBlocks
3339 * This method will convert a small block chain to a big block chain.
3340 * The small block chain will be destroyed.
3342 BlockChainStream
* Storage32Impl_SmallBlocksToBigBlocks(
3344 SmallBlockChainStream
** ppsbChain
)
3346 ULONG bbHeadOfChain
= BLOCK_END_OF_CHAIN
;
3347 ULARGE_INTEGER size
, offset
;
3348 ULONG cbRead
, cbWritten
, cbTotalRead
, cbTotalWritten
;
3349 ULONG propertyIndex
;
3350 BOOL successRead
, successWrite
;
3351 StgProperty chainProperty
;
3353 BlockChainStream
*bbTempChain
= NULL
;
3354 BlockChainStream
*bigBlockChain
= NULL
;
3357 * Create a temporary big block chain that doesn't have
3358 * an associated property. This temporary chain will be
3359 * used to copy data from small blocks to big blocks.
3361 bbTempChain
= BlockChainStream_Construct(This
,
3364 if(!bbTempChain
) return NULL
;
3366 * Grow the big block chain.
3368 size
= SmallBlockChainStream_GetSize(*ppsbChain
);
3369 BlockChainStream_SetSize(bbTempChain
, size
);
3372 * Copy the contents of the small block chain to the big block chain
3373 * by small block size increments.
3375 offset
.u
.LowPart
= 0;
3376 offset
.u
.HighPart
= 0;
3380 buffer
= HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE
);
3383 successRead
= SmallBlockChainStream_ReadAt(*ppsbChain
,
3385 DEF_SMALL_BLOCK_SIZE
,
3388 cbTotalRead
+= cbRead
;
3390 successWrite
= BlockChainStream_WriteAt(bbTempChain
,
3395 cbTotalWritten
+= cbWritten
;
3397 offset
.u
.LowPart
+= This
->smallBlockSize
;
3399 } while (successRead
&& successWrite
);
3400 HeapFree(GetProcessHeap(),0,buffer
);
3402 assert(cbTotalRead
== cbTotalWritten
);
3405 * Destroy the small block chain.
3407 propertyIndex
= (*ppsbChain
)->ownerPropertyIndex
;
3408 size
.u
.HighPart
= 0;
3410 SmallBlockChainStream_SetSize(*ppsbChain
, size
);
3411 SmallBlockChainStream_Destroy(*ppsbChain
);
3415 * Change the property information. This chain is now a big block chain
3416 * and it doesn't reside in the small blocks chain anymore.
3418 StorageImpl_ReadProperty(This
, propertyIndex
, &chainProperty
);
3420 chainProperty
.startingBlock
= bbHeadOfChain
;
3422 StorageImpl_WriteProperty(This
, propertyIndex
, &chainProperty
);
3425 * Destroy the temporary propertyless big block chain.
3426 * Create a new big block chain associated with this property.
3428 BlockChainStream_Destroy(bbTempChain
);
3429 bigBlockChain
= BlockChainStream_Construct(This
,
3433 return bigBlockChain
;
3436 void StorageInternalImpl_Destroy( StorageBaseImpl
*iface
)
3438 StorageInternalImpl
* This
= (StorageInternalImpl
*) iface
;
3440 StorageBaseImpl_Release((IStorage
*)This
->base
.ancestorStorage
);
3441 HeapFree(GetProcessHeap(), 0, This
);
3444 /******************************************************************************
3446 ** Storage32InternalImpl_Commit
3448 ** The non-root storages cannot be opened in transacted mode thus this function
3451 HRESULT WINAPI
StorageInternalImpl_Commit(
3453 DWORD grfCommitFlags
) /* [in] */
3458 /******************************************************************************
3460 ** Storage32InternalImpl_Revert
3462 ** The non-root storages cannot be opened in transacted mode thus this function
3465 HRESULT WINAPI
StorageInternalImpl_Revert(
3471 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl
* This
)
3473 IStorage_Release((IStorage
*)This
->parentStorage
);
3474 HeapFree(GetProcessHeap(), 0, This
->stackToVisit
);
3475 HeapFree(GetProcessHeap(), 0, This
);
3478 HRESULT WINAPI
IEnumSTATSTGImpl_QueryInterface(
3479 IEnumSTATSTG
* iface
,
3483 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3486 * Perform a sanity check on the parameters.
3489 return E_INVALIDARG
;
3492 * Initialize the return parameter.
3497 * Compare the riid with the interface IDs implemented by this object.
3499 if (IsEqualGUID(&IID_IUnknown
, riid
) ||
3500 IsEqualGUID(&IID_IStorage
, riid
))
3502 *ppvObject
= (IEnumSTATSTG
*)This
;
3503 IEnumSTATSTG_AddRef((IEnumSTATSTG
*)This
);
3507 return E_NOINTERFACE
;
3510 ULONG WINAPI
IEnumSTATSTGImpl_AddRef(
3511 IEnumSTATSTG
* iface
)
3513 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3514 return InterlockedIncrement(&This
->ref
);
3517 ULONG WINAPI
IEnumSTATSTGImpl_Release(
3518 IEnumSTATSTG
* iface
)
3520 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3524 newRef
= InterlockedDecrement(&This
->ref
);
3527 * If the reference count goes down to 0, perform suicide.
3531 IEnumSTATSTGImpl_Destroy(This
);
3537 HRESULT WINAPI
IEnumSTATSTGImpl_Next(
3538 IEnumSTATSTG
* iface
,
3541 ULONG
* pceltFetched
)
3543 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3545 StgProperty currentProperty
;
3546 STATSTG
* currentReturnStruct
= rgelt
;
3547 ULONG objectFetched
= 0;
3548 ULONG currentSearchNode
;
3551 * Perform a sanity check on the parameters.
3553 if ( (rgelt
==0) || ( (celt
!=1) && (pceltFetched
==0) ) )
3554 return E_INVALIDARG
;
3557 * To avoid the special case, get another pointer to a ULONG value if
3558 * the caller didn't supply one.
3560 if (pceltFetched
==0)
3561 pceltFetched
= &objectFetched
;
3564 * Start the iteration, we will iterate until we hit the end of the
3565 * linked list or until we hit the number of items to iterate through
3570 * Start with the node at the top of the stack.
3572 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3574 while ( ( *pceltFetched
< celt
) &&
3575 ( currentSearchNode
!=PROPERTY_NULL
) )
3578 * Remove the top node from the stack
3580 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3583 * Read the property from the storage.
3585 StorageImpl_ReadProperty(This
->parentStorage
,
3590 * Copy the information to the return buffer.
3592 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct
,
3597 * Step to the next item in the iteration
3600 currentReturnStruct
++;
3603 * Push the next search node in the search stack.
3605 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3608 * continue the iteration.
3610 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3613 if (*pceltFetched
== celt
)
3620 HRESULT WINAPI
IEnumSTATSTGImpl_Skip(
3621 IEnumSTATSTG
* iface
,
3624 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3626 StgProperty currentProperty
;
3627 ULONG objectFetched
= 0;
3628 ULONG currentSearchNode
;
3631 * Start with the node at the top of the stack.
3633 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3635 while ( (objectFetched
< celt
) &&
3636 (currentSearchNode
!=PROPERTY_NULL
) )
3639 * Remove the top node from the stack
3641 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3644 * Read the property from the storage.
3646 StorageImpl_ReadProperty(This
->parentStorage
,
3651 * Step to the next item in the iteration
3656 * Push the next search node in the search stack.
3658 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
.nextProperty
);
3661 * continue the iteration.
3663 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3666 if (objectFetched
== celt
)
3672 HRESULT WINAPI
IEnumSTATSTGImpl_Reset(
3673 IEnumSTATSTG
* iface
)
3675 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3677 StgProperty rootProperty
;
3678 BOOL readSuccessful
;
3681 * Re-initialize the search stack to an empty stack
3683 This
->stackSize
= 0;
3686 * Read the root property from the storage.
3688 readSuccessful
= StorageImpl_ReadProperty(
3689 This
->parentStorage
,
3690 This
->firstPropertyNode
,
3695 assert(rootProperty
.sizeOfNameString
!=0);
3698 * Push the search node in the search stack.
3700 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.dirProperty
);
3706 HRESULT WINAPI
IEnumSTATSTGImpl_Clone(
3707 IEnumSTATSTG
* iface
,
3708 IEnumSTATSTG
** ppenum
)
3710 IEnumSTATSTGImpl
* const This
=(IEnumSTATSTGImpl
*)iface
;
3712 IEnumSTATSTGImpl
* newClone
;
3715 * Perform a sanity check on the parameters.
3718 return E_INVALIDARG
;
3720 newClone
= IEnumSTATSTGImpl_Construct(This
->parentStorage
,
3721 This
->firstPropertyNode
);
3725 * The new clone enumeration must point to the same current node as
3728 newClone
->stackSize
= This
->stackSize
;
3729 newClone
->stackMaxSize
= This
->stackMaxSize
;
3730 newClone
->stackToVisit
=
3731 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
) * newClone
->stackMaxSize
);
3734 newClone
->stackToVisit
,
3736 sizeof(ULONG
) * newClone
->stackSize
);
3738 *ppenum
= (IEnumSTATSTG
*)newClone
;
3741 * Don't forget to nail down a reference to the clone before
3744 IEnumSTATSTGImpl_AddRef(*ppenum
);
3749 INT
IEnumSTATSTGImpl_FindParentProperty(
3750 IEnumSTATSTGImpl
*This
,
3751 ULONG childProperty
,
3752 StgProperty
*currentProperty
,
3755 ULONG currentSearchNode
;
3759 * To avoid the special case, get another pointer to a ULONG value if
3760 * the caller didn't supply one.
3763 thisNodeId
= &foundNode
;
3766 * Start with the node at the top of the stack.
3768 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3771 while (currentSearchNode
!=PROPERTY_NULL
)
3774 * Store the current node in the returned parameters
3776 *thisNodeId
= currentSearchNode
;
3779 * Remove the top node from the stack
3781 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3784 * Read the property from the storage.
3786 StorageImpl_ReadProperty(
3787 This
->parentStorage
,
3791 if (currentProperty
->previousProperty
== childProperty
)
3792 return PROPERTY_RELATION_PREVIOUS
;
3794 else if (currentProperty
->nextProperty
== childProperty
)
3795 return PROPERTY_RELATION_NEXT
;
3797 else if (currentProperty
->dirProperty
== childProperty
)
3798 return PROPERTY_RELATION_DIR
;
3801 * Push the next search node in the search stack.
3803 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3806 * continue the iteration.
3808 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3811 return PROPERTY_NULL
;
3814 ULONG
IEnumSTATSTGImpl_FindProperty(
3815 IEnumSTATSTGImpl
* This
,
3816 const OLECHAR
* lpszPropName
,
3817 StgProperty
* currentProperty
)
3819 ULONG currentSearchNode
;
3822 * Start with the node at the top of the stack.
3824 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3826 while (currentSearchNode
!=PROPERTY_NULL
)
3829 * Remove the top node from the stack
3831 IEnumSTATSTGImpl_PopSearchNode(This
, TRUE
);
3834 * Read the property from the storage.
3836 StorageImpl_ReadProperty(This
->parentStorage
,
3840 if ( propertyNameCmp(
3841 (const OLECHAR
*)currentProperty
->name
,
3842 (const OLECHAR
*)lpszPropName
) == 0)
3843 return currentSearchNode
;
3846 * Push the next search node in the search stack.
3848 IEnumSTATSTGImpl_PushSearchNode(This
, currentProperty
->nextProperty
);
3851 * continue the iteration.
3853 currentSearchNode
= IEnumSTATSTGImpl_PopSearchNode(This
, FALSE
);
3856 return PROPERTY_NULL
;
3859 void IEnumSTATSTGImpl_PushSearchNode(
3860 IEnumSTATSTGImpl
* This
,
3863 StgProperty rootProperty
;
3864 BOOL readSuccessful
;
3867 * First, make sure we're not trying to push an unexisting node.
3869 if (nodeToPush
==PROPERTY_NULL
)
3873 * First push the node to the stack
3875 if (This
->stackSize
== This
->stackMaxSize
)
3877 This
->stackMaxSize
+= ENUMSTATSGT_SIZE_INCREMENT
;
3879 This
->stackToVisit
= HeapReAlloc(
3883 sizeof(ULONG
) * This
->stackMaxSize
);
3886 This
->stackToVisit
[This
->stackSize
] = nodeToPush
;
3890 * Read the root property from the storage.
3892 readSuccessful
= StorageImpl_ReadProperty(
3893 This
->parentStorage
,
3899 assert(rootProperty
.sizeOfNameString
!=0);
3902 * Push the previous search node in the search stack.
3904 IEnumSTATSTGImpl_PushSearchNode(This
, rootProperty
.previousProperty
);
3908 ULONG
IEnumSTATSTGImpl_PopSearchNode(
3909 IEnumSTATSTGImpl
* This
,
3914 if (This
->stackSize
== 0)
3915 return PROPERTY_NULL
;
3917 topNode
= This
->stackToVisit
[This
->stackSize
-1];
3926 * Virtual function table for the IEnumSTATSTGImpl class.
3928 static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl
=
3930 IEnumSTATSTGImpl_QueryInterface
,
3931 IEnumSTATSTGImpl_AddRef
,
3932 IEnumSTATSTGImpl_Release
,
3933 IEnumSTATSTGImpl_Next
,
3934 IEnumSTATSTGImpl_Skip
,
3935 IEnumSTATSTGImpl_Reset
,
3936 IEnumSTATSTGImpl_Clone
3939 /******************************************************************************
3940 ** IEnumSTATSTGImpl implementation
3943 IEnumSTATSTGImpl
* IEnumSTATSTGImpl_Construct(
3944 StorageImpl
* parentStorage
,
3945 ULONG firstPropertyNode
)
3947 IEnumSTATSTGImpl
* newEnumeration
;
3949 newEnumeration
= HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl
));
3951 if (newEnumeration
!=0)
3954 * Set-up the virtual function table and reference count.
3956 newEnumeration
->lpVtbl
= &IEnumSTATSTGImpl_Vtbl
;
3957 newEnumeration
->ref
= 0;
3960 * We want to nail-down the reference to the storage in case the
3961 * enumeration out-lives the storage in the client application.
3963 newEnumeration
->parentStorage
= parentStorage
;
3964 IStorage_AddRef((IStorage
*)newEnumeration
->parentStorage
);
3966 newEnumeration
->firstPropertyNode
= firstPropertyNode
;
3969 * Initialize the search stack
3971 newEnumeration
->stackSize
= 0;
3972 newEnumeration
->stackMaxSize
= ENUMSTATSGT_SIZE_INCREMENT
;
3973 newEnumeration
->stackToVisit
=
3974 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG
)*ENUMSTATSGT_SIZE_INCREMENT
);
3977 * Make sure the current node of the iterator is the first one.
3979 IEnumSTATSTGImpl_Reset((IEnumSTATSTG
*)newEnumeration
);
3982 return newEnumeration
;
3986 * Virtual function table for the Storage32InternalImpl class.
3988 static IStorageVtbl Storage32InternalImpl_Vtbl
=
3990 StorageBaseImpl_QueryInterface
,
3991 StorageBaseImpl_AddRef
,
3992 StorageBaseImpl_Release
,
3993 StorageBaseImpl_CreateStream
,
3994 StorageBaseImpl_OpenStream
,
3995 StorageImpl_CreateStorage
,
3996 StorageBaseImpl_OpenStorage
,
3998 StorageImpl_MoveElementTo
,
3999 StorageInternalImpl_Commit
,
4000 StorageInternalImpl_Revert
,
4001 StorageBaseImpl_EnumElements
,
4002 StorageImpl_DestroyElement
,
4003 StorageBaseImpl_RenameElement
,
4004 StorageImpl_SetElementTimes
,
4005 StorageBaseImpl_SetClass
,
4006 StorageImpl_SetStateBits
,
4007 StorageBaseImpl_Stat
4010 /******************************************************************************
4011 ** Storage32InternalImpl implementation
4014 StorageInternalImpl
* StorageInternalImpl_Construct(
4015 StorageImpl
* ancestorStorage
,
4016 ULONG rootPropertyIndex
)
4018 StorageInternalImpl
* newStorage
;
4021 * Allocate space for the new storage object
4023 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl
));
4027 memset(newStorage
, 0, sizeof(StorageInternalImpl
));
4030 * Initialize the virtual function table.
4032 newStorage
->base
.lpVtbl
= &Storage32InternalImpl_Vtbl
;
4033 newStorage
->base
.v_destructor
= &StorageInternalImpl_Destroy
;
4036 * Keep the ancestor storage pointer and nail a reference to it.
4038 newStorage
->base
.ancestorStorage
= ancestorStorage
;
4039 StorageBaseImpl_AddRef((IStorage
*)(newStorage
->base
.ancestorStorage
));
4042 * Keep the index of the root property set for this storage,
4044 newStorage
->base
.rootPropertySetIndex
= rootPropertyIndex
;
4052 /******************************************************************************
4053 ** StorageUtl implementation
4054 * FIXME: these should read and write in little-endian order on all
4055 * architectures, but right now just assume the host is little-endian.
4058 void StorageUtl_ReadWord(const BYTE
* buffer
, ULONG offset
, WORD
* value
)
4060 memcpy(value
, buffer
+offset
, sizeof(WORD
));
4063 void StorageUtl_WriteWord(BYTE
* buffer
, ULONG offset
, WORD value
)
4065 memcpy(buffer
+offset
, &value
, sizeof(WORD
));
4068 void StorageUtl_ReadDWord(const BYTE
* buffer
, ULONG offset
, DWORD
* value
)
4070 memcpy(value
, buffer
+offset
, sizeof(DWORD
));
4073 void StorageUtl_WriteDWord(BYTE
* buffer
, ULONG offset
, DWORD value
)
4075 memcpy(buffer
+offset
, &value
, sizeof(DWORD
));
4078 void StorageUtl_ReadGUID(const BYTE
* buffer
, ULONG offset
, GUID
* value
)
4080 StorageUtl_ReadDWord(buffer
, offset
, &(value
->Data1
));
4081 StorageUtl_ReadWord(buffer
, offset
+4, &(value
->Data2
));
4082 StorageUtl_ReadWord(buffer
, offset
+6, &(value
->Data3
));
4084 memcpy(value
->Data4
, buffer
+offset
+8, sizeof(value
->Data4
));
4087 void StorageUtl_WriteGUID(BYTE
* buffer
, ULONG offset
, const GUID
* value
)
4089 StorageUtl_WriteDWord(buffer
, offset
, value
->Data1
);
4090 StorageUtl_WriteWord(buffer
, offset
+4, value
->Data2
);
4091 StorageUtl_WriteWord(buffer
, offset
+6, value
->Data3
);
4093 memcpy(buffer
+offset
+8, value
->Data4
, sizeof(value
->Data4
));
4096 void StorageUtl_CopyPropertyToSTATSTG(
4097 STATSTG
* destination
,
4098 StgProperty
* source
,
4102 * The copy of the string occurs only when the flag is not set
4104 if( ((statFlags
& STATFLAG_NONAME
) != 0) ||
4105 (source
->name
== NULL
) ||
4106 (source
->name
[0] == 0) )
4108 destination
->pwcsName
= 0;
4112 destination
->pwcsName
=
4113 CoTaskMemAlloc((lstrlenW(source
->name
)+1)*sizeof(WCHAR
));
4115 strcpyW((LPWSTR
)destination
->pwcsName
, source
->name
);
4118 switch (source
->propertyType
)
4120 case PROPTYPE_STORAGE
:
4122 destination
->type
= STGTY_STORAGE
;
4124 case PROPTYPE_STREAM
:
4125 destination
->type
= STGTY_STREAM
;
4128 destination
->type
= STGTY_STREAM
;
4132 destination
->cbSize
= source
->size
;
4134 currentReturnStruct->mtime = {0}; TODO
4135 currentReturnStruct->ctime = {0};
4136 currentReturnStruct->atime = {0};
4138 destination
->grfMode
= 0;
4139 destination
->grfLocksSupported
= 0;
4140 destination
->clsid
= source
->propertyUniqueID
;
4141 destination
->grfStateBits
= 0;
4142 destination
->reserved
= 0;
4145 /******************************************************************************
4146 ** BlockChainStream implementation
4149 BlockChainStream
* BlockChainStream_Construct(
4150 StorageImpl
* parentStorage
,
4151 ULONG
* headOfStreamPlaceHolder
,
4152 ULONG propertyIndex
)
4154 BlockChainStream
* newStream
;
4157 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream
));
4159 newStream
->parentStorage
= parentStorage
;
4160 newStream
->headOfStreamPlaceHolder
= headOfStreamPlaceHolder
;
4161 newStream
->ownerPropertyIndex
= propertyIndex
;
4162 newStream
->lastBlockNoInSequence
= 0xFFFFFFFF;
4163 newStream
->tailIndex
= BLOCK_END_OF_CHAIN
;
4164 newStream
->numBlocks
= 0;
4166 blockIndex
= BlockChainStream_GetHeadOfChain(newStream
);
4168 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4170 newStream
->numBlocks
++;
4171 newStream
->tailIndex
= blockIndex
;
4173 if(FAILED(StorageImpl_GetNextBlockInChain(
4178 HeapFree(GetProcessHeap(), 0, newStream
);
4186 void BlockChainStream_Destroy(BlockChainStream
* This
)
4188 HeapFree(GetProcessHeap(), 0, This
);
4191 /******************************************************************************
4192 * BlockChainStream_GetHeadOfChain
4194 * Returns the head of this stream chain.
4195 * Some special chains don't have properties, their heads are kept in
4196 * This->headOfStreamPlaceHolder.
4199 ULONG
BlockChainStream_GetHeadOfChain(BlockChainStream
* This
)
4201 StgProperty chainProperty
;
4202 BOOL readSuccessful
;
4204 if (This
->headOfStreamPlaceHolder
!= 0)
4205 return *(This
->headOfStreamPlaceHolder
);
4207 if (This
->ownerPropertyIndex
!= PROPERTY_NULL
)
4209 readSuccessful
= StorageImpl_ReadProperty(
4210 This
->parentStorage
,
4211 This
->ownerPropertyIndex
,
4216 return chainProperty
.startingBlock
;
4220 return BLOCK_END_OF_CHAIN
;
4223 /******************************************************************************
4224 * BlockChainStream_GetCount
4226 * Returns the number of blocks that comprises this chain.
4227 * This is not the size of the stream as the last block may not be full!
4230 ULONG
BlockChainStream_GetCount(BlockChainStream
* This
)
4235 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4237 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4241 if(FAILED(StorageImpl_GetNextBlockInChain(
4242 This
->parentStorage
,
4251 /******************************************************************************
4252 * BlockChainStream_ReadAt
4254 * Reads a specified number of bytes from this chain at the specified offset.
4255 * bytesRead may be NULL.
4256 * Failure will be returned if the specified number of bytes has not been read.
4258 BOOL
BlockChainStream_ReadAt(BlockChainStream
* This
,
4259 ULARGE_INTEGER offset
,
4264 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4265 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4266 ULONG bytesToReadInBuffer
;
4269 BYTE
* bigBlockBuffer
;
4272 * Find the first block in the stream that contains part of the buffer.
4274 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4275 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4276 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4278 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4279 This
->lastBlockNoInSequence
= blockNoInSequence
;
4283 ULONG temp
= blockNoInSequence
;
4285 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4286 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4287 This
->lastBlockNoInSequence
= temp
;
4290 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4292 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4294 blockNoInSequence
--;
4297 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4300 * Start reading the buffer.
4303 bufferWalker
= buffer
;
4305 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4308 * Calculate how many bytes we can copy from this big block.
4310 bytesToReadInBuffer
=
4311 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4314 * Copy those bytes to the buffer
4317 StorageImpl_GetROBigBlock(This
->parentStorage
, blockIndex
);
4319 memcpy(bufferWalker
, bigBlockBuffer
+ offsetInBlock
, bytesToReadInBuffer
);
4321 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4324 * Step to the next big block.
4326 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
, &blockIndex
)))
4329 bufferWalker
+= bytesToReadInBuffer
;
4330 size
-= bytesToReadInBuffer
;
4331 *bytesRead
+= bytesToReadInBuffer
;
4332 offsetInBlock
= 0; /* There is no offset on the next block */
4339 /******************************************************************************
4340 * BlockChainStream_WriteAt
4342 * Writes the specified number of bytes to this chain at the specified offset.
4343 * bytesWritten may be NULL.
4344 * Will fail if not all specified number of bytes have been written.
4346 BOOL
BlockChainStream_WriteAt(BlockChainStream
* This
,
4347 ULARGE_INTEGER offset
,
4350 ULONG
* bytesWritten
)
4352 ULONG blockNoInSequence
= offset
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4353 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->bigBlockSize
;
4356 const BYTE
* bufferWalker
;
4357 BYTE
* bigBlockBuffer
;
4360 * Find the first block in the stream that contains part of the buffer.
4362 if ( (This
->lastBlockNoInSequence
== 0xFFFFFFFF) ||
4363 (This
->lastBlockNoInSequenceIndex
== BLOCK_END_OF_CHAIN
) ||
4364 (blockNoInSequence
< This
->lastBlockNoInSequence
) )
4366 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4367 This
->lastBlockNoInSequence
= blockNoInSequence
;
4371 ULONG temp
= blockNoInSequence
;
4373 blockIndex
= This
->lastBlockNoInSequenceIndex
;
4374 blockNoInSequence
-= This
->lastBlockNoInSequence
;
4375 This
->lastBlockNoInSequence
= temp
;
4378 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
4380 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4383 blockNoInSequence
--;
4386 This
->lastBlockNoInSequenceIndex
= blockIndex
;
4389 * Here, I'm casting away the constness on the buffer variable
4390 * This is OK since we don't intend to modify that buffer.
4393 bufferWalker
= (const BYTE
*)buffer
;
4395 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
4398 * Calculate how many bytes we can copy from this big block.
4401 min(This
->parentStorage
->bigBlockSize
- offsetInBlock
, size
);
4404 * Copy those bytes to the buffer
4406 bigBlockBuffer
= StorageImpl_GetBigBlock(This
->parentStorage
, blockIndex
);
4408 memcpy(bigBlockBuffer
+ offsetInBlock
, bufferWalker
, bytesToWrite
);
4410 StorageImpl_ReleaseBigBlock(This
->parentStorage
, bigBlockBuffer
);
4413 * Step to the next big block.
4415 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4418 bufferWalker
+= bytesToWrite
;
4419 size
-= bytesToWrite
;
4420 *bytesWritten
+= bytesToWrite
;
4421 offsetInBlock
= 0; /* There is no offset on the next block */
4427 /******************************************************************************
4428 * BlockChainStream_Shrink
4430 * Shrinks this chain in the big block depot.
4432 BOOL
BlockChainStream_Shrink(BlockChainStream
* This
,
4433 ULARGE_INTEGER newSize
)
4435 ULONG blockIndex
, extraBlock
;
4440 * Reset the last accessed block cache.
4442 This
->lastBlockNoInSequence
= 0xFFFFFFFF;
4443 This
->lastBlockNoInSequenceIndex
= BLOCK_END_OF_CHAIN
;
4446 * Figure out how many blocks are needed to contain the new size
4448 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4450 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4453 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4456 * Go to the new end of chain
4458 while (count
< numBlocks
)
4460 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4466 /* Get the next block before marking the new end */
4467 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, blockIndex
,
4471 /* Mark the new end of chain */
4472 StorageImpl_SetNextBlockInChain(
4473 This
->parentStorage
,
4475 BLOCK_END_OF_CHAIN
);
4477 This
->tailIndex
= blockIndex
;
4478 This
->numBlocks
= numBlocks
;
4481 * Mark the extra blocks as free
4483 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
4485 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, extraBlock
,
4488 StorageImpl_FreeBigBlock(This
->parentStorage
, extraBlock
);
4489 extraBlock
= blockIndex
;
4495 /******************************************************************************
4496 * BlockChainStream_Enlarge
4498 * Grows this chain in the big block depot.
4500 BOOL
BlockChainStream_Enlarge(BlockChainStream
* This
,
4501 ULARGE_INTEGER newSize
)
4503 ULONG blockIndex
, currentBlock
;
4505 ULONG oldNumBlocks
= 0;
4507 blockIndex
= BlockChainStream_GetHeadOfChain(This
);
4510 * Empty chain. Create the head.
4512 if (blockIndex
== BLOCK_END_OF_CHAIN
)
4514 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4515 StorageImpl_SetNextBlockInChain(This
->parentStorage
,
4517 BLOCK_END_OF_CHAIN
);
4519 if (This
->headOfStreamPlaceHolder
!= 0)
4521 *(This
->headOfStreamPlaceHolder
) = blockIndex
;
4525 StgProperty chainProp
;
4526 assert(This
->ownerPropertyIndex
!= PROPERTY_NULL
);
4528 StorageImpl_ReadProperty(
4529 This
->parentStorage
,
4530 This
->ownerPropertyIndex
,
4533 chainProp
.startingBlock
= blockIndex
;
4535 StorageImpl_WriteProperty(
4536 This
->parentStorage
,
4537 This
->ownerPropertyIndex
,
4541 This
->tailIndex
= blockIndex
;
4542 This
->numBlocks
= 1;
4546 * Figure out how many blocks are needed to contain this stream
4548 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->bigBlockSize
;
4550 if ((newSize
.u
.LowPart
% This
->parentStorage
->bigBlockSize
) != 0)
4554 * Go to the current end of chain
4556 if (This
->tailIndex
== BLOCK_END_OF_CHAIN
)
4558 currentBlock
= blockIndex
;
4560 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
4563 currentBlock
= blockIndex
;
4565 if(FAILED(StorageImpl_GetNextBlockInChain(This
->parentStorage
, currentBlock
,
4570 This
->tailIndex
= currentBlock
;
4573 currentBlock
= This
->tailIndex
;
4574 oldNumBlocks
= This
->numBlocks
;
4577 * Add new blocks to the chain
4579 if (oldNumBlocks
< newNumBlocks
)
4581 while (oldNumBlocks
< newNumBlocks
)
4583 blockIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4585 StorageImpl_SetNextBlockInChain(
4586 This
->parentStorage
,
4590 StorageImpl_SetNextBlockInChain(
4591 This
->parentStorage
,
4593 BLOCK_END_OF_CHAIN
);
4595 currentBlock
= blockIndex
;
4599 This
->tailIndex
= blockIndex
;
4600 This
->numBlocks
= newNumBlocks
;
4606 /******************************************************************************
4607 * BlockChainStream_SetSize
4609 * Sets the size of this stream. The big block depot will be updated.
4610 * The file will grow if we grow the chain.
4612 * TODO: Free the actual blocks in the file when we shrink the chain.
4613 * Currently, the blocks are still in the file. So the file size
4614 * doesn't shrink even if we shrink streams.
4616 BOOL
BlockChainStream_SetSize(
4617 BlockChainStream
* This
,
4618 ULARGE_INTEGER newSize
)
4620 ULARGE_INTEGER size
= BlockChainStream_GetSize(This
);
4622 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
4625 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
4627 BlockChainStream_Shrink(This
, newSize
);
4631 ULARGE_INTEGER fileSize
=
4632 BIGBLOCKFILE_GetSize(This
->parentStorage
->bigBlockFile
);
4634 ULONG diff
= newSize
.u
.LowPart
- size
.u
.LowPart
;
4637 * Make sure the file stays a multiple of blocksize
4639 if ((diff
% This
->parentStorage
->bigBlockSize
) != 0)
4640 diff
+= (This
->parentStorage
->bigBlockSize
-
4641 (diff
% This
->parentStorage
->bigBlockSize
) );
4643 fileSize
.u
.LowPart
+= diff
;
4644 BIGBLOCKFILE_SetSize(This
->parentStorage
->bigBlockFile
, fileSize
);
4646 BlockChainStream_Enlarge(This
, newSize
);
4652 /******************************************************************************
4653 * BlockChainStream_GetSize
4655 * Returns the size of this chain.
4656 * Will return the block count if this chain doesn't have a property.
4658 ULARGE_INTEGER
BlockChainStream_GetSize(BlockChainStream
* This
)
4660 StgProperty chainProperty
;
4662 if(This
->headOfStreamPlaceHolder
== NULL
)
4665 * This chain is a data stream read the property and return
4666 * the appropriate size
4668 StorageImpl_ReadProperty(
4669 This
->parentStorage
,
4670 This
->ownerPropertyIndex
,
4673 return chainProperty
.size
;
4678 * this chain is a chain that does not have a property, figure out the
4679 * size by making the product number of used blocks times the
4682 ULARGE_INTEGER result
;
4683 result
.u
.HighPart
= 0;
4686 BlockChainStream_GetCount(This
) *
4687 This
->parentStorage
->bigBlockSize
;
4693 /******************************************************************************
4694 ** SmallBlockChainStream implementation
4697 SmallBlockChainStream
* SmallBlockChainStream_Construct(
4698 StorageImpl
* parentStorage
,
4699 ULONG propertyIndex
)
4701 SmallBlockChainStream
* newStream
;
4703 newStream
= HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream
));
4705 newStream
->parentStorage
= parentStorage
;
4706 newStream
->ownerPropertyIndex
= propertyIndex
;
4711 void SmallBlockChainStream_Destroy(
4712 SmallBlockChainStream
* This
)
4714 HeapFree(GetProcessHeap(), 0, This
);
4717 /******************************************************************************
4718 * SmallBlockChainStream_GetHeadOfChain
4720 * Returns the head of this chain of small blocks.
4722 ULONG
SmallBlockChainStream_GetHeadOfChain(
4723 SmallBlockChainStream
* This
)
4725 StgProperty chainProperty
;
4726 BOOL readSuccessful
;
4728 if (This
->ownerPropertyIndex
)
4730 readSuccessful
= StorageImpl_ReadProperty(
4731 This
->parentStorage
,
4732 This
->ownerPropertyIndex
,
4737 return chainProperty
.startingBlock
;
4742 return BLOCK_END_OF_CHAIN
;
4745 /******************************************************************************
4746 * SmallBlockChainStream_GetNextBlockInChain
4748 * Returns the index of the next small block in this chain.
4751 * - BLOCK_END_OF_CHAIN: end of this chain
4752 * - BLOCK_UNUSED: small block 'blockIndex' is free
4754 HRESULT
SmallBlockChainStream_GetNextBlockInChain(
4755 SmallBlockChainStream
* This
,
4757 ULONG
* nextBlockInChain
)
4759 ULARGE_INTEGER offsetOfBlockInDepot
;
4764 *nextBlockInChain
= BLOCK_END_OF_CHAIN
;
4766 offsetOfBlockInDepot
.u
.HighPart
= 0;
4767 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4770 * Read those bytes in the buffer from the small block file.
4772 success
= BlockChainStream_ReadAt(
4773 This
->parentStorage
->smallBlockDepotChain
,
4774 offsetOfBlockInDepot
,
4781 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, nextBlockInChain
);
4785 return STG_E_READFAULT
;
4788 /******************************************************************************
4789 * SmallBlockChainStream_SetNextBlockInChain
4791 * Writes the index of the next block of the specified block in the small
4793 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4794 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4796 void SmallBlockChainStream_SetNextBlockInChain(
4797 SmallBlockChainStream
* This
,
4801 ULARGE_INTEGER offsetOfBlockInDepot
;
4805 offsetOfBlockInDepot
.u
.HighPart
= 0;
4806 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4808 StorageUtl_WriteDWord((BYTE
*)&buffer
, 0, nextBlock
);
4811 * Read those bytes in the buffer from the small block file.
4813 BlockChainStream_WriteAt(
4814 This
->parentStorage
->smallBlockDepotChain
,
4815 offsetOfBlockInDepot
,
4821 /******************************************************************************
4822 * SmallBlockChainStream_FreeBlock
4824 * Flag small block 'blockIndex' as free in the small block depot.
4826 void SmallBlockChainStream_FreeBlock(
4827 SmallBlockChainStream
* This
,
4830 SmallBlockChainStream_SetNextBlockInChain(This
, blockIndex
, BLOCK_UNUSED
);
4833 /******************************************************************************
4834 * SmallBlockChainStream_GetNextFreeBlock
4836 * Returns the index of a free small block. The small block depot will be
4837 * enlarged if necessary. The small block chain will also be enlarged if
4840 ULONG
SmallBlockChainStream_GetNextFreeBlock(
4841 SmallBlockChainStream
* This
)
4843 ULARGE_INTEGER offsetOfBlockInDepot
;
4846 ULONG blockIndex
= 0;
4847 ULONG nextBlockIndex
= BLOCK_END_OF_CHAIN
;
4848 BOOL success
= TRUE
;
4849 ULONG smallBlocksPerBigBlock
;
4851 offsetOfBlockInDepot
.u
.HighPart
= 0;
4854 * Scan the small block depot for a free block
4856 while (nextBlockIndex
!= BLOCK_UNUSED
)
4858 offsetOfBlockInDepot
.u
.LowPart
= blockIndex
* sizeof(ULONG
);
4860 success
= BlockChainStream_ReadAt(
4861 This
->parentStorage
->smallBlockDepotChain
,
4862 offsetOfBlockInDepot
,
4868 * If we run out of space for the small block depot, enlarge it
4872 StorageUtl_ReadDWord((BYTE
*)&buffer
, 0, &nextBlockIndex
);
4874 if (nextBlockIndex
!= BLOCK_UNUSED
)
4880 BlockChainStream_GetCount(This
->parentStorage
->smallBlockDepotChain
);
4882 ULONG sbdIndex
= This
->parentStorage
->smallBlockDepotStart
;
4883 ULONG nextBlock
, newsbdIndex
;
4884 BYTE
* smallBlockDepot
;
4886 nextBlock
= sbdIndex
;
4887 while (nextBlock
!= BLOCK_END_OF_CHAIN
)
4889 sbdIndex
= nextBlock
;
4890 StorageImpl_GetNextBlockInChain(This
->parentStorage
, sbdIndex
, &nextBlock
);
4893 newsbdIndex
= StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4894 if (sbdIndex
!= BLOCK_END_OF_CHAIN
)
4895 StorageImpl_SetNextBlockInChain(
4896 This
->parentStorage
,
4900 StorageImpl_SetNextBlockInChain(
4901 This
->parentStorage
,
4903 BLOCK_END_OF_CHAIN
);
4906 * Initialize all the small blocks to free
4909 StorageImpl_GetBigBlock(This
->parentStorage
, newsbdIndex
);
4911 memset(smallBlockDepot
, BLOCK_UNUSED
, This
->parentStorage
->bigBlockSize
);
4912 StorageImpl_ReleaseBigBlock(This
->parentStorage
, smallBlockDepot
);
4917 * We have just created the small block depot.
4919 StgProperty rootProp
;
4923 * Save it in the header
4925 This
->parentStorage
->smallBlockDepotStart
= newsbdIndex
;
4926 StorageImpl_SaveFileHeader(This
->parentStorage
);
4929 * And allocate the first big block that will contain small blocks
4932 StorageImpl_GetNextFreeBigBlock(This
->parentStorage
);
4934 StorageImpl_SetNextBlockInChain(
4935 This
->parentStorage
,
4937 BLOCK_END_OF_CHAIN
);
4939 StorageImpl_ReadProperty(
4940 This
->parentStorage
,
4941 This
->parentStorage
->base
.rootPropertySetIndex
,
4944 rootProp
.startingBlock
= sbStartIndex
;
4945 rootProp
.size
.u
.HighPart
= 0;
4946 rootProp
.size
.u
.LowPart
= This
->parentStorage
->bigBlockSize
;
4948 StorageImpl_WriteProperty(
4949 This
->parentStorage
,
4950 This
->parentStorage
->base
.rootPropertySetIndex
,
4956 smallBlocksPerBigBlock
=
4957 This
->parentStorage
->bigBlockSize
/ This
->parentStorage
->smallBlockSize
;
4960 * Verify if we have to allocate big blocks to contain small blocks
4962 if (blockIndex
% smallBlocksPerBigBlock
== 0)
4964 StgProperty rootProp
;
4965 ULONG blocksRequired
= (blockIndex
/ smallBlocksPerBigBlock
) + 1;
4967 StorageImpl_ReadProperty(
4968 This
->parentStorage
,
4969 This
->parentStorage
->base
.rootPropertySetIndex
,
4972 if (rootProp
.size
.u
.LowPart
<
4973 (blocksRequired
* This
->parentStorage
->bigBlockSize
))
4975 rootProp
.size
.u
.LowPart
+= This
->parentStorage
->bigBlockSize
;
4977 BlockChainStream_SetSize(
4978 This
->parentStorage
->smallBlockRootChain
,
4981 StorageImpl_WriteProperty(
4982 This
->parentStorage
,
4983 This
->parentStorage
->base
.rootPropertySetIndex
,
4991 /******************************************************************************
4992 * SmallBlockChainStream_ReadAt
4994 * Reads a specified number of bytes from this chain at the specified offset.
4995 * bytesRead may be NULL.
4996 * Failure will be returned if the specified number of bytes has not been read.
4998 BOOL
SmallBlockChainStream_ReadAt(
4999 SmallBlockChainStream
* This
,
5000 ULARGE_INTEGER offset
,
5005 ULARGE_INTEGER offsetInBigBlockFile
;
5006 ULONG blockNoInSequence
=
5007 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5009 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5010 ULONG bytesToReadInBuffer
;
5012 ULONG bytesReadFromBigBlockFile
;
5016 * This should never happen on a small block file.
5018 assert(offset
.u
.HighPart
==0);
5021 * Find the first block in the stream that contains part of the buffer.
5023 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5025 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5027 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5030 blockNoInSequence
--;
5034 * Start reading the buffer.
5037 bufferWalker
= buffer
;
5039 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5042 * Calculate how many bytes we can copy from this small block.
5044 bytesToReadInBuffer
=
5045 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5048 * Calculate the offset of the small block in the small block file.
5050 offsetInBigBlockFile
.u
.HighPart
= 0;
5051 offsetInBigBlockFile
.u
.LowPart
=
5052 blockIndex
* This
->parentStorage
->smallBlockSize
;
5054 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5057 * Read those bytes in the buffer from the small block file.
5059 BlockChainStream_ReadAt(This
->parentStorage
->smallBlockRootChain
,
5060 offsetInBigBlockFile
,
5061 bytesToReadInBuffer
,
5063 &bytesReadFromBigBlockFile
);
5065 assert(bytesReadFromBigBlockFile
== bytesToReadInBuffer
);
5068 * Step to the next big block.
5070 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5072 bufferWalker
+= bytesToReadInBuffer
;
5073 size
-= bytesToReadInBuffer
;
5074 *bytesRead
+= bytesToReadInBuffer
;
5075 offsetInBlock
= 0; /* There is no offset on the next block */
5081 /******************************************************************************
5082 * SmallBlockChainStream_WriteAt
5084 * Writes the specified number of bytes to this chain at the specified offset.
5085 * bytesWritten may be NULL.
5086 * Will fail if not all specified number of bytes have been written.
5088 BOOL
SmallBlockChainStream_WriteAt(
5089 SmallBlockChainStream
* This
,
5090 ULARGE_INTEGER offset
,
5093 ULONG
* bytesWritten
)
5095 ULARGE_INTEGER offsetInBigBlockFile
;
5096 ULONG blockNoInSequence
=
5097 offset
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5099 ULONG offsetInBlock
= offset
.u
.LowPart
% This
->parentStorage
->smallBlockSize
;
5100 ULONG bytesToWriteInBuffer
;
5102 ULONG bytesWrittenFromBigBlockFile
;
5103 const BYTE
* bufferWalker
;
5106 * This should never happen on a small block file.
5108 assert(offset
.u
.HighPart
==0);
5111 * Find the first block in the stream that contains part of the buffer.
5113 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5115 while ( (blockNoInSequence
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
))
5117 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5119 blockNoInSequence
--;
5123 * Start writing the buffer.
5125 * Here, I'm casting away the constness on the buffer variable
5126 * This is OK since we don't intend to modify that buffer.
5129 bufferWalker
= (const BYTE
*)buffer
;
5130 while ( (size
> 0) && (blockIndex
!= BLOCK_END_OF_CHAIN
) )
5133 * Calculate how many bytes we can copy to this small block.
5135 bytesToWriteInBuffer
=
5136 min(This
->parentStorage
->smallBlockSize
- offsetInBlock
, size
);
5139 * Calculate the offset of the small block in the small block file.
5141 offsetInBigBlockFile
.u
.HighPart
= 0;
5142 offsetInBigBlockFile
.u
.LowPart
=
5143 blockIndex
* This
->parentStorage
->smallBlockSize
;
5145 offsetInBigBlockFile
.u
.LowPart
+= offsetInBlock
;
5148 * Write those bytes in the buffer to the small block file.
5150 BlockChainStream_WriteAt(This
->parentStorage
->smallBlockRootChain
,
5151 offsetInBigBlockFile
,
5152 bytesToWriteInBuffer
,
5154 &bytesWrittenFromBigBlockFile
);
5156 assert(bytesWrittenFromBigBlockFile
== bytesToWriteInBuffer
);
5159 * Step to the next big block.
5161 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5164 bufferWalker
+= bytesToWriteInBuffer
;
5165 size
-= bytesToWriteInBuffer
;
5166 *bytesWritten
+= bytesToWriteInBuffer
;
5167 offsetInBlock
= 0; /* There is no offset on the next block */
5173 /******************************************************************************
5174 * SmallBlockChainStream_Shrink
5176 * Shrinks this chain in the small block depot.
5178 BOOL
SmallBlockChainStream_Shrink(
5179 SmallBlockChainStream
* This
,
5180 ULARGE_INTEGER newSize
)
5182 ULONG blockIndex
, extraBlock
;
5186 numBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5188 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5191 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5194 * Go to the new end of chain
5196 while (count
< numBlocks
)
5198 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5205 * If the count is 0, we have a special case, the head of the chain was
5210 StgProperty chainProp
;
5212 StorageImpl_ReadProperty(This
->parentStorage
,
5213 This
->ownerPropertyIndex
,
5216 chainProp
.startingBlock
= BLOCK_END_OF_CHAIN
;
5218 StorageImpl_WriteProperty(This
->parentStorage
,
5219 This
->ownerPropertyIndex
,
5223 * We start freeing the chain at the head block.
5225 extraBlock
= blockIndex
;
5229 /* Get the next block before marking the new end */
5230 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
,
5234 /* Mark the new end of chain */
5235 SmallBlockChainStream_SetNextBlockInChain(
5238 BLOCK_END_OF_CHAIN
);
5242 * Mark the extra blocks as free
5244 while (extraBlock
!= BLOCK_END_OF_CHAIN
)
5246 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, extraBlock
,
5249 SmallBlockChainStream_FreeBlock(This
, extraBlock
);
5250 extraBlock
= blockIndex
;
5256 /******************************************************************************
5257 * SmallBlockChainStream_Enlarge
5259 * Grows this chain in the small block depot.
5261 BOOL
SmallBlockChainStream_Enlarge(
5262 SmallBlockChainStream
* This
,
5263 ULARGE_INTEGER newSize
)
5265 ULONG blockIndex
, currentBlock
;
5267 ULONG oldNumBlocks
= 0;
5269 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5274 if (blockIndex
== BLOCK_END_OF_CHAIN
)
5277 StgProperty chainProp
;
5279 StorageImpl_ReadProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5282 chainProp
.startingBlock
= SmallBlockChainStream_GetNextFreeBlock(This
);
5284 StorageImpl_WriteProperty(This
->parentStorage
, This
->ownerPropertyIndex
,
5287 blockIndex
= chainProp
.startingBlock
;
5288 SmallBlockChainStream_SetNextBlockInChain(
5291 BLOCK_END_OF_CHAIN
);
5294 currentBlock
= blockIndex
;
5297 * Figure out how many blocks are needed to contain this stream
5299 newNumBlocks
= newSize
.u
.LowPart
/ This
->parentStorage
->smallBlockSize
;
5301 if ((newSize
.u
.LowPart
% This
->parentStorage
->smallBlockSize
) != 0)
5305 * Go to the current end of chain
5307 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5310 currentBlock
= blockIndex
;
5311 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, currentBlock
, &blockIndex
)))
5316 * Add new blocks to the chain
5318 while (oldNumBlocks
< newNumBlocks
)
5320 blockIndex
= SmallBlockChainStream_GetNextFreeBlock(This
);
5321 SmallBlockChainStream_SetNextBlockInChain(This
, currentBlock
, blockIndex
);
5323 SmallBlockChainStream_SetNextBlockInChain(
5326 BLOCK_END_OF_CHAIN
);
5328 currentBlock
= blockIndex
;
5335 /******************************************************************************
5336 * SmallBlockChainStream_GetCount
5338 * Returns the number of blocks that comprises this chain.
5339 * This is not the size of this chain as the last block may not be full!
5341 ULONG
SmallBlockChainStream_GetCount(SmallBlockChainStream
* This
)
5346 blockIndex
= SmallBlockChainStream_GetHeadOfChain(This
);
5348 while (blockIndex
!= BLOCK_END_OF_CHAIN
)
5352 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This
, blockIndex
, &blockIndex
)))
5359 /******************************************************************************
5360 * SmallBlockChainStream_SetSize
5362 * Sets the size of this stream.
5363 * The file will grow if we grow the chain.
5365 * TODO: Free the actual blocks in the file when we shrink the chain.
5366 * Currently, the blocks are still in the file. So the file size
5367 * doesn't shrink even if we shrink streams.
5369 BOOL
SmallBlockChainStream_SetSize(
5370 SmallBlockChainStream
* This
,
5371 ULARGE_INTEGER newSize
)
5373 ULARGE_INTEGER size
= SmallBlockChainStream_GetSize(This
);
5375 if (newSize
.u
.LowPart
== size
.u
.LowPart
)
5378 if (newSize
.u
.LowPart
< size
.u
.LowPart
)
5380 SmallBlockChainStream_Shrink(This
, newSize
);
5384 SmallBlockChainStream_Enlarge(This
, newSize
);
5390 /******************************************************************************
5391 * SmallBlockChainStream_GetSize
5393 * Returns the size of this chain.
5395 ULARGE_INTEGER
SmallBlockChainStream_GetSize(SmallBlockChainStream
* This
)
5397 StgProperty chainProperty
;
5399 StorageImpl_ReadProperty(
5400 This
->parentStorage
,
5401 This
->ownerPropertyIndex
,
5404 return chainProperty
.size
;
5407 /******************************************************************************
5408 * StgCreateDocfile [OLE32.@]
5410 HRESULT WINAPI
StgCreateDocfile(
5414 IStorage
**ppstgOpen
)
5416 StorageImpl
* newStorage
= 0;
5417 HANDLE hFile
= INVALID_HANDLE_VALUE
;
5418 HRESULT hr
= STG_E_INVALIDFLAG
;
5422 DWORD fileAttributes
;
5423 WCHAR tempFileName
[MAX_PATH
];
5425 TRACE("(%s, %lx, %ld, %p)\n",
5426 debugstr_w(pwcsName
), grfMode
,
5427 reserved
, ppstgOpen
);
5430 * Validate the parameters
5433 return STG_E_INVALIDPOINTER
;
5435 return STG_E_INVALIDPARAMETER
;
5438 * Validate the STGM flags
5440 if ( FAILED( validateSTGM(grfMode
) ))
5443 /* StgCreateDocFile always opens for write */
5444 switch(STGM_ACCESS_MODE(grfMode
))
5447 case STGM_READWRITE
:
5453 /* can't share write */
5454 switch(STGM_SHARE_MODE(grfMode
))
5456 case STGM_SHARE_EXCLUSIVE
:
5457 case STGM_SHARE_DENY_WRITE
:
5463 /* shared reading requires transacted mode */
5464 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
5465 !(grfMode
&STGM_TRANSACTED
) )
5469 * Generate a unique name.
5473 WCHAR tempPath
[MAX_PATH
];
5474 static const WCHAR prefix
[] = { 'S', 'T', 'O', 0 };
5476 if (STGM_SHARE_MODE(grfMode
) != STGM_SHARE_EXCLUSIVE
)
5479 memset(tempPath
, 0, sizeof(tempPath
));
5480 memset(tempFileName
, 0, sizeof(tempFileName
));
5482 if ((GetTempPathW(MAX_PATH
, tempPath
)) == 0 )
5485 if (GetTempFileNameW(tempPath
, prefix
, 0, tempFileName
) != 0)
5486 pwcsName
= tempFileName
;
5489 hr
= STG_E_INSUFFICIENTMEMORY
;
5493 creationMode
= TRUNCATE_EXISTING
;
5497 creationMode
= GetCreationModeFromSTGM(grfMode
);
5501 * Interpret the STGM value grfMode
5503 shareMode
= GetShareModeFromSTGM(grfMode
);
5504 accessMode
= GetAccessModeFromSTGM(grfMode
);
5506 if (grfMode
& STGM_DELETEONRELEASE
)
5507 fileAttributes
= FILE_FLAG_RANDOM_ACCESS
| FILE_FLAG_DELETE_ON_CLOSE
;
5509 fileAttributes
= FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
;
5511 if (grfMode
& STGM_TRANSACTED
)
5512 FIXME("Transacted mode not implemented.\n");
5515 * Initialize the "out" parameter.
5519 hFile
= CreateFileW(pwcsName
,
5527 if (hFile
== INVALID_HANDLE_VALUE
)
5529 if(GetLastError() == ERROR_FILE_EXISTS
)
5530 hr
= STG_E_FILEALREADYEXISTS
;
5537 * Allocate and initialize the new IStorage32object.
5539 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5541 if (newStorage
== 0)
5543 hr
= STG_E_INSUFFICIENTMEMORY
;
5547 hr
= StorageImpl_Construct(
5558 HeapFree(GetProcessHeap(), 0, newStorage
);
5563 * Get an "out" pointer for the caller.
5565 hr
= StorageBaseImpl_QueryInterface(
5566 (IStorage
*)newStorage
,
5567 (REFIID
)&IID_IStorage
,
5570 TRACE("<-- %p r = %08lx\n", *ppstgOpen
, hr
);
5575 /******************************************************************************
5576 * StgCreateStorageEx [OLE32.@]
5578 HRESULT WINAPI
StgCreateStorageEx(const WCHAR
* pwcsName
, DWORD grfMode
, DWORD stgfmt
, DWORD grfAttrs
, STGOPTIONS
* pStgOptions
, void* reserved
, REFIID riid
, void** ppObjectOpen
)
5580 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName
),
5581 grfMode
, stgfmt
, grfAttrs
, pStgOptions
, reserved
, riid
, ppObjectOpen
);
5582 return STG_E_UNIMPLEMENTEDFUNCTION
;
5585 /******************************************************************************
5586 * StgCreatePropSetStg [OLE32.@]
5588 HRESULT WINAPI
StgCreatePropSetStg(IStorage
*pstg
, DWORD reserved
,
5589 IPropertySetStorage
**ppPropSetStg
)
5593 TRACE("(%p, 0x%lx, %p): stub\n", pstg
, reserved
, ppPropSetStg
);
5595 hr
= STG_E_INVALIDPARAMETER
;
5597 hr
= StorageBaseImpl_QueryInterface(pstg
, &IID_IPropertySetStorage
,
5598 (void**)ppPropSetStg
);
5602 /******************************************************************************
5603 * StgOpenStorage [OLE32.@]
5605 HRESULT WINAPI
StgOpenStorage(
5606 const OLECHAR
*pwcsName
,
5607 IStorage
*pstgPriority
,
5611 IStorage
**ppstgOpen
)
5613 StorageImpl
* newStorage
= 0;
5618 WCHAR fullname
[MAX_PATH
];
5621 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5622 debugstr_w(pwcsName
), pstgPriority
, grfMode
,
5623 snbExclude
, reserved
, ppstgOpen
);
5626 * Perform sanity checks
5630 hr
= STG_E_INVALIDNAME
;
5636 hr
= STG_E_INVALIDPOINTER
;
5642 hr
= STG_E_INVALIDPARAMETER
;
5647 * Validate the sharing mode
5649 switch(STGM_SHARE_MODE(grfMode
))
5651 case STGM_SHARE_EXCLUSIVE
:
5652 case STGM_SHARE_DENY_WRITE
:
5655 hr
= STG_E_INVALIDFLAG
;
5660 * Validate the STGM flags
5662 if ( FAILED( validateSTGM(grfMode
) ) ||
5663 (grfMode
&STGM_CREATE
))
5665 hr
= STG_E_INVALIDFLAG
;
5669 /* shared reading requires transacted mode */
5670 if( STGM_SHARE_MODE(grfMode
) == STGM_SHARE_DENY_WRITE
&&
5671 STGM_ACCESS_MODE(grfMode
) == STGM_READWRITE
&&
5672 !(grfMode
&STGM_TRANSACTED
) )
5674 hr
= STG_E_INVALIDFLAG
;
5679 * Interpret the STGM value grfMode
5681 shareMode
= GetShareModeFromSTGM(grfMode
);
5682 accessMode
= GetAccessModeFromSTGM(grfMode
);
5685 * Initialize the "out" parameter.
5689 hFile
= CreateFileW( pwcsName
,
5694 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_RANDOM_ACCESS
,
5697 if (hFile
==INVALID_HANDLE_VALUE
)
5699 DWORD last_error
= GetLastError();
5705 case ERROR_FILE_NOT_FOUND
:
5706 hr
= STG_E_FILENOTFOUND
;
5709 case ERROR_PATH_NOT_FOUND
:
5710 hr
= STG_E_PATHNOTFOUND
;
5713 case ERROR_ACCESS_DENIED
:
5714 case ERROR_WRITE_PROTECT
:
5715 hr
= STG_E_ACCESSDENIED
;
5718 case ERROR_SHARING_VIOLATION
:
5719 hr
= STG_E_SHAREVIOLATION
;
5730 * Refuse to open the file if it's too small to be a structured storage file
5731 * FIXME: verify the file when reading instead of here
5733 length
= GetFileSize(hFile
, NULL
);
5737 hr
= STG_E_FILEALREADYEXISTS
;
5742 * Allocate and initialize the new IStorage32object.
5744 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5746 if (newStorage
== 0)
5748 hr
= STG_E_INSUFFICIENTMEMORY
;
5752 /* if the file's length was zero, initialize the storage */
5753 hr
= StorageImpl_Construct(
5764 HeapFree(GetProcessHeap(), 0, newStorage
);
5766 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5768 if(hr
== STG_E_INVALIDHEADER
)
5769 hr
= STG_E_FILEALREADYEXISTS
;
5773 /* prepare the file name string given in lieu of the root property name */
5774 GetFullPathNameW(pwcsName
, MAX_PATH
, fullname
, NULL
);
5775 memcpy(newStorage
->filename
, fullname
, PROPERTY_NAME_BUFFER_LEN
);
5776 newStorage
->filename
[PROPERTY_NAME_BUFFER_LEN
-1] = '\0';
5779 * Get an "out" pointer for the caller.
5781 hr
= StorageBaseImpl_QueryInterface(
5782 (IStorage
*)newStorage
,
5783 (REFIID
)&IID_IStorage
,
5787 TRACE("<-- %08lx, IStorage %p\n", hr
, ppstgOpen
? *ppstgOpen
: NULL
);
5791 /******************************************************************************
5792 * StgCreateDocfileOnILockBytes [OLE32.@]
5794 HRESULT WINAPI
StgCreateDocfileOnILockBytes(
5798 IStorage
** ppstgOpen
)
5800 StorageImpl
* newStorage
= 0;
5804 * Validate the parameters
5806 if ((ppstgOpen
== 0) || (plkbyt
== 0))
5807 return STG_E_INVALIDPOINTER
;
5810 * Allocate and initialize the new IStorage object.
5812 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5814 if (newStorage
== 0)
5815 return STG_E_INSUFFICIENTMEMORY
;
5817 hr
= StorageImpl_Construct(
5828 HeapFree(GetProcessHeap(), 0, newStorage
);
5833 * Get an "out" pointer for the caller.
5835 hr
= StorageBaseImpl_QueryInterface(
5836 (IStorage
*)newStorage
,
5837 (REFIID
)&IID_IStorage
,
5843 /******************************************************************************
5844 * StgOpenStorageOnILockBytes [OLE32.@]
5846 HRESULT WINAPI
StgOpenStorageOnILockBytes(
5848 IStorage
*pstgPriority
,
5852 IStorage
**ppstgOpen
)
5854 StorageImpl
* newStorage
= 0;
5858 * Perform a sanity check
5860 if ((plkbyt
== 0) || (ppstgOpen
== 0))
5861 return STG_E_INVALIDPOINTER
;
5864 * Validate the STGM flags
5866 if ( FAILED( validateSTGM(grfMode
) ))
5867 return STG_E_INVALIDFLAG
;
5870 * Initialize the "out" parameter.
5875 * Allocate and initialize the new IStorage object.
5877 newStorage
= HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl
));
5879 if (newStorage
== 0)
5880 return STG_E_INSUFFICIENTMEMORY
;
5882 hr
= StorageImpl_Construct(
5893 HeapFree(GetProcessHeap(), 0, newStorage
);
5898 * Get an "out" pointer for the caller.
5900 hr
= StorageBaseImpl_QueryInterface(
5901 (IStorage
*)newStorage
,
5902 (REFIID
)&IID_IStorage
,
5908 /******************************************************************************
5909 * StgSetTimes [ole32.@]
5910 * StgSetTimes [OLE32.@]
5914 HRESULT WINAPI
StgSetTimes(OLECHAR
const *str
, FILETIME
const *pctime
,
5915 FILETIME
const *patime
, FILETIME
const *pmtime
)
5917 IStorage
*stg
= NULL
;
5920 TRACE("%s %p %p %p\n", debugstr_w(str
), pctime
, patime
, pmtime
);
5922 r
= StgOpenStorage(str
, NULL
, STGM_READWRITE
| STGM_SHARE_DENY_WRITE
,
5926 r
= IStorage_SetElementTimes(stg
, NULL
, pctime
, patime
, pmtime
);
5927 IStorage_Release(stg
);
5933 /******************************************************************************
5934 * StgIsStorageILockBytes [OLE32.@]
5936 * Determines if the ILockBytes contains a storage object.
5938 HRESULT WINAPI
StgIsStorageILockBytes(ILockBytes
*plkbyt
)
5941 ULARGE_INTEGER offset
;
5943 offset
.u
.HighPart
= 0;
5944 offset
.u
.LowPart
= 0;
5946 ILockBytes_ReadAt(plkbyt
, offset
, sig
, sizeof(sig
), NULL
);
5948 if (memcmp(sig
, STORAGE_magic
, sizeof(STORAGE_magic
)) == 0)
5954 /******************************************************************************
5955 * WriteClassStg [OLE32.@]
5957 * This method will store the specified CLSID in the specified storage object
5959 HRESULT WINAPI
WriteClassStg(IStorage
* pStg
, REFCLSID rclsid
)
5965 hRes
= IStorage_SetClass(pStg
, rclsid
);
5970 /***********************************************************************
5971 * ReadClassStg (OLE32.@)
5973 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5975 HRESULT WINAPI
ReadClassStg(IStorage
*pstg
,CLSID
*pclsid
){
5985 * read a STATSTG structure (contains the clsid) from the storage
5987 hRes
=IStorage_Stat(pstg
,&pstatstg
,STATFLAG_DEFAULT
);
5990 *pclsid
=pstatstg
.clsid
;
5995 /***********************************************************************
5996 * OleLoadFromStream (OLE32.@)
5998 * This function loads an object from stream
6000 HRESULT WINAPI
OleLoadFromStream(IStream
*pStm
,REFIID iidInterface
,void** ppvObj
)
6004 LPPERSISTSTREAM xstm
;
6006 TRACE("(%p,%s,%p)\n",pStm
,debugstr_guid(iidInterface
),ppvObj
);
6008 res
=ReadClassStm(pStm
,&clsid
);
6009 if (!SUCCEEDED(res
))
6011 res
=CoCreateInstance(&clsid
,NULL
,CLSCTX_INPROC_SERVER
,iidInterface
,ppvObj
);
6012 if (!SUCCEEDED(res
))
6014 res
=IUnknown_QueryInterface((IUnknown
*)*ppvObj
,&IID_IPersistStream
,(LPVOID
*)&xstm
);
6015 if (!SUCCEEDED(res
)) {
6016 IUnknown_Release((IUnknown
*)*ppvObj
);
6019 res
=IPersistStream_Load(xstm
,pStm
);
6020 IPersistStream_Release(xstm
);
6021 /* FIXME: all refcounts ok at this point? I think they should be:
6024 * xstm : 0 (released)
6029 /***********************************************************************
6030 * OleSaveToStream (OLE32.@)
6032 * This function saves an object with the IPersistStream interface on it
6033 * to the specified stream.
6035 HRESULT WINAPI
OleSaveToStream(IPersistStream
*pPStm
,IStream
*pStm
)
6041 TRACE("(%p,%p)\n",pPStm
,pStm
);
6043 res
=IPersistStream_GetClassID(pPStm
,&clsid
);
6045 if (SUCCEEDED(res
)){
6047 res
=WriteClassStm(pStm
,&clsid
);
6051 res
=IPersistStream_Save(pPStm
,pStm
,TRUE
);
6054 TRACE("Finished Save\n");
6058 /****************************************************************************
6059 * This method validate a STGM parameter that can contain the values below
6061 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6062 * The stgm values contained in 0xffff0000 are bitmasks.
6064 * STGM_DIRECT 0x00000000
6065 * STGM_TRANSACTED 0x00010000
6066 * STGM_SIMPLE 0x08000000
6068 * STGM_READ 0x00000000
6069 * STGM_WRITE 0x00000001
6070 * STGM_READWRITE 0x00000002
6072 * STGM_SHARE_DENY_NONE 0x00000040
6073 * STGM_SHARE_DENY_READ 0x00000030
6074 * STGM_SHARE_DENY_WRITE 0x00000020
6075 * STGM_SHARE_EXCLUSIVE 0x00000010
6077 * STGM_PRIORITY 0x00040000
6078 * STGM_DELETEONRELEASE 0x04000000
6080 * STGM_CREATE 0x00001000
6081 * STGM_CONVERT 0x00020000
6082 * STGM_FAILIFTHERE 0x00000000
6084 * STGM_NOSCRATCH 0x00100000
6085 * STGM_NOSNAPSHOT 0x00200000
6087 static HRESULT
validateSTGM(DWORD stgm
)
6089 DWORD access
= STGM_ACCESS_MODE(stgm
);
6090 DWORD share
= STGM_SHARE_MODE(stgm
);
6091 DWORD create
= STGM_CREATE_MODE(stgm
);
6093 if (stgm
&~STGM_KNOWN_FLAGS
)
6095 ERR("unknown flags %08lx\n", stgm
);
6103 case STGM_READWRITE
:
6111 case STGM_SHARE_DENY_NONE
:
6112 case STGM_SHARE_DENY_READ
:
6113 case STGM_SHARE_DENY_WRITE
:
6114 case STGM_SHARE_EXCLUSIVE
:
6123 case STGM_FAILIFTHERE
:
6130 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6132 if ( (stgm
& STGM_TRANSACTED
) && (stgm
& STGM_SIMPLE
) )
6136 * STGM_CREATE | STGM_CONVERT
6137 * if both are false, STGM_FAILIFTHERE is set to TRUE
6139 if ( create
== STGM_CREATE
&& (stgm
& STGM_CONVERT
) )
6143 * STGM_NOSCRATCH requires STGM_TRANSACTED
6145 if ( (stgm
& STGM_NOSCRATCH
) && !(stgm
& STGM_TRANSACTED
) )
6149 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6150 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6152 if ( (stgm
& STGM_NOSNAPSHOT
) &&
6153 (!(stgm
& STGM_TRANSACTED
) ||
6154 share
== STGM_SHARE_EXCLUSIVE
||
6155 share
== STGM_SHARE_DENY_WRITE
) )
6161 /****************************************************************************
6162 * GetShareModeFromSTGM
6164 * This method will return a share mode flag from a STGM value.
6165 * The STGM value is assumed valid.
6167 static DWORD
GetShareModeFromSTGM(DWORD stgm
)
6169 switch (STGM_SHARE_MODE(stgm
))
6171 case STGM_SHARE_DENY_NONE
:
6172 return FILE_SHARE_READ
| FILE_SHARE_WRITE
;
6173 case STGM_SHARE_DENY_READ
:
6174 return FILE_SHARE_WRITE
;
6175 case STGM_SHARE_DENY_WRITE
:
6176 return FILE_SHARE_READ
;
6177 case STGM_SHARE_EXCLUSIVE
:
6180 ERR("Invalid share mode!\n");
6185 /****************************************************************************
6186 * GetAccessModeFromSTGM
6188 * This method will return an access mode flag from a STGM value.
6189 * The STGM value is assumed valid.
6191 static DWORD
GetAccessModeFromSTGM(DWORD stgm
)
6193 switch (STGM_ACCESS_MODE(stgm
))
6196 return GENERIC_READ
;
6198 case STGM_READWRITE
:
6199 return GENERIC_READ
| GENERIC_WRITE
;
6201 ERR("Invalid access mode!\n");
6206 /****************************************************************************
6207 * GetCreationModeFromSTGM
6209 * This method will return a creation mode flag from a STGM value.
6210 * The STGM value is assumed valid.
6212 static DWORD
GetCreationModeFromSTGM(DWORD stgm
)
6214 switch(STGM_CREATE_MODE(stgm
))
6217 return CREATE_ALWAYS
;
6219 FIXME("STGM_CONVERT not implemented!\n");
6221 case STGM_FAILIFTHERE
:
6224 ERR("Invalid create mode!\n");
6230 /*************************************************************************
6231 * OLECONVERT_LoadOLE10 [Internal]
6233 * Loads the OLE10 STREAM to memory
6236 * pOleStream [I] The OLESTREAM
6237 * pData [I] Data Structure for the OLESTREAM Data
6241 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6242 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6245 * This function is used by OleConvertOLESTREAMToIStorage only.
6247 * Memory allocated for pData must be freed by the caller
6249 HRESULT
OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream
, OLECONVERT_OLESTREAM_DATA
*pData
, BOOL bStrem1
)
6252 HRESULT hRes
= S_OK
;
6256 pData
->pData
= NULL
;
6257 pData
->pstrOleObjFileName
= (CHAR
*) NULL
;
6259 for( nTryCnt
=0;nTryCnt
< max_try
; nTryCnt
++)
6262 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6263 if(dwSize
!= sizeof(pData
->dwOleID
))
6265 hRes
= CONVERT10_E_OLESTREAM_GET
;
6267 else if(pData
->dwOleID
!= OLESTREAM_ID
)
6269 hRes
= CONVERT10_E_OLESTREAM_FMT
;
6280 /* Get the TypeID...more info needed for this field */
6281 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6282 if(dwSize
!= sizeof(pData
->dwTypeID
))
6284 hRes
= CONVERT10_E_OLESTREAM_GET
;
6289 if(pData
->dwTypeID
!= 0)
6291 /* Get the length of the OleTypeName */
6292 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *) &(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6293 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6295 hRes
= CONVERT10_E_OLESTREAM_GET
;
6300 if(pData
->dwOleTypeNameLength
> 0)
6302 /* Get the OleTypeName */
6303 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6304 if(dwSize
!= pData
->dwOleTypeNameLength
)
6306 hRes
= CONVERT10_E_OLESTREAM_GET
;
6312 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwOleObjFileNameLength
), sizeof(pData
->dwOleObjFileNameLength
));
6313 if(dwSize
!= sizeof(pData
->dwOleObjFileNameLength
))
6315 hRes
= CONVERT10_E_OLESTREAM_GET
;
6319 if(pData
->dwOleObjFileNameLength
< 1) /* there is no file name exist */
6320 pData
->dwOleObjFileNameLength
= sizeof(pData
->dwOleObjFileNameLength
);
6321 pData
->pstrOleObjFileName
= HeapAlloc(GetProcessHeap(), 0, pData
->dwOleObjFileNameLength
);
6322 if(pData
->pstrOleObjFileName
)
6324 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->pstrOleObjFileName
),pData
->dwOleObjFileNameLength
);
6325 if(dwSize
!= pData
->dwOleObjFileNameLength
)
6327 hRes
= CONVERT10_E_OLESTREAM_GET
;
6331 hRes
= CONVERT10_E_OLESTREAM_GET
;
6336 /* Get the Width of the Metafile */
6337 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6338 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6340 hRes
= CONVERT10_E_OLESTREAM_GET
;
6344 /* Get the Height of the Metafile */
6345 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6346 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6348 hRes
= CONVERT10_E_OLESTREAM_GET
;
6354 /* Get the Length of the Data */
6355 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6356 if(dwSize
!= sizeof(pData
->dwDataLength
))
6358 hRes
= CONVERT10_E_OLESTREAM_GET
;
6362 if(hRes
== S_OK
) /* I don't know what is this 8 byts information is we have to figure out */
6364 if(!bStrem1
) /* if it is a second OLE stream data */
6366 pData
->dwDataLength
-= 8;
6367 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)(pData
->strUnknown
), sizeof(pData
->strUnknown
));
6368 if(dwSize
!= sizeof(pData
->strUnknown
))
6370 hRes
= CONVERT10_E_OLESTREAM_GET
;
6376 if(pData
->dwDataLength
> 0)
6378 pData
->pData
= HeapAlloc(GetProcessHeap(),0,pData
->dwDataLength
);
6380 /* Get Data (ex. IStorage, Metafile, or BMP) */
6383 dwSize
= pOleStream
->lpstbl
->Get(pOleStream
, (void *)pData
->pData
, pData
->dwDataLength
);
6384 if(dwSize
!= pData
->dwDataLength
)
6386 hRes
= CONVERT10_E_OLESTREAM_GET
;
6391 hRes
= CONVERT10_E_OLESTREAM_GET
;
6400 /*************************************************************************
6401 * OLECONVERT_SaveOLE10 [Internal]
6403 * Saves the OLE10 STREAM From memory
6406 * pData [I] Data Structure for the OLESTREAM Data
6407 * pOleStream [I] The OLESTREAM to save
6411 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6414 * This function is used by OleConvertIStorageToOLESTREAM only.
6417 HRESULT
OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA
*pData
, LPOLESTREAM pOleStream
)
6420 HRESULT hRes
= S_OK
;
6424 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleID
), sizeof(pData
->dwOleID
));
6425 if(dwSize
!= sizeof(pData
->dwOleID
))
6427 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6432 /* Set the TypeID */
6433 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwTypeID
), sizeof(pData
->dwTypeID
));
6434 if(dwSize
!= sizeof(pData
->dwTypeID
))
6436 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6440 if(pData
->dwOleID
== OLESTREAM_ID
&& pData
->dwTypeID
!= 0 && hRes
== S_OK
)
6442 /* Set the Length of the OleTypeName */
6443 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwOleTypeNameLength
), sizeof(pData
->dwOleTypeNameLength
));
6444 if(dwSize
!= sizeof(pData
->dwOleTypeNameLength
))
6446 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6451 if(pData
->dwOleTypeNameLength
> 0)
6453 /* Set the OleTypeName */
6454 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->strOleTypeName
, pData
->dwOleTypeNameLength
);
6455 if(dwSize
!= pData
->dwOleTypeNameLength
)
6457 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6464 /* Set the width of the Metafile */
6465 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileWidth
), sizeof(pData
->dwMetaFileWidth
));
6466 if(dwSize
!= sizeof(pData
->dwMetaFileWidth
))
6468 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6474 /* Set the height of the Metafile */
6475 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwMetaFileHeight
), sizeof(pData
->dwMetaFileHeight
));
6476 if(dwSize
!= sizeof(pData
->dwMetaFileHeight
))
6478 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6484 /* Set the length of the Data */
6485 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *)&(pData
->dwDataLength
), sizeof(pData
->dwDataLength
));
6486 if(dwSize
!= sizeof(pData
->dwDataLength
))
6488 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6494 if(pData
->dwDataLength
> 0)
6496 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6497 dwSize
= pOleStream
->lpstbl
->Put(pOleStream
, (void *) pData
->pData
, pData
->dwDataLength
);
6498 if(dwSize
!= pData
->dwDataLength
)
6500 hRes
= CONVERT10_E_OLESTREAM_PUT
;
6508 /*************************************************************************
6509 * OLECONVERT_GetOLE20FromOLE10[Internal]
6511 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6512 * opens it, and copies the content to the dest IStorage for
6513 * OleConvertOLESTREAMToIStorage
6517 * pDestStorage [I] The IStorage to copy the data to
6518 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6519 * nBufferLength [I] The size of the buffer
6528 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage
, BYTE
*pBuffer
, DWORD nBufferLength
)
6532 IStorage
*pTempStorage
;
6533 DWORD dwNumOfBytesWritten
;
6534 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6535 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6537 /* Create a temp File */
6538 GetTempPathW(MAX_PATH
, wstrTempDir
);
6539 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6540 hFile
= CreateFileW(wstrTempFile
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0);
6542 if(hFile
!= INVALID_HANDLE_VALUE
)
6544 /* Write IStorage Data to File */
6545 WriteFile(hFile
, pBuffer
, nBufferLength
, &dwNumOfBytesWritten
, NULL
);
6548 /* Open and copy temp storage to the Dest Storage */
6549 hRes
= StgOpenStorage(wstrTempFile
, NULL
, STGM_READ
, NULL
, 0, &pTempStorage
);
6552 hRes
= StorageImpl_CopyTo(pTempStorage
, 0, NULL
, NULL
, pDestStorage
);
6553 StorageBaseImpl_Release(pTempStorage
);
6555 DeleteFileW(wstrTempFile
);
6560 /*************************************************************************
6561 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6563 * Saves the OLE10 STREAM From memory
6566 * pStorage [I] The Src IStorage to copy
6567 * pData [I] The Dest Memory to write to.
6570 * The size in bytes allocated for pData
6573 * Memory allocated for pData must be freed by the caller
6575 * Used by OleConvertIStorageToOLESTREAM only.
6578 DWORD
OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage
, BYTE
**pData
)
6582 DWORD nDataLength
= 0;
6583 IStorage
*pTempStorage
;
6584 WCHAR wstrTempDir
[MAX_PATH
], wstrTempFile
[MAX_PATH
];
6585 static const WCHAR wstrPrefix
[] = {'s', 'i', 's', 0};
6589 /* Create temp Storage */
6590 GetTempPathW(MAX_PATH
, wstrTempDir
);
6591 GetTempFileNameW(wstrTempDir
, wstrPrefix
, 0, wstrTempFile
);
6592 hRes
= StgCreateDocfile(wstrTempFile
, STGM_CREATE
| STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
, 0, &pTempStorage
);
6596 /* Copy Src Storage to the Temp Storage */
6597 StorageImpl_CopyTo(pStorage
, 0, NULL
, NULL
, pTempStorage
);
6598 StorageBaseImpl_Release(pTempStorage
);
6600 /* Open Temp Storage as a file and copy to memory */
6601 hFile
= CreateFileW(wstrTempFile
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
6602 if(hFile
!= INVALID_HANDLE_VALUE
)
6604 nDataLength
= GetFileSize(hFile
, NULL
);
6605 *pData
= HeapAlloc(GetProcessHeap(),0,nDataLength
);
6606 ReadFile(hFile
, *pData
, nDataLength
, &nDataLength
, 0);
6609 DeleteFileW(wstrTempFile
);
6614 /*************************************************************************
6615 * OLECONVERT_CreateOleStream [Internal]
6617 * Creates the "\001OLE" stream in the IStorage if necessary.
6620 * pStorage [I] Dest storage to create the stream in
6626 * This function is used by OleConvertOLESTREAMToIStorage only.
6628 * This stream is still unknown, MS Word seems to have extra data
6629 * but since the data is stored in the OLESTREAM there should be
6630 * no need to recreate the stream. If the stream is manually
6631 * deleted it will create it with this default data.
6634 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage
)
6638 static const WCHAR wstrStreamName
[] = {1,'O', 'l', 'e', 0};
6639 BYTE pOleStreamHeader
[] =
6641 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6642 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6643 0x00, 0x00, 0x00, 0x00
6646 /* Create stream if not present */
6647 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6648 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6652 /* Write default Data */
6653 hRes
= IStream_Write(pStream
, pOleStreamHeader
, sizeof(pOleStreamHeader
), NULL
);
6654 IStream_Release(pStream
);
6658 /* write a string to a stream, preceded by its length */
6659 static HRESULT
STREAM_WriteString( IStream
*stm
, LPCWSTR string
)
6666 len
= WideCharToMultiByte( CP_ACP
, 0, string
, -1, NULL
, 0, NULL
, NULL
);
6667 r
= IStream_Write( stm
, &len
, sizeof(len
), NULL
);
6672 str
= CoTaskMemAlloc( len
);
6673 WideCharToMultiByte( CP_ACP
, 0, string
, -1, str
, len
, NULL
, NULL
);
6674 r
= IStream_Write( stm
, str
, len
, NULL
);
6675 CoTaskMemFree( str
);
6679 /* read a string preceded by its length from a stream */
6680 static HRESULT
STREAM_ReadString( IStream
*stm
, LPWSTR
*string
)
6683 DWORD len
, count
= 0;
6687 r
= IStream_Read( stm
, &len
, sizeof(len
), &count
);
6690 if( count
!= sizeof(len
) )
6691 return E_OUTOFMEMORY
;
6693 TRACE("%ld bytes\n",len
);
6695 str
= CoTaskMemAlloc( len
);
6697 return E_OUTOFMEMORY
;
6699 r
= IStream_Read( stm
, str
, len
, &count
);
6704 CoTaskMemFree( str
);
6705 return E_OUTOFMEMORY
;
6708 TRACE("Read string %s\n",debugstr_an(str
,len
));
6710 len
= MultiByteToWideChar( CP_ACP
, 0, str
, count
, NULL
, 0 );
6711 wstr
= CoTaskMemAlloc( (len
+ 1)*sizeof (WCHAR
) );
6713 MultiByteToWideChar( CP_ACP
, 0, str
, count
, wstr
, len
);
6714 CoTaskMemFree( str
);
6722 static HRESULT
STORAGE_WriteCompObj( LPSTORAGE pstg
, CLSID
*clsid
,
6723 LPCWSTR lpszUserType
, LPCWSTR szClipName
, LPCWSTR szProgIDName
)
6727 static const WCHAR szwStreamName
[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6729 static const BYTE unknown1
[12] =
6730 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6731 0xFF, 0xFF, 0xFF, 0xFF};
6732 static const BYTE unknown2
[16] =
6733 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6734 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6736 TRACE("%p %s %s %s %s\n", pstg
, debugstr_guid(clsid
),
6737 debugstr_w(lpszUserType
), debugstr_w(szClipName
),
6738 debugstr_w(szProgIDName
));
6740 /* Create a CompObj stream if it doesn't exist */
6741 r
= IStorage_CreateStream(pstg
, szwStreamName
,
6742 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pstm
);
6746 /* Write CompObj Structure to stream */
6747 r
= IStream_Write(pstm
, unknown1
, sizeof(unknown1
), NULL
);
6749 if( SUCCEEDED( r
) )
6750 r
= WriteClassStm( pstm
, clsid
);
6752 if( SUCCEEDED( r
) )
6753 r
= STREAM_WriteString( pstm
, lpszUserType
);
6754 if( SUCCEEDED( r
) )
6755 r
= STREAM_WriteString( pstm
, szClipName
);
6756 if( SUCCEEDED( r
) )
6757 r
= STREAM_WriteString( pstm
, szProgIDName
);
6758 if( SUCCEEDED( r
) )
6759 r
= IStream_Write(pstm
, unknown2
, sizeof(unknown2
), NULL
);
6761 IStream_Release( pstm
);
6766 /***********************************************************************
6767 * WriteFmtUserTypeStg (OLE32.@)
6769 HRESULT WINAPI
WriteFmtUserTypeStg(
6770 LPSTORAGE pstg
, CLIPFORMAT cf
, LPOLESTR lpszUserType
)
6773 WCHAR szwClipName
[0x40];
6774 CLSID clsid
= CLSID_NULL
;
6775 LPWSTR wstrProgID
= NULL
;
6778 TRACE("(%p,%x,%s)\n",pstg
,cf
,debugstr_w(lpszUserType
));
6780 /* get the clipboard format name */
6781 n
= GetClipboardFormatNameW( cf
, szwClipName
, sizeof(szwClipName
) );
6784 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName
));
6786 /* FIXME: There's room to save a CLSID and its ProgID, but
6787 the CLSID is not looked up in the registry and in all the
6788 tests I wrote it was CLSID_NULL. Where does it come from?
6791 /* get the real program ID. This may fail, but that's fine */
6792 ProgIDFromCLSID(&clsid
, &wstrProgID
);
6794 TRACE("progid is %s\n",debugstr_w(wstrProgID
));
6796 r
= STORAGE_WriteCompObj( pstg
, &clsid
,
6797 lpszUserType
, szwClipName
, wstrProgID
);
6799 CoTaskMemFree(wstrProgID
);
6805 /******************************************************************************
6806 * ReadFmtUserTypeStg [OLE32.@]
6808 HRESULT WINAPI
ReadFmtUserTypeStg (LPSTORAGE pstg
, CLIPFORMAT
* pcf
, LPOLESTR
* lplpszUserType
)
6812 static const WCHAR szCompObj
[] = { 1, 'C','o','m','p','O','b','j', 0 };
6813 unsigned char unknown1
[12];
6814 unsigned char unknown2
[16];
6816 LPWSTR szProgIDName
= NULL
, szCLSIDName
= NULL
, szOleTypeName
= NULL
;
6819 TRACE("(%p,%p,%p)\n", pstg
, pcf
, lplpszUserType
);
6821 r
= IStorage_OpenStream( pstg
, szCompObj
, NULL
,
6822 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
6825 WARN("Failed to open stream r = %08lx\n", r
);
6829 /* read the various parts of the structure */
6830 r
= IStream_Read( stm
, unknown1
, sizeof(unknown1
), &count
);
6831 if( FAILED( r
) || ( count
!= sizeof(unknown1
) ) )
6833 r
= ReadClassStm( stm
, &clsid
);
6837 r
= STREAM_ReadString( stm
, &szCLSIDName
);
6841 r
= STREAM_ReadString( stm
, &szOleTypeName
);
6845 r
= STREAM_ReadString( stm
, &szProgIDName
);
6849 r
= IStream_Read( stm
, unknown2
, sizeof(unknown2
), &count
);
6850 if( FAILED( r
) || ( count
!= sizeof(unknown2
) ) )
6853 /* ok, success... now we just need to store what we found */
6855 *pcf
= RegisterClipboardFormatW( szOleTypeName
);
6856 CoTaskMemFree( szOleTypeName
);
6858 if( lplpszUserType
)
6859 *lplpszUserType
= szCLSIDName
;
6860 CoTaskMemFree( szProgIDName
);
6863 IStream_Release( stm
);
6869 /*************************************************************************
6870 * OLECONVERT_CreateCompObjStream [Internal]
6872 * Creates a "\001CompObj" is the destination IStorage if necessary.
6875 * pStorage [I] The dest IStorage to create the CompObj Stream
6877 * strOleTypeName [I] The ProgID
6881 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6884 * This function is used by OleConvertOLESTREAMToIStorage only.
6886 * The stream data is stored in the OLESTREAM and there should be
6887 * no need to recreate the stream. If the stream is manually
6888 * deleted it will attempt to create it by querying the registry.
6892 HRESULT
OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage
, LPCSTR strOleTypeName
)
6895 HRESULT hStorageRes
, hRes
= S_OK
;
6896 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj
;
6897 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6898 WCHAR bufferW
[OLESTREAM_MAX_STR_LEN
];
6900 BYTE pCompObjUnknown1
[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6901 BYTE pCompObjUnknown2
[] = {0xF4, 0x39, 0xB2, 0x71};
6903 /* Initialize the CompObj structure */
6904 memset(&IStorageCompObj
, 0, sizeof(IStorageCompObj
));
6905 memcpy(&(IStorageCompObj
.byUnknown1
), pCompObjUnknown1
, sizeof(pCompObjUnknown1
));
6906 memcpy(&(IStorageCompObj
.byUnknown2
), pCompObjUnknown2
, sizeof(pCompObjUnknown2
));
6909 /* Create a CompObj stream if it doesn't exist */
6910 hStorageRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
6911 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
6912 if(hStorageRes
== S_OK
)
6914 /* copy the OleTypeName to the compobj struct */
6915 IStorageCompObj
.dwOleTypeNameLength
= strlen(strOleTypeName
)+1;
6916 strcpy(IStorageCompObj
.strOleTypeName
, strOleTypeName
);
6918 /* copy the OleTypeName to the compobj struct */
6919 /* Note: in the test made, these were Identical */
6920 IStorageCompObj
.dwProgIDNameLength
= strlen(strOleTypeName
)+1;
6921 strcpy(IStorageCompObj
.strProgIDName
, strOleTypeName
);
6924 MultiByteToWideChar( CP_ACP
, 0, IStorageCompObj
.strProgIDName
, -1,
6925 bufferW
, OLESTREAM_MAX_STR_LEN
);
6926 hRes
= CLSIDFromProgID(bufferW
, &(IStorageCompObj
.clsid
));
6932 /* Get the CLSID Default Name from the Registry */
6933 hErr
= RegOpenKeyA(HKEY_CLASSES_ROOT
, IStorageCompObj
.strProgIDName
, &hKey
);
6934 if(hErr
== ERROR_SUCCESS
)
6936 char strTemp
[OLESTREAM_MAX_STR_LEN
];
6937 IStorageCompObj
.dwCLSIDNameLength
= OLESTREAM_MAX_STR_LEN
;
6938 hErr
= RegQueryValueA(hKey
, NULL
, strTemp
, &(IStorageCompObj
.dwCLSIDNameLength
));
6939 if(hErr
== ERROR_SUCCESS
)
6941 strcpy(IStorageCompObj
.strCLSIDName
, strTemp
);
6947 /* Write CompObj Structure to stream */
6948 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown1
, sizeof(IStorageCompObj
.byUnknown1
), NULL
);
6950 WriteClassStm(pStream
,&(IStorageCompObj
.clsid
));
6952 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwCLSIDNameLength
), sizeof(IStorageCompObj
.dwCLSIDNameLength
), NULL
);
6953 if(IStorageCompObj
.dwCLSIDNameLength
> 0)
6955 hRes
= IStream_Write(pStream
, IStorageCompObj
.strCLSIDName
, IStorageCompObj
.dwCLSIDNameLength
, NULL
);
6957 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwOleTypeNameLength
) , sizeof(IStorageCompObj
.dwOleTypeNameLength
), NULL
);
6958 if(IStorageCompObj
.dwOleTypeNameLength
> 0)
6960 hRes
= IStream_Write(pStream
, IStorageCompObj
.strOleTypeName
, IStorageCompObj
.dwOleTypeNameLength
, NULL
);
6962 hRes
= IStream_Write(pStream
, &(IStorageCompObj
.dwProgIDNameLength
) , sizeof(IStorageCompObj
.dwProgIDNameLength
), NULL
);
6963 if(IStorageCompObj
.dwProgIDNameLength
> 0)
6965 hRes
= IStream_Write(pStream
, IStorageCompObj
.strProgIDName
, IStorageCompObj
.dwProgIDNameLength
, NULL
);
6967 hRes
= IStream_Write(pStream
, IStorageCompObj
.byUnknown2
, sizeof(IStorageCompObj
.byUnknown2
), NULL
);
6968 IStream_Release(pStream
);
6974 /*************************************************************************
6975 * OLECONVERT_CreateOlePresStream[Internal]
6977 * Creates the "\002OlePres000" Stream with the Metafile data
6980 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6981 * dwExtentX [I] Width of the Metafile
6982 * dwExtentY [I] Height of the Metafile
6983 * pData [I] Metafile data
6984 * dwDataLength [I] Size of the Metafile data
6988 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6991 * This function is used by OleConvertOLESTREAMToIStorage only.
6994 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage
, DWORD dwExtentX
, DWORD dwExtentY
, BYTE
*pData
, DWORD dwDataLength
)
6998 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6999 BYTE pOlePresStreamHeader
[] =
7001 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7002 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7003 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7004 0x00, 0x00, 0x00, 0x00
7007 BYTE pOlePresStreamHeaderEmpty
[] =
7009 0x00, 0x00, 0x00, 0x00,
7010 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7011 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7012 0x00, 0x00, 0x00, 0x00
7015 /* Create the OlePres000 Stream */
7016 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7017 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7022 OLECONVERT_ISTORAGE_OLEPRES OlePres
;
7024 memset(&OlePres
, 0, sizeof(OlePres
));
7025 /* Do we have any metafile data to save */
7026 if(dwDataLength
> 0)
7028 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeader
, sizeof(pOlePresStreamHeader
));
7029 nHeaderSize
= sizeof(pOlePresStreamHeader
);
7033 memcpy(OlePres
.byUnknown1
, pOlePresStreamHeaderEmpty
, sizeof(pOlePresStreamHeaderEmpty
));
7034 nHeaderSize
= sizeof(pOlePresStreamHeaderEmpty
);
7036 /* Set width and height of the metafile */
7037 OlePres
.dwExtentX
= dwExtentX
;
7038 OlePres
.dwExtentY
= -dwExtentY
;
7040 /* Set Data and Length */
7041 if(dwDataLength
> sizeof(METAFILEPICT16
))
7043 OlePres
.dwSize
= dwDataLength
- sizeof(METAFILEPICT16
);
7044 OlePres
.pData
= &(pData
[8]);
7046 /* Save OlePres000 Data to Stream */
7047 hRes
= IStream_Write(pStream
, OlePres
.byUnknown1
, nHeaderSize
, NULL
);
7048 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentX
), sizeof(OlePres
.dwExtentX
), NULL
);
7049 hRes
= IStream_Write(pStream
, &(OlePres
.dwExtentY
), sizeof(OlePres
.dwExtentY
), NULL
);
7050 hRes
= IStream_Write(pStream
, &(OlePres
.dwSize
), sizeof(OlePres
.dwSize
), NULL
);
7051 if(OlePres
.dwSize
> 0)
7053 hRes
= IStream_Write(pStream
, OlePres
.pData
, OlePres
.dwSize
, NULL
);
7055 IStream_Release(pStream
);
7059 /*************************************************************************
7060 * OLECONVERT_CreateOle10NativeStream [Internal]
7062 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7065 * pStorage [I] Dest storage to create the stream in
7066 * pData [I] Ole10 Native Data (ex. bmp)
7067 * dwDataLength [I] Size of the Ole10 Native Data
7073 * This function is used by OleConvertOLESTREAMToIStorage only.
7075 * Might need to verify the data and return appropriate error message
7078 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage
, BYTE
*pData
, DWORD dwDataLength
)
7082 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7084 /* Create the Ole10Native Stream */
7085 hRes
= IStorage_CreateStream(pStorage
, wstrStreamName
,
7086 STGM_CREATE
| STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &pStream
);
7090 /* Write info to stream */
7091 hRes
= IStream_Write(pStream
, &dwDataLength
, sizeof(dwDataLength
), NULL
);
7092 hRes
= IStream_Write(pStream
, pData
, dwDataLength
, NULL
);
7093 IStream_Release(pStream
);
7098 /*************************************************************************
7099 * OLECONVERT_GetOLE10ProgID [Internal]
7101 * Finds the ProgID (or OleTypeID) from the IStorage
7104 * pStorage [I] The Src IStorage to get the ProgID
7105 * strProgID [I] the ProgID string to get
7106 * dwSize [I] the size of the string
7110 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7113 * This function is used by OleConvertIStorageToOLESTREAM only.
7117 HRESULT
OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage
, char *strProgID
, DWORD
*dwSize
)
7121 LARGE_INTEGER iSeekPos
;
7122 OLECONVERT_ISTORAGE_COMPOBJ CompObj
;
7123 static const WCHAR wstrStreamName
[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7125 /* Open the CompObj Stream */
7126 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7127 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7131 /*Get the OleType from the CompObj Stream */
7132 iSeekPos
.u
.LowPart
= sizeof(CompObj
.byUnknown1
) + sizeof(CompObj
.clsid
);
7133 iSeekPos
.u
.HighPart
= 0;
7135 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7136 IStream_Read(pStream
, &CompObj
.dwCLSIDNameLength
, sizeof(CompObj
.dwCLSIDNameLength
), NULL
);
7137 iSeekPos
.u
.LowPart
= CompObj
.dwCLSIDNameLength
;
7138 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7139 IStream_Read(pStream
, &CompObj
.dwOleTypeNameLength
, sizeof(CompObj
.dwOleTypeNameLength
), NULL
);
7140 iSeekPos
.u
.LowPart
= CompObj
.dwOleTypeNameLength
;
7141 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_CUR
, NULL
);
7143 IStream_Read(pStream
, dwSize
, sizeof(*dwSize
), NULL
);
7146 IStream_Read(pStream
, strProgID
, *dwSize
, NULL
);
7148 IStream_Release(pStream
);
7153 LPOLESTR wstrProgID
;
7155 /* Get the OleType from the registry */
7156 REFCLSID clsid
= &(stat
.clsid
);
7157 IStorage_Stat(pStorage
, &stat
, STATFLAG_NONAME
);
7158 hRes
= ProgIDFromCLSID(clsid
, &wstrProgID
);
7161 *dwSize
= WideCharToMultiByte(CP_ACP
, 0, wstrProgID
, -1, strProgID
, *dwSize
, NULL
, FALSE
);
7168 /*************************************************************************
7169 * OLECONVERT_GetOle10PresData [Internal]
7171 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7174 * pStorage [I] Src IStroage
7175 * pOleStream [I] Dest OleStream Mem Struct
7181 * This function is used by OleConvertIStorageToOLESTREAM only.
7183 * Memory allocated for pData must be freed by the caller
7187 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7192 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7194 /* Initialize Default data for OLESTREAM */
7195 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7196 pOleStreamData
[0].dwTypeID
= 2;
7197 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7198 pOleStreamData
[1].dwTypeID
= 0;
7199 pOleStreamData
[0].dwMetaFileWidth
= 0;
7200 pOleStreamData
[0].dwMetaFileHeight
= 0;
7201 pOleStreamData
[0].pData
= NULL
;
7202 pOleStreamData
[1].pData
= NULL
;
7204 /* Open Ole10Native Stream */
7205 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7206 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7210 /* Read Size and Data */
7211 IStream_Read(pStream
, &(pOleStreamData
->dwDataLength
), sizeof(pOleStreamData
->dwDataLength
), NULL
);
7212 if(pOleStreamData
->dwDataLength
> 0)
7214 pOleStreamData
->pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
->dwDataLength
);
7215 IStream_Read(pStream
, pOleStreamData
->pData
, pOleStreamData
->dwDataLength
, NULL
);
7217 IStream_Release(pStream
);
7223 /*************************************************************************
7224 * OLECONVERT_GetOle20PresData[Internal]
7226 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7229 * pStorage [I] Src IStroage
7230 * pOleStreamData [I] Dest OleStream Mem Struct
7236 * This function is used by OleConvertIStorageToOLESTREAM only.
7238 * Memory allocated for pData must be freed by the caller
7240 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage
, OLECONVERT_OLESTREAM_DATA
*pOleStreamData
)
7244 OLECONVERT_ISTORAGE_OLEPRES olePress
;
7245 static const WCHAR wstrStreamName
[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7247 /* Initialize Default data for OLESTREAM */
7248 pOleStreamData
[0].dwOleID
= OLESTREAM_ID
;
7249 pOleStreamData
[0].dwTypeID
= 2;
7250 pOleStreamData
[0].dwMetaFileWidth
= 0;
7251 pOleStreamData
[0].dwMetaFileHeight
= 0;
7252 pOleStreamData
[0].dwDataLength
= OLECONVERT_WriteOLE20ToBuffer(pStorage
, &(pOleStreamData
[0].pData
));
7253 pOleStreamData
[1].dwOleID
= OLESTREAM_ID
;
7254 pOleStreamData
[1].dwTypeID
= 0;
7255 pOleStreamData
[1].dwOleTypeNameLength
= 0;
7256 pOleStreamData
[1].strOleTypeName
[0] = 0;
7257 pOleStreamData
[1].dwMetaFileWidth
= 0;
7258 pOleStreamData
[1].dwMetaFileHeight
= 0;
7259 pOleStreamData
[1].pData
= NULL
;
7260 pOleStreamData
[1].dwDataLength
= 0;
7263 /* Open OlePress000 stream */
7264 hRes
= IStorage_OpenStream(pStorage
, wstrStreamName
, NULL
,
7265 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7268 LARGE_INTEGER iSeekPos
;
7269 METAFILEPICT16 MetaFilePict
;
7270 static const char strMetafilePictName
[] = "METAFILEPICT";
7272 /* Set the TypeID for a Metafile */
7273 pOleStreamData
[1].dwTypeID
= 5;
7275 /* Set the OleTypeName to Metafile */
7276 pOleStreamData
[1].dwOleTypeNameLength
= strlen(strMetafilePictName
) +1;
7277 strcpy(pOleStreamData
[1].strOleTypeName
, strMetafilePictName
);
7279 iSeekPos
.u
.HighPart
= 0;
7280 iSeekPos
.u
.LowPart
= sizeof(olePress
.byUnknown1
);
7282 /* Get Presentation Data */
7283 IStream_Seek(pStream
, iSeekPos
, STREAM_SEEK_SET
, NULL
);
7284 IStream_Read(pStream
, &(olePress
.dwExtentX
), sizeof(olePress
.dwExtentX
), NULL
);
7285 IStream_Read(pStream
, &(olePress
.dwExtentY
), sizeof(olePress
.dwExtentY
), NULL
);
7286 IStream_Read(pStream
, &(olePress
.dwSize
), sizeof(olePress
.dwSize
), NULL
);
7288 /*Set width and Height */
7289 pOleStreamData
[1].dwMetaFileWidth
= olePress
.dwExtentX
;
7290 pOleStreamData
[1].dwMetaFileHeight
= -olePress
.dwExtentY
;
7291 if(olePress
.dwSize
> 0)
7294 pOleStreamData
[1].dwDataLength
= olePress
.dwSize
+ sizeof(METAFILEPICT16
);
7296 /* Set MetaFilePict struct */
7297 MetaFilePict
.mm
= 8;
7298 MetaFilePict
.xExt
= olePress
.dwExtentX
;
7299 MetaFilePict
.yExt
= olePress
.dwExtentY
;
7300 MetaFilePict
.hMF
= 0;
7302 /* Get Metafile Data */
7303 pOleStreamData
[1].pData
= HeapAlloc(GetProcessHeap(),0,pOleStreamData
[1].dwDataLength
);
7304 memcpy(pOleStreamData
[1].pData
, &MetaFilePict
, sizeof(MetaFilePict
));
7305 IStream_Read(pStream
, &(pOleStreamData
[1].pData
[sizeof(MetaFilePict
)]), pOleStreamData
[1].dwDataLength
-sizeof(METAFILEPICT16
), NULL
);
7307 IStream_Release(pStream
);
7311 /*************************************************************************
7312 * OleConvertOLESTREAMToIStorage [OLE32.@]
7317 * DVTARGETDEVICE paramenter is not handled
7318 * Still unsure of some mem fields for OLE 10 Stream
7319 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7320 * and "\001OLE" streams
7323 HRESULT WINAPI
OleConvertOLESTREAMToIStorage (
7324 LPOLESTREAM pOleStream
,
7326 const DVTARGETDEVICE
* ptd
)
7330 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7332 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7336 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7339 if(pstg
== NULL
|| pOleStream
== NULL
)
7341 hRes
= E_INVALIDARG
;
7346 /* Load the OLESTREAM to Memory */
7347 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[0], TRUE
);
7352 /* Load the OLESTREAM to Memory (part 2)*/
7353 hRes
= OLECONVERT_LoadOLE10(pOleStream
, &pOleStreamData
[1], FALSE
);
7359 if(pOleStreamData
[0].dwDataLength
> sizeof(STORAGE_magic
))
7361 /* Do we have the IStorage Data in the OLESTREAM */
7362 if(memcmp(pOleStreamData
[0].pData
, STORAGE_magic
, sizeof(STORAGE_magic
)) ==0)
7364 OLECONVERT_GetOLE20FromOLE10(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7365 OLECONVERT_CreateOlePresStream(pstg
, pOleStreamData
[1].dwMetaFileWidth
, pOleStreamData
[1].dwMetaFileHeight
, pOleStreamData
[1].pData
, pOleStreamData
[1].dwDataLength
);
7369 /* It must be an original OLE 1.0 source */
7370 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7375 /* It must be an original OLE 1.0 source */
7376 OLECONVERT_CreateOle10NativeStream(pstg
, pOleStreamData
[0].pData
, pOleStreamData
[0].dwDataLength
);
7379 /* Create CompObj Stream if necessary */
7380 hRes
= OLECONVERT_CreateCompObjStream(pstg
, pOleStreamData
[0].strOleTypeName
);
7383 /*Create the Ole Stream if necessary */
7384 OLECONVERT_CreateOleStream(pstg
);
7389 /* Free allocated memory */
7390 for(i
=0; i
< 2; i
++)
7392 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7393 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pstrOleObjFileName
);
7394 pOleStreamData
[i
].pstrOleObjFileName
= NULL
;
7399 /*************************************************************************
7400 * OleConvertIStorageToOLESTREAM [OLE32.@]
7407 * Still unsure of some mem fields for OLE 10 Stream
7408 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7409 * and "\001OLE" streams.
7412 HRESULT WINAPI
OleConvertIStorageToOLESTREAM (
7414 LPOLESTREAM pOleStream
)
7417 HRESULT hRes
= S_OK
;
7419 OLECONVERT_OLESTREAM_DATA pOleStreamData
[2];
7420 static const WCHAR wstrStreamName
[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7423 memset(pOleStreamData
, 0, sizeof(pOleStreamData
));
7425 if(pstg
== NULL
|| pOleStream
== NULL
)
7427 hRes
= E_INVALIDARG
;
7431 /* Get the ProgID */
7432 pOleStreamData
[0].dwOleTypeNameLength
= OLESTREAM_MAX_STR_LEN
;
7433 hRes
= OLECONVERT_GetOLE10ProgID(pstg
, pOleStreamData
[0].strOleTypeName
, &(pOleStreamData
[0].dwOleTypeNameLength
));
7437 /* Was it originally Ole10 */
7438 hRes
= IStorage_OpenStream(pstg
, wstrStreamName
, 0, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &pStream
);
7441 IStream_Release(pStream
);
7442 /* Get Presentation Data for Ole10Native */
7443 OLECONVERT_GetOle10PresData(pstg
, pOleStreamData
);
7447 /* Get Presentation Data (OLE20) */
7448 OLECONVERT_GetOle20PresData(pstg
, pOleStreamData
);
7451 /* Save OLESTREAM */
7452 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[0]), pOleStream
);
7455 hRes
= OLECONVERT_SaveOLE10(&(pOleStreamData
[1]), pOleStream
);
7460 /* Free allocated memory */
7461 for(i
=0; i
< 2; i
++)
7463 HeapFree(GetProcessHeap(),0,pOleStreamData
[i
].pData
);
7469 /***********************************************************************
7470 * GetConvertStg (OLE32.@)
7472 HRESULT WINAPI
GetConvertStg(IStorage
*stg
) {
7473 FIXME("unimplemented stub!\n");
7477 /******************************************************************************
7478 * StgIsStorageFile [OLE32.@]
7481 StgIsStorageFile(LPCOLESTR fn
)
7487 TRACE("(\'%s\')\n", debugstr_w(fn
));
7488 hf
= CreateFileW(fn
, GENERIC_READ
,
7489 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
7490 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0);
7492 if (hf
== INVALID_HANDLE_VALUE
)
7493 return STG_E_FILENOTFOUND
;
7495 if (!ReadFile(hf
, magic
, 8, &bytes_read
, NULL
))
7497 WARN(" unable to read file\n");
7504 if (bytes_read
!= 8) {
7505 WARN(" too short\n");
7509 if (!memcmp(magic
,STORAGE_magic
,8)) {
7514 WARN(" -> Invalid header.\n");