Add support for HCBT_SYSCOMMAND hook, add logging for HCBT_SYSCOMMAND
[wine.git] / dlls / ole32 / storage32.c
blob9d01a686d4b6ae8346a2c24d9030fd6969b0ecdc
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <assert.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winnls.h"
38 #include "winuser.h"
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
42 #include "storage32.h"
43 #include "ole2.h" /* For Write/ReadClassStm */
45 #include "winreg.h"
46 #include "wine/wingdi16.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(storage);
50 #define FILE_BEGIN 0
53 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
54 #define OLESTREAM_ID 0x501
55 #define OLESTREAM_MAX_STR_LEN 255
57 static const char rootPropertyName[] = "Root Entry";
60 /* OLESTREAM memory structure to use for Get and Put Routines */
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 typedef struct
64 DWORD dwOleID;
65 DWORD dwTypeID;
66 DWORD dwOleTypeNameLength;
67 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
68 CHAR *pstrOleObjFileName;
69 DWORD dwOleObjFileNameLength;
70 DWORD dwMetaFileWidth;
71 DWORD dwMetaFileHeight;
72 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
73 DWORD dwDataLength;
74 BYTE *pData;
75 }OLECONVERT_OLESTREAM_DATA;
77 /* CompObj Stream structure */
78 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
79 typedef struct
81 BYTE byUnknown1[12];
82 CLSID clsid;
83 DWORD dwCLSIDNameLength;
84 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
85 DWORD dwOleTypeNameLength;
86 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
87 DWORD dwProgIDNameLength;
88 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
89 BYTE byUnknown2[16];
90 }OLECONVERT_ISTORAGE_COMPOBJ;
93 /* Ole Presention Stream structure */
94 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
95 typedef struct
97 BYTE byUnknown1[28];
98 DWORD dwExtentX;
99 DWORD dwExtentY;
100 DWORD dwSize;
101 BYTE *pData;
102 }OLECONVERT_ISTORAGE_OLEPRES;
106 /***********************************************************************
107 * Forward declaration of internal functions used by the method DestroyElement
109 static HRESULT deleteStorageProperty(
110 StorageImpl *parentStorage,
111 ULONG foundPropertyIndexToDelete,
112 StgProperty propertyToDelete);
114 static HRESULT deleteStreamProperty(
115 StorageImpl *parentStorage,
116 ULONG foundPropertyIndexToDelete,
117 StgProperty propertyToDelete);
119 static HRESULT findPlaceholder(
120 StorageImpl *storage,
121 ULONG propertyIndexToStore,
122 ULONG storagePropertyIndex,
123 INT typeOfRelation);
125 static HRESULT adjustPropertyChain(
126 StorageImpl *This,
127 StgProperty propertyToDelete,
128 StgProperty parentProperty,
129 ULONG parentPropertyId,
130 INT typeOfRelation);
132 /***********************************************************************
133 * Declaration of the functions used to manipulate StgProperty
136 static ULONG getFreeProperty(
137 StorageImpl *storage);
139 static void updatePropertyChain(
140 StorageImpl *storage,
141 ULONG newPropertyIndex,
142 StgProperty newProperty);
144 static LONG propertyNameCmp(
145 OLECHAR *newProperty,
146 OLECHAR *currentProperty);
149 /***********************************************************************
150 * Declaration of miscellaneous functions...
152 static HRESULT validateSTGM(DWORD stgmValue);
154 static DWORD GetShareModeFromSTGM(DWORD stgm);
155 static DWORD GetAccessModeFromSTGM(DWORD stgm);
156 static DWORD GetCreationModeFromSTGM(DWORD stgm);
159 * Virtual function table for the IStorage32Impl class.
161 static IStorageVtbl Storage32Impl_Vtbl =
163 StorageBaseImpl_QueryInterface,
164 StorageBaseImpl_AddRef,
165 StorageBaseImpl_Release,
166 StorageBaseImpl_CreateStream,
167 StorageBaseImpl_OpenStream,
168 StorageImpl_CreateStorage,
169 StorageBaseImpl_OpenStorage,
170 StorageImpl_CopyTo,
171 StorageImpl_MoveElementTo,
172 StorageImpl_Commit,
173 StorageImpl_Revert,
174 StorageBaseImpl_EnumElements,
175 StorageImpl_DestroyElement,
176 StorageBaseImpl_RenameElement,
177 StorageImpl_SetElementTimes,
178 StorageBaseImpl_SetClass,
179 StorageImpl_SetStateBits,
180 StorageImpl_Stat
184 * Virtual function table for the Storage32InternalImpl class.
186 static IStorageVtbl Storage32InternalImpl_Vtbl =
188 StorageBaseImpl_QueryInterface,
189 StorageBaseImpl_AddRef,
190 StorageBaseImpl_Release,
191 StorageBaseImpl_CreateStream,
192 StorageBaseImpl_OpenStream,
193 StorageImpl_CreateStorage,
194 StorageBaseImpl_OpenStorage,
195 StorageImpl_CopyTo,
196 StorageImpl_MoveElementTo,
197 StorageInternalImpl_Commit,
198 StorageInternalImpl_Revert,
199 StorageBaseImpl_EnumElements,
200 StorageImpl_DestroyElement,
201 StorageBaseImpl_RenameElement,
202 StorageImpl_SetElementTimes,
203 StorageBaseImpl_SetClass,
204 StorageImpl_SetStateBits,
205 StorageBaseImpl_Stat
209 * Virtual function table for the IEnumSTATSTGImpl class.
211 static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
213 IEnumSTATSTGImpl_QueryInterface,
214 IEnumSTATSTGImpl_AddRef,
215 IEnumSTATSTGImpl_Release,
216 IEnumSTATSTGImpl_Next,
217 IEnumSTATSTGImpl_Skip,
218 IEnumSTATSTGImpl_Reset,
219 IEnumSTATSTGImpl_Clone
226 /************************************************************************
227 ** Storage32BaseImpl implementatiion
230 /************************************************************************
231 * Storage32BaseImpl_QueryInterface (IUnknown)
233 * This method implements the common QueryInterface for all IStorage32
234 * implementations contained in this file.
236 * See Windows documentation for more details on IUnknown methods.
238 HRESULT WINAPI StorageBaseImpl_QueryInterface(
239 IStorage* iface,
240 REFIID riid,
241 void** ppvObject)
243 StorageBaseImpl *This = (StorageBaseImpl *)iface;
245 * Perform a sanity check on the parameters.
247 if ( (This==0) || (ppvObject==0) )
248 return E_INVALIDARG;
251 * Initialize the return parameter.
253 *ppvObject = 0;
256 * Compare the riid with the interface IDs implemented by this object.
258 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
260 *ppvObject = (IStorage*)This;
262 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
264 *ppvObject = (IStorage*)This;
268 * Check that we obtained an interface.
270 if ((*ppvObject)==0)
271 return E_NOINTERFACE;
274 * Query Interface always increases the reference count by one when it is
275 * successful
277 StorageBaseImpl_AddRef(iface);
279 return S_OK;
282 /************************************************************************
283 * Storage32BaseImpl_AddRef (IUnknown)
285 * This method implements the common AddRef for all IStorage32
286 * implementations contained in this file.
288 * See Windows documentation for more details on IUnknown methods.
290 ULONG WINAPI StorageBaseImpl_AddRef(
291 IStorage* iface)
293 StorageBaseImpl *This = (StorageBaseImpl *)iface;
294 This->ref++;
296 return This->ref;
299 /************************************************************************
300 * Storage32BaseImpl_Release (IUnknown)
302 * This method implements the common Release for all IStorage32
303 * implementations contained in this file.
305 * See Windows documentation for more details on IUnknown methods.
307 ULONG WINAPI StorageBaseImpl_Release(
308 IStorage* iface)
310 StorageBaseImpl *This = (StorageBaseImpl *)iface;
312 * Decrease the reference count on this object.
314 This->ref--;
317 * If the reference count goes down to 0, perform suicide.
319 if (This->ref==0)
322 * Since we are using a system of base-classes, we want to call the
323 * destructor of the appropriate derived class. To do this, we are
324 * using virtual functions to implement the destructor.
326 This->v_destructor(This);
328 return 0;
331 return This->ref;
334 /************************************************************************
335 * Storage32BaseImpl_OpenStream (IStorage)
337 * This method will open the specified stream object from the current storage.
339 * See Windows documentation for more details on IStorage methods.
341 HRESULT WINAPI StorageBaseImpl_OpenStream(
342 IStorage* iface,
343 const OLECHAR* pwcsName, /* [string][in] */
344 void* reserved1, /* [unique][in] */
345 DWORD grfMode, /* [in] */
346 DWORD reserved2, /* [in] */
347 IStream** ppstm) /* [out] */
349 StorageBaseImpl *This = (StorageBaseImpl *)iface;
350 IEnumSTATSTGImpl* propertyEnumeration;
351 StgStreamImpl* newStream;
352 StgProperty currentProperty;
353 ULONG foundPropertyIndex;
354 HRESULT res = STG_E_UNKNOWN;
356 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
357 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
360 * Perform a sanity check on the parameters.
362 if ( (pwcsName==NULL) || (ppstm==0) )
364 res = E_INVALIDARG;
365 goto end;
369 * Initialize the out parameter
371 *ppstm = NULL;
374 * Validate the STGM flags
376 if ( FAILED( validateSTGM(grfMode) ))
378 res = STG_E_INVALIDFLAG;
379 goto end;
383 * As documented.
385 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
386 (grfMode & STGM_DELETEONRELEASE) ||
387 (grfMode & STGM_TRANSACTED) )
389 res = STG_E_INVALIDFUNCTION;
390 goto end;
394 * Create a property enumeration to search the properties
396 propertyEnumeration = IEnumSTATSTGImpl_Construct(
397 This->ancestorStorage,
398 This->rootPropertySetIndex);
401 * Search the enumeration for the property with the given name
403 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
404 propertyEnumeration,
405 pwcsName,
406 &currentProperty);
409 * Delete the property enumeration since we don't need it anymore
411 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
414 * If it was found, construct the stream object and return a pointer to it.
416 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
417 (currentProperty.propertyType==PROPTYPE_STREAM) )
419 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
421 if (newStream!=0)
423 newStream->grfMode = grfMode;
424 *ppstm = (IStream*)newStream;
427 * Since we are returning a pointer to the interface, we have to
428 * nail down the reference.
430 StgStreamImpl_AddRef(*ppstm);
432 res = S_OK;
433 goto end;
436 res = E_OUTOFMEMORY;
437 goto end;
440 res = STG_E_FILENOTFOUND;
442 end:
443 if (res == S_OK)
444 TRACE("<-- IStream %p\n", *ppstm);
445 TRACE("<-- %08lx\n", res);
446 return res;
449 /************************************************************************
450 * Storage32BaseImpl_OpenStorage (IStorage)
452 * This method will open a new storage object from the current storage.
454 * See Windows documentation for more details on IStorage methods.
456 HRESULT WINAPI StorageBaseImpl_OpenStorage(
457 IStorage* iface,
458 const OLECHAR* pwcsName, /* [string][unique][in] */
459 IStorage* pstgPriority, /* [unique][in] */
460 DWORD grfMode, /* [in] */
461 SNB snbExclude, /* [unique][in] */
462 DWORD reserved, /* [in] */
463 IStorage** ppstg) /* [out] */
465 StorageBaseImpl *This = (StorageBaseImpl *)iface;
466 StorageInternalImpl* newStorage;
467 IEnumSTATSTGImpl* propertyEnumeration;
468 StgProperty currentProperty;
469 ULONG foundPropertyIndex;
470 HRESULT res = STG_E_UNKNOWN;
472 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
473 iface, debugstr_w(pwcsName), pstgPriority,
474 grfMode, snbExclude, reserved, ppstg);
477 * Perform a sanity check on the parameters.
479 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
481 res = E_INVALIDARG;
482 goto end;
485 /* as documented */
486 if (snbExclude != NULL)
488 res = STG_E_INVALIDPARAMETER;
489 goto end;
493 * Validate the STGM flags
495 if ( FAILED( validateSTGM(grfMode) ))
497 res = STG_E_INVALIDFLAG;
498 goto end;
502 * As documented.
504 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
505 (grfMode & STGM_DELETEONRELEASE) ||
506 (grfMode & STGM_PRIORITY) )
508 res = STG_E_INVALIDFUNCTION;
509 goto end;
513 * Initialize the out parameter
515 *ppstg = NULL;
518 * Create a property enumeration to search the properties
520 propertyEnumeration = IEnumSTATSTGImpl_Construct(
521 This->ancestorStorage,
522 This->rootPropertySetIndex);
525 * Search the enumeration for the property with the given name
527 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
528 propertyEnumeration,
529 pwcsName,
530 &currentProperty);
533 * Delete the property enumeration since we don't need it anymore
535 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
538 * If it was found, construct the stream object and return a pointer to it.
540 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
541 (currentProperty.propertyType==PROPTYPE_STORAGE) )
544 * Construct a new Storage object
546 newStorage = StorageInternalImpl_Construct(
547 This->ancestorStorage,
548 foundPropertyIndex);
550 if (newStorage != 0)
552 *ppstg = (IStorage*)newStorage;
555 * Since we are returning a pointer to the interface,
556 * we have to nail down the reference.
558 StorageBaseImpl_AddRef(*ppstg);
560 res = S_OK;
561 goto end;
564 res = STG_E_INSUFFICIENTMEMORY;
565 goto end;
568 res = STG_E_FILENOTFOUND;
570 end:
571 TRACE("<-- %08lx\n", res);
572 return res;
575 /************************************************************************
576 * Storage32BaseImpl_EnumElements (IStorage)
578 * This method will create an enumerator object that can be used to
579 * retrieve informatino about all the properties in the storage object.
581 * See Windows documentation for more details on IStorage methods.
583 HRESULT WINAPI StorageBaseImpl_EnumElements(
584 IStorage* iface,
585 DWORD reserved1, /* [in] */
586 void* reserved2, /* [size_is][unique][in] */
587 DWORD reserved3, /* [in] */
588 IEnumSTATSTG** ppenum) /* [out] */
590 StorageBaseImpl *This = (StorageBaseImpl *)iface;
591 IEnumSTATSTGImpl* newEnum;
593 TRACE("(%p, %ld, %p, %ld, %p)\n",
594 iface, reserved1, reserved2, reserved3, ppenum);
597 * Perform a sanity check on the parameters.
599 if ( (This==0) || (ppenum==0))
600 return E_INVALIDARG;
603 * Construct the enumerator.
605 newEnum = IEnumSTATSTGImpl_Construct(
606 This->ancestorStorage,
607 This->rootPropertySetIndex);
609 if (newEnum!=0)
611 *ppenum = (IEnumSTATSTG*)newEnum;
614 * Don't forget to nail down a reference to the new object before
615 * returning it.
617 IEnumSTATSTGImpl_AddRef(*ppenum);
619 return S_OK;
622 return E_OUTOFMEMORY;
625 /************************************************************************
626 * Storage32BaseImpl_Stat (IStorage)
628 * This method will retrieve information about this storage object.
630 * See Windows documentation for more details on IStorage methods.
632 HRESULT WINAPI StorageBaseImpl_Stat(
633 IStorage* iface,
634 STATSTG* pstatstg, /* [out] */
635 DWORD grfStatFlag) /* [in] */
637 StorageBaseImpl *This = (StorageBaseImpl *)iface;
638 StgProperty curProperty;
639 BOOL readSuccessful;
640 HRESULT res = STG_E_UNKNOWN;
642 TRACE("(%p, %p, %lx)\n",
643 iface, pstatstg, grfStatFlag);
646 * Perform a sanity check on the parameters.
648 if ( (This==0) || (pstatstg==0))
650 res = E_INVALIDARG;
651 goto end;
655 * Read the information from the property.
657 readSuccessful = StorageImpl_ReadProperty(
658 This->ancestorStorage,
659 This->rootPropertySetIndex,
660 &curProperty);
662 if (readSuccessful)
664 StorageUtl_CopyPropertyToSTATSTG(
665 pstatstg,
666 &curProperty,
667 grfStatFlag);
669 res = S_OK;
670 goto end;
673 res = E_FAIL;
675 end:
676 if (res == S_OK)
678 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);
680 TRACE("<-- %08lx\n", res);
681 return res;
684 /************************************************************************
685 * Storage32BaseImpl_RenameElement (IStorage)
687 * This method will rename the specified element.
689 * See Windows documentation for more details on IStorage methods.
691 * Implementation notes: The method used to rename consists of creating a clone
692 * of the deleted StgProperty object setting it with the new name and to
693 * perform a DestroyElement of the old StgProperty.
695 HRESULT WINAPI StorageBaseImpl_RenameElement(
696 IStorage* iface,
697 const OLECHAR* pwcsOldName, /* [in] */
698 const OLECHAR* pwcsNewName) /* [in] */
700 StorageBaseImpl *This = (StorageBaseImpl *)iface;
701 IEnumSTATSTGImpl* propertyEnumeration;
702 StgProperty currentProperty;
703 ULONG foundPropertyIndex;
705 TRACE("(%p, %s, %s)\n",
706 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
709 * Create a property enumeration to search the properties
711 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
712 This->rootPropertySetIndex);
715 * Search the enumeration for the new property name
717 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
718 pwcsNewName,
719 &currentProperty);
721 if (foundPropertyIndex != PROPERTY_NULL)
724 * There is already a property with the new name
726 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
727 return STG_E_FILEALREADYEXISTS;
730 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
733 * Search the enumeration for the old property name
735 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
736 pwcsOldName,
737 &currentProperty);
740 * Delete the property enumeration since we don't need it anymore
742 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
744 if (foundPropertyIndex != PROPERTY_NULL)
746 StgProperty renamedProperty;
747 ULONG renamedPropertyIndex;
750 * Setup a new property for the renamed property
752 renamedProperty.sizeOfNameString =
753 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
755 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
756 return STG_E_INVALIDNAME;
758 strcpyW(renamedProperty.name, pwcsNewName);
760 renamedProperty.propertyType = currentProperty.propertyType;
761 renamedProperty.startingBlock = currentProperty.startingBlock;
762 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
763 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
765 renamedProperty.previousProperty = PROPERTY_NULL;
766 renamedProperty.nextProperty = PROPERTY_NULL;
769 * Bring the dirProperty link in case it is a storage and in which
770 * case the renamed storage elements don't require to be reorganized.
772 renamedProperty.dirProperty = currentProperty.dirProperty;
774 /* call CoFileTime to get the current time
775 renamedProperty.timeStampS1
776 renamedProperty.timeStampD1
777 renamedProperty.timeStampS2
778 renamedProperty.timeStampD2
779 renamedProperty.propertyUniqueID
783 * Obtain a free property in the property chain
785 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
788 * Save the new property into the new property spot
790 StorageImpl_WriteProperty(
791 This->ancestorStorage,
792 renamedPropertyIndex,
793 &renamedProperty);
796 * Find a spot in the property chain for our newly created property.
798 updatePropertyChain(
799 (StorageImpl*)This,
800 renamedPropertyIndex,
801 renamedProperty);
804 * At this point the renamed property has been inserted in the tree,
805 * now, before to Destroy the old property we must zeroed it's dirProperty
806 * otherwise the DestroyProperty below will zap it all and we do not want
807 * this to happen.
808 * Also, we fake that the old property is a storage so the DestroyProperty
809 * will not do a SetSize(0) on the stream data.
811 * This means that we need to tweek the StgProperty if it is a stream or a
812 * non empty storage.
814 StorageImpl_ReadProperty(This->ancestorStorage,
815 foundPropertyIndex,
816 &currentProperty);
818 currentProperty.dirProperty = PROPERTY_NULL;
819 currentProperty.propertyType = PROPTYPE_STORAGE;
820 StorageImpl_WriteProperty(
821 This->ancestorStorage,
822 foundPropertyIndex,
823 &currentProperty);
826 * Invoke Destroy to get rid of the ole property and automatically redo
827 * the linking of it's previous and next members...
829 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
832 else
835 * There is no property with the old name
837 return STG_E_FILENOTFOUND;
840 return S_OK;
843 /************************************************************************
844 * Storage32BaseImpl_CreateStream (IStorage)
846 * This method will create a stream object within this storage
848 * See Windows documentation for more details on IStorage methods.
850 HRESULT WINAPI StorageBaseImpl_CreateStream(
851 IStorage* iface,
852 const OLECHAR* pwcsName, /* [string][in] */
853 DWORD grfMode, /* [in] */
854 DWORD reserved1, /* [in] */
855 DWORD reserved2, /* [in] */
856 IStream** ppstm) /* [out] */
858 StorageBaseImpl *This = (StorageBaseImpl *)iface;
859 IEnumSTATSTGImpl* propertyEnumeration;
860 StgStreamImpl* newStream;
861 StgProperty currentProperty, newStreamProperty;
862 ULONG foundPropertyIndex, newPropertyIndex;
864 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
865 iface, debugstr_w(pwcsName), grfMode,
866 reserved1, reserved2, ppstm);
869 * Validate parameters
871 if (ppstm == 0)
872 return STG_E_INVALIDPOINTER;
874 if (pwcsName == 0)
875 return STG_E_INVALIDNAME;
878 * Validate the STGM flags
880 if ( FAILED( validateSTGM(grfMode) ))
881 return STG_E_INVALIDFLAG;
884 * As documented.
886 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
887 (grfMode & STGM_DELETEONRELEASE) ||
888 (grfMode & STGM_TRANSACTED) )
889 return STG_E_INVALIDFUNCTION;
892 * Initialize the out parameter
894 *ppstm = 0;
897 * Create a property enumeration to search the properties
899 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
900 This->rootPropertySetIndex);
902 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
903 pwcsName,
904 &currentProperty);
906 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
908 if (foundPropertyIndex != PROPERTY_NULL)
911 * An element with this name already exists
913 if (grfMode & STGM_CREATE)
915 IStorage_DestroyElement(iface, pwcsName);
917 else
918 return STG_E_FILEALREADYEXISTS;
922 * memset the empty property
924 memset(&newStreamProperty, 0, sizeof(StgProperty));
926 newStreamProperty.sizeOfNameString =
927 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
929 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
930 return STG_E_INVALIDNAME;
932 strcpyW(newStreamProperty.name, pwcsName);
934 newStreamProperty.propertyType = PROPTYPE_STREAM;
935 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
936 newStreamProperty.size.u.LowPart = 0;
937 newStreamProperty.size.u.HighPart = 0;
939 newStreamProperty.previousProperty = PROPERTY_NULL;
940 newStreamProperty.nextProperty = PROPERTY_NULL;
941 newStreamProperty.dirProperty = PROPERTY_NULL;
943 /* call CoFileTime to get the current time
944 newStreamProperty.timeStampS1
945 newStreamProperty.timeStampD1
946 newStreamProperty.timeStampS2
947 newStreamProperty.timeStampD2
950 /* newStreamProperty.propertyUniqueID */
953 * Get a free property or create a new one
955 newPropertyIndex = getFreeProperty(This->ancestorStorage);
958 * Save the new property into the new property spot
960 StorageImpl_WriteProperty(
961 This->ancestorStorage,
962 newPropertyIndex,
963 &newStreamProperty);
966 * Find a spot in the property chain for our newly created property.
968 updatePropertyChain(
969 (StorageImpl*)This,
970 newPropertyIndex,
971 newStreamProperty);
974 * Open the stream to return it.
976 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
978 if (newStream != 0)
980 *ppstm = (IStream*)newStream;
983 * Since we are returning a pointer to the interface, we have to nail down
984 * the reference.
986 StgStreamImpl_AddRef(*ppstm);
988 else
990 return STG_E_INSUFFICIENTMEMORY;
993 return S_OK;
996 /************************************************************************
997 * Storage32BaseImpl_SetClass (IStorage)
999 * This method will write the specified CLSID in the property of this
1000 * storage.
1002 * See Windows documentation for more details on IStorage methods.
1004 HRESULT WINAPI StorageBaseImpl_SetClass(
1005 IStorage* iface,
1006 REFCLSID clsid) /* [in] */
1008 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1009 HRESULT hRes = E_FAIL;
1010 StgProperty curProperty;
1011 BOOL success;
1013 TRACE("(%p, %p)\n", iface, clsid);
1015 success = StorageImpl_ReadProperty(This->ancestorStorage,
1016 This->rootPropertySetIndex,
1017 &curProperty);
1018 if (success)
1020 curProperty.propertyUniqueID = *clsid;
1022 success = StorageImpl_WriteProperty(This->ancestorStorage,
1023 This->rootPropertySetIndex,
1024 &curProperty);
1025 if (success)
1026 hRes = S_OK;
1029 return hRes;
1032 /************************************************************************
1033 ** Storage32Impl implementation
1036 /************************************************************************
1037 * Storage32Impl_CreateStorage (IStorage)
1039 * This method will create the storage object within the provided storage.
1041 * See Windows documentation for more details on IStorage methods.
1043 HRESULT WINAPI StorageImpl_CreateStorage(
1044 IStorage* iface,
1045 const OLECHAR *pwcsName, /* [string][in] */
1046 DWORD grfMode, /* [in] */
1047 DWORD reserved1, /* [in] */
1048 DWORD reserved2, /* [in] */
1049 IStorage **ppstg) /* [out] */
1051 StorageImpl* const This=(StorageImpl*)iface;
1053 IEnumSTATSTGImpl *propertyEnumeration;
1054 StgProperty currentProperty;
1055 StgProperty newProperty;
1056 ULONG foundPropertyIndex;
1057 ULONG newPropertyIndex;
1058 HRESULT hr;
1060 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1061 iface, debugstr_w(pwcsName), grfMode,
1062 reserved1, reserved2, ppstg);
1065 * Validate parameters
1067 if (ppstg == 0)
1068 return STG_E_INVALIDPOINTER;
1070 if (pwcsName == 0)
1071 return STG_E_INVALIDNAME;
1074 * Validate the STGM flags
1076 if ( FAILED( validateSTGM(grfMode) ) ||
1077 (grfMode & STGM_DELETEONRELEASE) )
1078 return STG_E_INVALIDFLAG;
1081 * Initialize the out parameter
1083 *ppstg = 0;
1086 * Create a property enumeration and search the properties
1088 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1089 This->rootPropertySetIndex);
1091 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1092 pwcsName,
1093 &currentProperty);
1094 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1096 if (foundPropertyIndex != PROPERTY_NULL)
1099 * An element with this name already exists
1101 if (grfMode & STGM_CREATE)
1102 IStorage_DestroyElement(iface, pwcsName);
1103 else
1104 return STG_E_FILEALREADYEXISTS;
1108 * memset the empty property
1110 memset(&newProperty, 0, sizeof(StgProperty));
1112 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1114 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1115 return STG_E_INVALIDNAME;
1117 strcpyW(newProperty.name, pwcsName);
1119 newProperty.propertyType = PROPTYPE_STORAGE;
1120 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1121 newProperty.size.u.LowPart = 0;
1122 newProperty.size.u.HighPart = 0;
1124 newProperty.previousProperty = PROPERTY_NULL;
1125 newProperty.nextProperty = PROPERTY_NULL;
1126 newProperty.dirProperty = PROPERTY_NULL;
1128 /* call CoFileTime to get the current time
1129 newProperty.timeStampS1
1130 newProperty.timeStampD1
1131 newProperty.timeStampS2
1132 newProperty.timeStampD2
1135 /* newStorageProperty.propertyUniqueID */
1138 * Obtain a free property in the property chain
1140 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1143 * Save the new property into the new property spot
1145 StorageImpl_WriteProperty(
1146 This->ancestorStorage,
1147 newPropertyIndex,
1148 &newProperty);
1151 * Find a spot in the property chain for our newly created property.
1153 updatePropertyChain(
1154 This,
1155 newPropertyIndex,
1156 newProperty);
1159 * Open it to get a pointer to return.
1161 hr = IStorage_OpenStorage(
1162 iface,
1163 (OLECHAR*)pwcsName,
1165 grfMode,
1168 ppstg);
1170 if( (hr != S_OK) || (*ppstg == NULL))
1172 return hr;
1176 return S_OK;
1180 /***************************************************************************
1182 * Internal Method
1184 * Get a free property or create a new one.
1186 static ULONG getFreeProperty(
1187 StorageImpl *storage)
1189 ULONG currentPropertyIndex = 0;
1190 ULONG newPropertyIndex = PROPERTY_NULL;
1191 BOOL readSuccessful = TRUE;
1192 StgProperty currentProperty;
1197 * Start by reading the root property
1199 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1200 currentPropertyIndex,
1201 &currentProperty);
1202 if (readSuccessful)
1204 if (currentProperty.sizeOfNameString == 0)
1207 * The property existis and is available, we found it.
1209 newPropertyIndex = currentPropertyIndex;
1212 else
1215 * We exhausted the property list, we will create more space below
1217 newPropertyIndex = currentPropertyIndex;
1219 currentPropertyIndex++;
1221 } while (newPropertyIndex == PROPERTY_NULL);
1224 * grow the property chain
1226 if (! readSuccessful)
1228 StgProperty emptyProperty;
1229 ULARGE_INTEGER newSize;
1230 ULONG propertyIndex;
1231 ULONG lastProperty = 0;
1232 ULONG blockCount = 0;
1235 * obtain the new count of property blocks
1237 blockCount = BlockChainStream_GetCount(
1238 storage->ancestorStorage->rootBlockChain)+1;
1241 * initialize the size used by the property stream
1243 newSize.u.HighPart = 0;
1244 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1247 * add a property block to the property chain
1249 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1252 * memset the empty property in order to initialize the unused newly
1253 * created property
1255 memset(&emptyProperty, 0, sizeof(StgProperty));
1258 * initialize them
1260 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1262 for(
1263 propertyIndex = newPropertyIndex;
1264 propertyIndex < lastProperty;
1265 propertyIndex++)
1267 StorageImpl_WriteProperty(
1268 storage->ancestorStorage,
1269 propertyIndex,
1270 &emptyProperty);
1274 return newPropertyIndex;
1277 /****************************************************************************
1279 * Internal Method
1281 * Case insensitive comparaison of StgProperty.name by first considering
1282 * their size.
1284 * Returns <0 when newPrpoerty < currentProperty
1285 * >0 when newPrpoerty > currentProperty
1286 * 0 when newPrpoerty == currentProperty
1288 static LONG propertyNameCmp(
1289 OLECHAR *newProperty,
1290 OLECHAR *currentProperty)
1292 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1294 if (diff == 0)
1297 * We compare the string themselves only when they are of the same length
1299 diff = lstrcmpiW( newProperty, currentProperty);
1302 return diff;
1305 /****************************************************************************
1307 * Internal Method
1309 * Properly link this new element in the property chain.
1311 static void updatePropertyChain(
1312 StorageImpl *storage,
1313 ULONG newPropertyIndex,
1314 StgProperty newProperty)
1316 StgProperty currentProperty;
1319 * Read the root property
1321 StorageImpl_ReadProperty(storage->ancestorStorage,
1322 storage->rootPropertySetIndex,
1323 &currentProperty);
1325 if (currentProperty.dirProperty != PROPERTY_NULL)
1328 * The root storage contains some element, therefore, start the research
1329 * for the appropriate location.
1331 BOOL found = 0;
1332 ULONG current, next, previous, currentPropertyId;
1335 * Keep the StgProperty sequence number of the storage first property
1337 currentPropertyId = currentProperty.dirProperty;
1340 * Read
1342 StorageImpl_ReadProperty(storage->ancestorStorage,
1343 currentProperty.dirProperty,
1344 &currentProperty);
1346 previous = currentProperty.previousProperty;
1347 next = currentProperty.nextProperty;
1348 current = currentPropertyId;
1350 while (found == 0)
1352 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1354 if (diff < 0)
1356 if (previous != PROPERTY_NULL)
1358 StorageImpl_ReadProperty(storage->ancestorStorage,
1359 previous,
1360 &currentProperty);
1361 current = previous;
1363 else
1365 currentProperty.previousProperty = newPropertyIndex;
1366 StorageImpl_WriteProperty(storage->ancestorStorage,
1367 current,
1368 &currentProperty);
1369 found = 1;
1372 else if (diff > 0)
1374 if (next != PROPERTY_NULL)
1376 StorageImpl_ReadProperty(storage->ancestorStorage,
1377 next,
1378 &currentProperty);
1379 current = next;
1381 else
1383 currentProperty.nextProperty = newPropertyIndex;
1384 StorageImpl_WriteProperty(storage->ancestorStorage,
1385 current,
1386 &currentProperty);
1387 found = 1;
1390 else
1393 * Trying to insert an item with the same name in the
1394 * subtree structure.
1396 assert(FALSE);
1399 previous = currentProperty.previousProperty;
1400 next = currentProperty.nextProperty;
1403 else
1406 * The root storage is empty, link the new property to it's dir property
1408 currentProperty.dirProperty = newPropertyIndex;
1409 StorageImpl_WriteProperty(storage->ancestorStorage,
1410 storage->rootPropertySetIndex,
1411 &currentProperty);
1416 /*************************************************************************
1417 * CopyTo (IStorage)
1419 HRESULT WINAPI StorageImpl_CopyTo(
1420 IStorage* iface,
1421 DWORD ciidExclude, /* [in] */
1422 const IID* rgiidExclude, /* [size_is][unique][in] */
1423 SNB snbExclude, /* [unique][in] */
1424 IStorage* pstgDest) /* [unique][in] */
1426 IEnumSTATSTG *elements = 0;
1427 STATSTG curElement, strStat;
1428 HRESULT hr;
1429 IStorage *pstgTmp, *pstgChild;
1430 IStream *pstrTmp, *pstrChild;
1432 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1433 FIXME("Exclude option not implemented\n");
1435 TRACE("(%p, %ld, %p, %p, %p)\n",
1436 iface, ciidExclude, rgiidExclude,
1437 snbExclude, pstgDest);
1440 * Perform a sanity check
1442 if ( pstgDest == 0 )
1443 return STG_E_INVALIDPOINTER;
1446 * Enumerate the elements
1448 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1450 if ( hr != S_OK )
1451 return hr;
1454 * set the class ID
1456 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1457 IStorage_SetClass( pstgDest, &curElement.clsid );
1462 * Obtain the next element
1464 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1466 if ( hr == S_FALSE )
1468 hr = S_OK; /* done, every element has been copied */
1469 break;
1472 if (curElement.type == STGTY_STORAGE)
1475 * open child source storage
1477 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1478 STGM_READ|STGM_SHARE_EXCLUSIVE,
1479 NULL, 0, &pstgChild );
1481 if (hr != S_OK)
1482 break;
1485 * Check if destination storage is not a child of the source
1486 * storage, which will cause an infinite loop
1488 if (pstgChild == pstgDest)
1490 IEnumSTATSTG_Release(elements);
1492 return STG_E_ACCESSDENIED;
1496 * create a new storage in destination storage
1498 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1499 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1500 0, 0,
1501 &pstgTmp );
1503 * if it already exist, don't create a new one use this one
1505 if (hr == STG_E_FILEALREADYEXISTS)
1507 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1508 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1509 NULL, 0, &pstgTmp );
1512 if (hr != S_OK)
1513 break;
1517 * do the copy recursively
1519 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1520 snbExclude, pstgTmp );
1522 IStorage_Release( pstgTmp );
1523 IStorage_Release( pstgChild );
1525 else if (curElement.type == STGTY_STREAM)
1528 * create a new stream in destination storage. If the stream already
1529 * exist, it will be deleted and a new one will be created.
1531 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1532 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1533 0, 0, &pstrTmp );
1535 if (hr != S_OK)
1536 break;
1539 * open child stream storage
1541 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1542 STGM_READ|STGM_SHARE_EXCLUSIVE,
1543 0, &pstrChild );
1545 if (hr != S_OK)
1546 break;
1549 * Get the size of the source stream
1551 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1554 * Set the size of the destination stream.
1556 IStream_SetSize(pstrTmp, strStat.cbSize);
1559 * do the copy
1561 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1562 NULL, NULL );
1564 IStream_Release( pstrTmp );
1565 IStream_Release( pstrChild );
1567 else
1569 WARN("unknown element type: %ld\n", curElement.type);
1572 } while (hr == S_OK);
1575 * Clean-up
1577 IEnumSTATSTG_Release(elements);
1579 return hr;
1582 /*************************************************************************
1583 * MoveElementTo (IStorage)
1585 HRESULT WINAPI StorageImpl_MoveElementTo(
1586 IStorage* iface,
1587 const OLECHAR *pwcsName, /* [string][in] */
1588 IStorage *pstgDest, /* [unique][in] */
1589 const OLECHAR *pwcsNewName,/* [string][in] */
1590 DWORD grfFlags) /* [in] */
1592 FIXME("not implemented!\n");
1593 return E_NOTIMPL;
1596 /*************************************************************************
1597 * Commit (IStorage)
1599 HRESULT WINAPI StorageImpl_Commit(
1600 IStorage* iface,
1601 DWORD grfCommitFlags)/* [in] */
1603 FIXME("(%ld): stub!\n", grfCommitFlags);
1604 return S_OK;
1607 /*************************************************************************
1608 * Revert (IStorage)
1610 HRESULT WINAPI StorageImpl_Revert(
1611 IStorage* iface)
1613 FIXME("not implemented!\n");
1614 return E_NOTIMPL;
1617 /*************************************************************************
1618 * DestroyElement (IStorage)
1620 * Stategy: This implementation is build this way for simplicity not for speed.
1621 * I always delete the top most element of the enumeration and adjust
1622 * the deleted element pointer all the time. This takes longer to
1623 * do but allow to reinvoke DestroyElement whenever we encounter a
1624 * storage object. The optimisation reside in the usage of another
1625 * enumeration stategy that would give all the leaves of a storage
1626 * first. (postfix order)
1628 HRESULT WINAPI StorageImpl_DestroyElement(
1629 IStorage* iface,
1630 const OLECHAR *pwcsName)/* [string][in] */
1632 StorageImpl* const This=(StorageImpl*)iface;
1634 IEnumSTATSTGImpl* propertyEnumeration;
1635 HRESULT hr = S_OK;
1636 BOOL res;
1637 StgProperty propertyToDelete;
1638 StgProperty parentProperty;
1639 ULONG foundPropertyIndexToDelete;
1640 ULONG typeOfRelation;
1641 ULONG parentPropertyId;
1643 TRACE("(%p, %s)\n",
1644 iface, debugstr_w(pwcsName));
1647 * Perform a sanity check on the parameters.
1649 if (pwcsName==NULL)
1650 return STG_E_INVALIDPOINTER;
1653 * Create a property enumeration to search the property with the given name
1655 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1656 This->ancestorStorage,
1657 This->rootPropertySetIndex);
1659 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1660 propertyEnumeration,
1661 pwcsName,
1662 &propertyToDelete);
1664 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1666 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1668 return STG_E_FILENOTFOUND;
1672 * Find the parent property of the property to delete (the one that
1673 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1674 * the parent is This. Otherwise, the parent is one of it's sibling...
1678 * First, read This's StgProperty..
1680 res = StorageImpl_ReadProperty(
1681 This->ancestorStorage,
1682 This->rootPropertySetIndex,
1683 &parentProperty);
1685 assert(res==TRUE);
1688 * Second, check to see if by any chance the actual storage (This) is not
1689 * the parent of the property to delete... We never know...
1691 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1694 * Set data as it would have been done in the else part...
1696 typeOfRelation = PROPERTY_RELATION_DIR;
1697 parentPropertyId = This->rootPropertySetIndex;
1699 else
1702 * Create a property enumeration to search the parent properties, and
1703 * delete it once done.
1705 IEnumSTATSTGImpl* propertyEnumeration2;
1707 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1708 This->ancestorStorage,
1709 This->rootPropertySetIndex);
1711 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1712 propertyEnumeration2,
1713 foundPropertyIndexToDelete,
1714 &parentProperty,
1715 &parentPropertyId);
1717 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1720 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1722 hr = deleteStorageProperty(
1723 This,
1724 foundPropertyIndexToDelete,
1725 propertyToDelete);
1727 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1729 hr = deleteStreamProperty(
1730 This,
1731 foundPropertyIndexToDelete,
1732 propertyToDelete);
1735 if (hr!=S_OK)
1736 return hr;
1739 * Adjust the property chain
1741 hr = adjustPropertyChain(
1742 This,
1743 propertyToDelete,
1744 parentProperty,
1745 parentPropertyId,
1746 typeOfRelation);
1748 return hr;
1752 /************************************************************************
1753 * StorageImpl_Stat (IStorage)
1755 * This method will retrieve information about this storage object.
1757 * See Windows documentation for more details on IStorage methods.
1759 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1760 STATSTG* pstatstg, /* [out] */
1761 DWORD grfStatFlag) /* [in] */
1763 StorageImpl* const This = (StorageImpl*)iface;
1764 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1766 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1768 CoTaskMemFree(pstatstg->pwcsName);
1769 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1770 strcpyW(pstatstg->pwcsName, This->pwcsName);
1773 return result;
1778 /*********************************************************************
1780 * Internal Method
1782 * Perform the deletion of a complete storage node
1785 static HRESULT deleteStorageProperty(
1786 StorageImpl *parentStorage,
1787 ULONG indexOfPropertyToDelete,
1788 StgProperty propertyToDelete)
1790 IEnumSTATSTG *elements = 0;
1791 IStorage *childStorage = 0;
1792 STATSTG currentElement;
1793 HRESULT hr;
1794 HRESULT destroyHr = S_OK;
1797 * Open the storage and enumerate it
1799 hr = StorageBaseImpl_OpenStorage(
1800 (IStorage*)parentStorage,
1801 propertyToDelete.name,
1803 STGM_SHARE_EXCLUSIVE,
1806 &childStorage);
1808 if (hr != S_OK)
1810 return hr;
1814 * Enumerate the elements
1816 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1821 * Obtain the next element
1823 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1824 if (hr==S_OK)
1826 destroyHr = StorageImpl_DestroyElement(
1827 (IStorage*)childStorage,
1828 (OLECHAR*)currentElement.pwcsName);
1830 CoTaskMemFree(currentElement.pwcsName);
1834 * We need to Reset the enumeration every time because we delete elements
1835 * and the enumeration could be invalid
1837 IEnumSTATSTG_Reset(elements);
1839 } while ((hr == S_OK) && (destroyHr == S_OK));
1842 * Invalidate the property by zeroing it's name member.
1844 propertyToDelete.sizeOfNameString = 0;
1846 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1847 indexOfPropertyToDelete,
1848 &propertyToDelete);
1850 IStorage_Release(childStorage);
1851 IEnumSTATSTG_Release(elements);
1853 return destroyHr;
1856 /*********************************************************************
1858 * Internal Method
1860 * Perform the deletion of a stream node
1863 static HRESULT deleteStreamProperty(
1864 StorageImpl *parentStorage,
1865 ULONG indexOfPropertyToDelete,
1866 StgProperty propertyToDelete)
1868 IStream *pis;
1869 HRESULT hr;
1870 ULARGE_INTEGER size;
1872 size.u.HighPart = 0;
1873 size.u.LowPart = 0;
1875 hr = StorageBaseImpl_OpenStream(
1876 (IStorage*)parentStorage,
1877 (OLECHAR*)propertyToDelete.name,
1878 NULL,
1879 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1881 &pis);
1883 if (hr!=S_OK)
1885 return(hr);
1889 * Zap the stream
1891 hr = IStream_SetSize(pis, size);
1893 if(hr != S_OK)
1895 return hr;
1899 * Release the stream object.
1901 IStream_Release(pis);
1904 * Invalidate the property by zeroing it's name member.
1906 propertyToDelete.sizeOfNameString = 0;
1909 * Here we should re-read the property so we get the updated pointer
1910 * but since we are here to zap it, I don't do it...
1912 StorageImpl_WriteProperty(
1913 parentStorage->ancestorStorage,
1914 indexOfPropertyToDelete,
1915 &propertyToDelete);
1917 return S_OK;
1920 /*********************************************************************
1922 * Internal Method
1924 * Finds a placeholder for the StgProperty within the Storage
1927 static HRESULT findPlaceholder(
1928 StorageImpl *storage,
1929 ULONG propertyIndexToStore,
1930 ULONG storePropertyIndex,
1931 INT typeOfRelation)
1933 StgProperty storeProperty;
1934 HRESULT hr = S_OK;
1935 BOOL res = TRUE;
1938 * Read the storage property
1940 res = StorageImpl_ReadProperty(
1941 storage->ancestorStorage,
1942 storePropertyIndex,
1943 &storeProperty);
1945 if(! res)
1947 return E_FAIL;
1950 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1952 if (storeProperty.previousProperty != PROPERTY_NULL)
1954 return findPlaceholder(
1955 storage,
1956 propertyIndexToStore,
1957 storeProperty.previousProperty,
1958 typeOfRelation);
1960 else
1962 storeProperty.previousProperty = propertyIndexToStore;
1965 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1967 if (storeProperty.nextProperty != PROPERTY_NULL)
1969 return findPlaceholder(
1970 storage,
1971 propertyIndexToStore,
1972 storeProperty.nextProperty,
1973 typeOfRelation);
1975 else
1977 storeProperty.nextProperty = propertyIndexToStore;
1980 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1982 if (storeProperty.dirProperty != PROPERTY_NULL)
1984 return findPlaceholder(
1985 storage,
1986 propertyIndexToStore,
1987 storeProperty.dirProperty,
1988 typeOfRelation);
1990 else
1992 storeProperty.dirProperty = propertyIndexToStore;
1996 hr = StorageImpl_WriteProperty(
1997 storage->ancestorStorage,
1998 storePropertyIndex,
1999 &storeProperty);
2001 if(! hr)
2003 return E_FAIL;
2006 return S_OK;
2009 /*************************************************************************
2011 * Internal Method
2013 * This method takes the previous and the next property link of a property
2014 * to be deleted and find them a place in the Storage.
2016 static HRESULT adjustPropertyChain(
2017 StorageImpl *This,
2018 StgProperty propertyToDelete,
2019 StgProperty parentProperty,
2020 ULONG parentPropertyId,
2021 INT typeOfRelation)
2023 ULONG newLinkProperty = PROPERTY_NULL;
2024 BOOL needToFindAPlaceholder = FALSE;
2025 ULONG storeNode = PROPERTY_NULL;
2026 ULONG toStoreNode = PROPERTY_NULL;
2027 INT relationType = 0;
2028 HRESULT hr = S_OK;
2029 BOOL res = TRUE;
2031 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2033 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2036 * Set the parent previous to the property to delete previous
2038 newLinkProperty = propertyToDelete.previousProperty;
2040 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2043 * We also need to find a storage for the other link, setup variables
2044 * to do this at the end...
2046 needToFindAPlaceholder = TRUE;
2047 storeNode = propertyToDelete.previousProperty;
2048 toStoreNode = propertyToDelete.nextProperty;
2049 relationType = PROPERTY_RELATION_NEXT;
2052 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2055 * Set the parent previous to the property to delete next
2057 newLinkProperty = propertyToDelete.nextProperty;
2061 * Link it for real...
2063 parentProperty.previousProperty = newLinkProperty;
2066 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2068 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2071 * Set the parent next to the property to delete next previous
2073 newLinkProperty = propertyToDelete.previousProperty;
2075 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2078 * We also need to find a storage for the other link, setup variables
2079 * to do this at the end...
2081 needToFindAPlaceholder = TRUE;
2082 storeNode = propertyToDelete.previousProperty;
2083 toStoreNode = propertyToDelete.nextProperty;
2084 relationType = PROPERTY_RELATION_NEXT;
2087 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2090 * Set the parent next to the property to delete next
2092 newLinkProperty = propertyToDelete.nextProperty;
2096 * Link it for real...
2098 parentProperty.nextProperty = newLinkProperty;
2100 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2102 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2105 * Set the parent dir to the property to delete previous
2107 newLinkProperty = propertyToDelete.previousProperty;
2109 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2112 * We also need to find a storage for the other link, setup variables
2113 * to do this at the end...
2115 needToFindAPlaceholder = TRUE;
2116 storeNode = propertyToDelete.previousProperty;
2117 toStoreNode = propertyToDelete.nextProperty;
2118 relationType = PROPERTY_RELATION_NEXT;
2121 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2124 * Set the parent dir to the property to delete next
2126 newLinkProperty = propertyToDelete.nextProperty;
2130 * Link it for real...
2132 parentProperty.dirProperty = newLinkProperty;
2136 * Write back the parent property
2138 res = StorageImpl_WriteProperty(
2139 This->ancestorStorage,
2140 parentPropertyId,
2141 &parentProperty);
2142 if(! res)
2144 return E_FAIL;
2148 * If a placeholder is required for the other link, then, find one and
2149 * get out of here...
2151 if (needToFindAPlaceholder)
2153 hr = findPlaceholder(
2154 This,
2155 toStoreNode,
2156 storeNode,
2157 relationType);
2160 return hr;
2164 /******************************************************************************
2165 * SetElementTimes (IStorage)
2167 HRESULT WINAPI StorageImpl_SetElementTimes(
2168 IStorage* iface,
2169 const OLECHAR *pwcsName,/* [string][in] */
2170 const FILETIME *pctime, /* [in] */
2171 const FILETIME *patime, /* [in] */
2172 const FILETIME *pmtime) /* [in] */
2174 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2175 return S_OK;
2178 /******************************************************************************
2179 * SetStateBits (IStorage)
2181 HRESULT WINAPI StorageImpl_SetStateBits(
2182 IStorage* iface,
2183 DWORD grfStateBits,/* [in] */
2184 DWORD grfMask) /* [in] */
2186 FIXME("not implemented!\n");
2187 return E_NOTIMPL;
2190 HRESULT StorageImpl_Construct(
2191 StorageImpl* This,
2192 HANDLE hFile,
2193 LPCOLESTR pwcsName,
2194 ILockBytes* pLkbyt,
2195 DWORD openFlags,
2196 BOOL fileBased,
2197 BOOL fileCreate)
2199 HRESULT hr = S_OK;
2200 StgProperty currentProperty;
2201 BOOL readSuccessful;
2202 ULONG currentPropertyIndex;
2204 if ( FAILED( validateSTGM(openFlags) ))
2205 return STG_E_INVALIDFLAG;
2207 memset(This, 0, sizeof(StorageImpl));
2210 * Initialize the virtual function table.
2212 This->lpVtbl = &Storage32Impl_Vtbl;
2213 This->v_destructor = &StorageImpl_Destroy;
2216 * This is the top-level storage so initialize the ancestor pointer
2217 * to this.
2219 This->ancestorStorage = This;
2222 * Initialize the physical support of the storage.
2224 This->hFile = hFile;
2227 * Store copy of file path.
2229 if(pwcsName) {
2230 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2231 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2232 if (!This->pwcsName)
2233 return STG_E_INSUFFICIENTMEMORY;
2234 strcpyW(This->pwcsName, pwcsName);
2238 * Initialize the big block cache.
2240 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2241 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2242 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2243 pLkbyt,
2244 openFlags,
2245 This->bigBlockSize,
2246 fileBased);
2248 if (This->bigBlockFile == 0)
2249 return E_FAIL;
2251 if (fileCreate)
2253 ULARGE_INTEGER size;
2254 BYTE* bigBlockBuffer;
2257 * Initialize all header variables:
2258 * - The big block depot consists of one block and it is at block 0
2259 * - The properties start at block 1
2260 * - There is no small block depot
2262 memset( This->bigBlockDepotStart,
2263 BLOCK_UNUSED,
2264 sizeof(This->bigBlockDepotStart));
2266 This->bigBlockDepotCount = 1;
2267 This->bigBlockDepotStart[0] = 0;
2268 This->rootStartBlock = 1;
2269 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2270 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2271 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2272 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2273 This->extBigBlockDepotCount = 0;
2275 StorageImpl_SaveFileHeader(This);
2278 * Add one block for the big block depot and one block for the properties
2280 size.u.HighPart = 0;
2281 size.u.LowPart = This->bigBlockSize * 3;
2282 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2285 * Initialize the big block depot
2287 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2288 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2289 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2290 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2291 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2293 else
2296 * Load the header for the file.
2298 hr = StorageImpl_LoadFileHeader(This);
2300 if (FAILED(hr))
2302 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2304 return hr;
2309 * There is no block depot cached yet.
2311 This->indexBlockDepotCached = 0xFFFFFFFF;
2314 * Start searching for free blocks with block 0.
2316 This->prevFreeBlock = 0;
2319 * Create the block chain abstractions.
2321 if(!(This->rootBlockChain =
2322 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2323 return STG_E_READFAULT;
2325 if(!(This->smallBlockDepotChain =
2326 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2327 PROPERTY_NULL)))
2328 return STG_E_READFAULT;
2331 * Write the root property
2333 if (fileCreate)
2335 StgProperty rootProp;
2337 * Initialize the property chain
2339 memset(&rootProp, 0, sizeof(rootProp));
2340 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2341 sizeof(rootProp.name)/sizeof(WCHAR) );
2342 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2343 rootProp.propertyType = PROPTYPE_ROOT;
2344 rootProp.previousProperty = PROPERTY_NULL;
2345 rootProp.nextProperty = PROPERTY_NULL;
2346 rootProp.dirProperty = PROPERTY_NULL;
2347 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2348 rootProp.size.u.HighPart = 0;
2349 rootProp.size.u.LowPart = 0;
2351 StorageImpl_WriteProperty(This, 0, &rootProp);
2355 * Find the ID of the root in the property sets.
2357 currentPropertyIndex = 0;
2361 readSuccessful = StorageImpl_ReadProperty(
2362 This,
2363 currentPropertyIndex,
2364 &currentProperty);
2366 if (readSuccessful)
2368 if ( (currentProperty.sizeOfNameString != 0 ) &&
2369 (currentProperty.propertyType == PROPTYPE_ROOT) )
2371 This->rootPropertySetIndex = currentPropertyIndex;
2375 currentPropertyIndex++;
2377 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2379 if (!readSuccessful)
2381 /* TODO CLEANUP */
2382 return STG_E_READFAULT;
2386 * Create the block chain abstraction for the small block root chain.
2388 if(!(This->smallBlockRootChain =
2389 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2390 return STG_E_READFAULT;
2392 return hr;
2395 void StorageImpl_Destroy(
2396 StorageImpl* This)
2398 TRACE("(%p)\n", This);
2400 if(This->pwcsName)
2401 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2403 BlockChainStream_Destroy(This->smallBlockRootChain);
2404 BlockChainStream_Destroy(This->rootBlockChain);
2405 BlockChainStream_Destroy(This->smallBlockDepotChain);
2407 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2408 return;
2411 /******************************************************************************
2412 * Storage32Impl_GetNextFreeBigBlock
2414 * Returns the index of the next free big block.
2415 * If the big block depot is filled, this method will enlarge it.
2418 ULONG StorageImpl_GetNextFreeBigBlock(
2419 StorageImpl* This)
2421 ULONG depotBlockIndexPos;
2422 void *depotBuffer;
2423 ULONG depotBlockOffset;
2424 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2425 ULONG nextBlockIndex = BLOCK_SPECIAL;
2426 int depotIndex = 0;
2427 ULONG freeBlock = BLOCK_UNUSED;
2429 depotIndex = This->prevFreeBlock / blocksPerDepot;
2430 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2433 * Scan the entire big block depot until we find a block marked free
2435 while (nextBlockIndex != BLOCK_UNUSED)
2437 if (depotIndex < COUNT_BBDEPOTINHEADER)
2439 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2442 * Grow the primary depot.
2444 if (depotBlockIndexPos == BLOCK_UNUSED)
2446 depotBlockIndexPos = depotIndex*blocksPerDepot;
2449 * Add a block depot.
2451 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2452 This->bigBlockDepotCount++;
2453 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2456 * Flag it as a block depot.
2458 StorageImpl_SetNextBlockInChain(This,
2459 depotBlockIndexPos,
2460 BLOCK_SPECIAL);
2462 /* Save new header information.
2464 StorageImpl_SaveFileHeader(This);
2467 else
2469 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2471 if (depotBlockIndexPos == BLOCK_UNUSED)
2474 * Grow the extended depot.
2476 ULONG extIndex = BLOCK_UNUSED;
2477 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2478 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2480 if (extBlockOffset == 0)
2482 /* We need an extended block.
2484 extIndex = Storage32Impl_AddExtBlockDepot(This);
2485 This->extBigBlockDepotCount++;
2486 depotBlockIndexPos = extIndex + 1;
2488 else
2489 depotBlockIndexPos = depotIndex * blocksPerDepot;
2492 * Add a block depot and mark it in the extended block.
2494 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2495 This->bigBlockDepotCount++;
2496 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2498 /* Flag the block depot.
2500 StorageImpl_SetNextBlockInChain(This,
2501 depotBlockIndexPos,
2502 BLOCK_SPECIAL);
2504 /* If necessary, flag the extended depot block.
2506 if (extIndex != BLOCK_UNUSED)
2507 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2509 /* Save header information.
2511 StorageImpl_SaveFileHeader(This);
2515 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2517 if (depotBuffer != 0)
2519 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2520 ( nextBlockIndex != BLOCK_UNUSED))
2522 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2524 if (nextBlockIndex == BLOCK_UNUSED)
2526 freeBlock = (depotIndex * blocksPerDepot) +
2527 (depotBlockOffset/sizeof(ULONG));
2530 depotBlockOffset += sizeof(ULONG);
2533 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2536 depotIndex++;
2537 depotBlockOffset = 0;
2540 This->prevFreeBlock = freeBlock;
2542 return freeBlock;
2545 /******************************************************************************
2546 * Storage32Impl_AddBlockDepot
2548 * This will create a depot block, essentially it is a block initialized
2549 * to BLOCK_UNUSEDs.
2551 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2553 BYTE* blockBuffer;
2555 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2558 * Initialize blocks as free
2560 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2562 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2565 /******************************************************************************
2566 * Storage32Impl_GetExtDepotBlock
2568 * Returns the index of the block that corresponds to the specified depot
2569 * index. This method is only for depot indexes equal or greater than
2570 * COUNT_BBDEPOTINHEADER.
2572 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2574 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2575 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2576 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2577 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2578 ULONG blockIndex = BLOCK_UNUSED;
2579 ULONG extBlockIndex = This->extBigBlockDepotStart;
2581 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2583 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2584 return BLOCK_UNUSED;
2586 while (extBlockCount > 0)
2588 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2589 extBlockCount--;
2592 if (extBlockIndex != BLOCK_UNUSED)
2594 BYTE* depotBuffer;
2596 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2598 if (depotBuffer != 0)
2600 StorageUtl_ReadDWord(depotBuffer,
2601 extBlockOffset * sizeof(ULONG),
2602 &blockIndex);
2604 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2608 return blockIndex;
2611 /******************************************************************************
2612 * Storage32Impl_SetExtDepotBlock
2614 * Associates the specified block index to the specified depot index.
2615 * This method is only for depot indexes equal or greater than
2616 * COUNT_BBDEPOTINHEADER.
2618 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2619 ULONG depotIndex,
2620 ULONG blockIndex)
2622 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2623 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2624 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2625 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2626 ULONG extBlockIndex = This->extBigBlockDepotStart;
2628 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2630 while (extBlockCount > 0)
2632 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2633 extBlockCount--;
2636 if (extBlockIndex != BLOCK_UNUSED)
2638 BYTE* depotBuffer;
2640 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2642 if (depotBuffer != 0)
2644 StorageUtl_WriteDWord(depotBuffer,
2645 extBlockOffset * sizeof(ULONG),
2646 blockIndex);
2648 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2653 /******************************************************************************
2654 * Storage32Impl_AddExtBlockDepot
2656 * Creates an extended depot block.
2658 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2660 ULONG numExtBlocks = This->extBigBlockDepotCount;
2661 ULONG nextExtBlock = This->extBigBlockDepotStart;
2662 BYTE* depotBuffer = NULL;
2663 ULONG index = BLOCK_UNUSED;
2664 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2665 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2666 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2668 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2669 blocksPerDepotBlock;
2671 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2674 * The first extended block.
2676 This->extBigBlockDepotStart = index;
2678 else
2680 int i;
2682 * Follow the chain to the last one.
2684 for (i = 0; i < (numExtBlocks - 1); i++)
2686 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2690 * Add the new extended block to the chain.
2692 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2693 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2694 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2698 * Initialize this block.
2700 depotBuffer = StorageImpl_GetBigBlock(This, index);
2701 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2702 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2704 return index;
2707 /******************************************************************************
2708 * Storage32Impl_FreeBigBlock
2710 * This method will flag the specified block as free in the big block depot.
2712 void StorageImpl_FreeBigBlock(
2713 StorageImpl* This,
2714 ULONG blockIndex)
2716 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2718 if (blockIndex < This->prevFreeBlock)
2719 This->prevFreeBlock = blockIndex;
2722 /************************************************************************
2723 * Storage32Impl_GetNextBlockInChain
2725 * This method will retrieve the block index of the next big block in
2726 * in the chain.
2728 * Params: This - Pointer to the Storage object.
2729 * blockIndex - Index of the block to retrieve the chain
2730 * for.
2731 * nextBlockIndex - receives the return value.
2733 * Returns: This method returns the index of the next block in the chain.
2734 * It will return the constants:
2735 * BLOCK_SPECIAL - If the block given was not part of a
2736 * chain.
2737 * BLOCK_END_OF_CHAIN - If the block given was the last in
2738 * a chain.
2739 * BLOCK_UNUSED - If the block given was not past of a chain
2740 * and is available.
2741 * BLOCK_EXTBBDEPOT - This block is part of the extended
2742 * big block depot.
2744 * See Windows documentation for more details on IStorage methods.
2746 HRESULT StorageImpl_GetNextBlockInChain(
2747 StorageImpl* This,
2748 ULONG blockIndex,
2749 ULONG* nextBlockIndex)
2751 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2752 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2753 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2754 void* depotBuffer;
2755 ULONG depotBlockIndexPos;
2756 int index;
2758 *nextBlockIndex = BLOCK_SPECIAL;
2760 if(depotBlockCount >= This->bigBlockDepotCount)
2762 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2763 This->bigBlockDepotCount);
2764 return STG_E_READFAULT;
2768 * Cache the currently accessed depot block.
2770 if (depotBlockCount != This->indexBlockDepotCached)
2772 This->indexBlockDepotCached = depotBlockCount;
2774 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2776 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2778 else
2781 * We have to look in the extended depot.
2783 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2786 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2788 if (!depotBuffer)
2789 return STG_E_READFAULT;
2791 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2793 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2794 This->blockDepotCached[index] = *nextBlockIndex;
2796 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2799 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2801 return S_OK;
2804 /******************************************************************************
2805 * Storage32Impl_GetNextExtendedBlock
2807 * Given an extended block this method will return the next extended block.
2809 * NOTES:
2810 * The last ULONG of an extended block is the block index of the next
2811 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2812 * depot.
2814 * Return values:
2815 * - The index of the next extended block
2816 * - BLOCK_UNUSED: there is no next extended block.
2817 * - Any other return values denotes failure.
2819 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2821 ULONG nextBlockIndex = BLOCK_SPECIAL;
2822 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2823 void* depotBuffer;
2825 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2827 if (depotBuffer!=0)
2829 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2831 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2834 return nextBlockIndex;
2837 /******************************************************************************
2838 * Storage32Impl_SetNextBlockInChain
2840 * This method will write the index of the specified block's next block
2841 * in the big block depot.
2843 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2844 * do the following
2846 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2847 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2848 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2851 void StorageImpl_SetNextBlockInChain(
2852 StorageImpl* This,
2853 ULONG blockIndex,
2854 ULONG nextBlock)
2856 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2857 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2858 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2859 ULONG depotBlockIndexPos;
2860 void* depotBuffer;
2862 assert(depotBlockCount < This->bigBlockDepotCount);
2863 assert(blockIndex != nextBlock);
2865 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2867 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2869 else
2872 * We have to look in the extended depot.
2874 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2877 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2879 if (depotBuffer!=0)
2881 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2882 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2886 * Update the cached block depot, if necessary.
2888 if (depotBlockCount == This->indexBlockDepotCached)
2890 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2894 /******************************************************************************
2895 * Storage32Impl_LoadFileHeader
2897 * This method will read in the file header, i.e. big block index -1.
2899 HRESULT StorageImpl_LoadFileHeader(
2900 StorageImpl* This)
2902 HRESULT hr = STG_E_FILENOTFOUND;
2903 void* headerBigBlock = NULL;
2904 int index;
2907 * Get a pointer to the big block of data containing the header.
2909 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2912 * Extract the information from the header.
2914 if (headerBigBlock!=0)
2917 * Check for the "magic number" signature and return an error if it is not
2918 * found.
2920 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2922 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2923 return STG_E_OLDFORMAT;
2926 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2928 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2929 return STG_E_INVALIDHEADER;
2932 StorageUtl_ReadWord(
2933 headerBigBlock,
2934 OFFSET_BIGBLOCKSIZEBITS,
2935 &This->bigBlockSizeBits);
2937 StorageUtl_ReadWord(
2938 headerBigBlock,
2939 OFFSET_SMALLBLOCKSIZEBITS,
2940 &This->smallBlockSizeBits);
2942 StorageUtl_ReadDWord(
2943 headerBigBlock,
2944 OFFSET_BBDEPOTCOUNT,
2945 &This->bigBlockDepotCount);
2947 StorageUtl_ReadDWord(
2948 headerBigBlock,
2949 OFFSET_ROOTSTARTBLOCK,
2950 &This->rootStartBlock);
2952 StorageUtl_ReadDWord(
2953 headerBigBlock,
2954 OFFSET_SBDEPOTSTART,
2955 &This->smallBlockDepotStart);
2957 StorageUtl_ReadDWord(
2958 headerBigBlock,
2959 OFFSET_EXTBBDEPOTSTART,
2960 &This->extBigBlockDepotStart);
2962 StorageUtl_ReadDWord(
2963 headerBigBlock,
2964 OFFSET_EXTBBDEPOTCOUNT,
2965 &This->extBigBlockDepotCount);
2967 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2969 StorageUtl_ReadDWord(
2970 headerBigBlock,
2971 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2972 &(This->bigBlockDepotStart[index]));
2976 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2978 if ((1 << 2) == 4)
2980 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2981 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2983 else
2985 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2986 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2990 * Right now, the code is making some assumptions about the size of the
2991 * blocks, just make sure they are what we're expecting.
2993 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2994 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2996 WARN("Broken OLE storage file\n");
2997 hr = STG_E_INVALIDHEADER;
2999 else
3000 hr = S_OK;
3003 * Release the block.
3005 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3008 return hr;
3011 /******************************************************************************
3012 * Storage32Impl_SaveFileHeader
3014 * This method will save to the file the header, i.e. big block -1.
3016 void StorageImpl_SaveFileHeader(
3017 StorageImpl* This)
3019 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3020 int index;
3021 BOOL success;
3024 * Get a pointer to the big block of data containing the header.
3026 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3029 * If the block read failed, the file is probably new.
3031 if (!success)
3034 * Initialize for all unknown fields.
3036 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3039 * Initialize the magic number.
3041 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3044 * And a bunch of things we don't know what they mean
3046 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3047 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3048 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3049 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3053 * Write the information to the header.
3055 StorageUtl_WriteWord(
3056 headerBigBlock,
3057 OFFSET_BIGBLOCKSIZEBITS,
3058 This->bigBlockSizeBits);
3060 StorageUtl_WriteWord(
3061 headerBigBlock,
3062 OFFSET_SMALLBLOCKSIZEBITS,
3063 This->smallBlockSizeBits);
3065 StorageUtl_WriteDWord(
3066 headerBigBlock,
3067 OFFSET_BBDEPOTCOUNT,
3068 This->bigBlockDepotCount);
3070 StorageUtl_WriteDWord(
3071 headerBigBlock,
3072 OFFSET_ROOTSTARTBLOCK,
3073 This->rootStartBlock);
3075 StorageUtl_WriteDWord(
3076 headerBigBlock,
3077 OFFSET_SBDEPOTSTART,
3078 This->smallBlockDepotStart);
3080 StorageUtl_WriteDWord(
3081 headerBigBlock,
3082 OFFSET_SBDEPOTCOUNT,
3083 This->smallBlockDepotChain ?
3084 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3086 StorageUtl_WriteDWord(
3087 headerBigBlock,
3088 OFFSET_EXTBBDEPOTSTART,
3089 This->extBigBlockDepotStart);
3091 StorageUtl_WriteDWord(
3092 headerBigBlock,
3093 OFFSET_EXTBBDEPOTCOUNT,
3094 This->extBigBlockDepotCount);
3096 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3098 StorageUtl_WriteDWord(
3099 headerBigBlock,
3100 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3101 (This->bigBlockDepotStart[index]));
3105 * Write the big block back to the file.
3107 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3110 /******************************************************************************
3111 * Storage32Impl_ReadProperty
3113 * This method will read the specified property from the property chain.
3115 BOOL StorageImpl_ReadProperty(
3116 StorageImpl* This,
3117 ULONG index,
3118 StgProperty* buffer)
3120 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3121 ULARGE_INTEGER offsetInPropSet;
3122 BOOL readSuccessful;
3123 ULONG bytesRead;
3125 offsetInPropSet.u.HighPart = 0;
3126 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3128 readSuccessful = BlockChainStream_ReadAt(
3129 This->rootBlockChain,
3130 offsetInPropSet,
3131 PROPSET_BLOCK_SIZE,
3132 currentProperty,
3133 &bytesRead);
3135 if (readSuccessful)
3137 /* replace the name of root entry (often "Root Entry") by the file name */
3138 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3139 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3141 memset(buffer->name, 0, sizeof(buffer->name));
3142 memcpy(
3143 buffer->name,
3144 propName,
3145 PROPERTY_NAME_BUFFER_LEN );
3146 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3148 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3150 StorageUtl_ReadWord(
3151 currentProperty,
3152 OFFSET_PS_NAMELENGTH,
3153 &buffer->sizeOfNameString);
3155 StorageUtl_ReadDWord(
3156 currentProperty,
3157 OFFSET_PS_PREVIOUSPROP,
3158 &buffer->previousProperty);
3160 StorageUtl_ReadDWord(
3161 currentProperty,
3162 OFFSET_PS_NEXTPROP,
3163 &buffer->nextProperty);
3165 StorageUtl_ReadDWord(
3166 currentProperty,
3167 OFFSET_PS_DIRPROP,
3168 &buffer->dirProperty);
3170 StorageUtl_ReadGUID(
3171 currentProperty,
3172 OFFSET_PS_GUID,
3173 &buffer->propertyUniqueID);
3175 StorageUtl_ReadDWord(
3176 currentProperty,
3177 OFFSET_PS_TSS1,
3178 &buffer->timeStampS1);
3180 StorageUtl_ReadDWord(
3181 currentProperty,
3182 OFFSET_PS_TSD1,
3183 &buffer->timeStampD1);
3185 StorageUtl_ReadDWord(
3186 currentProperty,
3187 OFFSET_PS_TSS2,
3188 &buffer->timeStampS2);
3190 StorageUtl_ReadDWord(
3191 currentProperty,
3192 OFFSET_PS_TSD2,
3193 &buffer->timeStampD2);
3195 StorageUtl_ReadDWord(
3196 currentProperty,
3197 OFFSET_PS_STARTBLOCK,
3198 &buffer->startingBlock);
3200 StorageUtl_ReadDWord(
3201 currentProperty,
3202 OFFSET_PS_SIZE,
3203 &buffer->size.u.LowPart);
3205 buffer->size.u.HighPart = 0;
3208 return readSuccessful;
3211 /*********************************************************************
3212 * Write the specified property into the property chain
3214 BOOL StorageImpl_WriteProperty(
3215 StorageImpl* This,
3216 ULONG index,
3217 StgProperty* buffer)
3219 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3220 ULARGE_INTEGER offsetInPropSet;
3221 BOOL writeSuccessful;
3222 ULONG bytesWritten;
3224 offsetInPropSet.u.HighPart = 0;
3225 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3227 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3229 memcpy(
3230 currentProperty + OFFSET_PS_NAME,
3231 buffer->name,
3232 PROPERTY_NAME_BUFFER_LEN );
3234 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3236 StorageUtl_WriteWord(
3237 currentProperty,
3238 OFFSET_PS_NAMELENGTH,
3239 buffer->sizeOfNameString);
3241 StorageUtl_WriteDWord(
3242 currentProperty,
3243 OFFSET_PS_PREVIOUSPROP,
3244 buffer->previousProperty);
3246 StorageUtl_WriteDWord(
3247 currentProperty,
3248 OFFSET_PS_NEXTPROP,
3249 buffer->nextProperty);
3251 StorageUtl_WriteDWord(
3252 currentProperty,
3253 OFFSET_PS_DIRPROP,
3254 buffer->dirProperty);
3256 StorageUtl_WriteGUID(
3257 currentProperty,
3258 OFFSET_PS_GUID,
3259 &buffer->propertyUniqueID);
3261 StorageUtl_WriteDWord(
3262 currentProperty,
3263 OFFSET_PS_TSS1,
3264 buffer->timeStampS1);
3266 StorageUtl_WriteDWord(
3267 currentProperty,
3268 OFFSET_PS_TSD1,
3269 buffer->timeStampD1);
3271 StorageUtl_WriteDWord(
3272 currentProperty,
3273 OFFSET_PS_TSS2,
3274 buffer->timeStampS2);
3276 StorageUtl_WriteDWord(
3277 currentProperty,
3278 OFFSET_PS_TSD2,
3279 buffer->timeStampD2);
3281 StorageUtl_WriteDWord(
3282 currentProperty,
3283 OFFSET_PS_STARTBLOCK,
3284 buffer->startingBlock);
3286 StorageUtl_WriteDWord(
3287 currentProperty,
3288 OFFSET_PS_SIZE,
3289 buffer->size.u.LowPart);
3291 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3292 offsetInPropSet,
3293 PROPSET_BLOCK_SIZE,
3294 currentProperty,
3295 &bytesWritten);
3296 return writeSuccessful;
3299 BOOL StorageImpl_ReadBigBlock(
3300 StorageImpl* This,
3301 ULONG blockIndex,
3302 void* buffer)
3304 void* bigBlockBuffer;
3306 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3308 if (bigBlockBuffer!=0)
3310 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3312 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3314 return TRUE;
3317 return FALSE;
3320 BOOL StorageImpl_WriteBigBlock(
3321 StorageImpl* This,
3322 ULONG blockIndex,
3323 void* buffer)
3325 void* bigBlockBuffer;
3327 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3329 if (bigBlockBuffer!=0)
3331 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3333 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3335 return TRUE;
3338 return FALSE;
3341 void* StorageImpl_GetROBigBlock(
3342 StorageImpl* This,
3343 ULONG blockIndex)
3345 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3348 void* StorageImpl_GetBigBlock(
3349 StorageImpl* This,
3350 ULONG blockIndex)
3352 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3355 void StorageImpl_ReleaseBigBlock(
3356 StorageImpl* This,
3357 void* pBigBlock)
3359 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3362 /******************************************************************************
3363 * Storage32Impl_SmallBlocksToBigBlocks
3365 * This method will convert a small block chain to a big block chain.
3366 * The small block chain will be destroyed.
3368 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3369 StorageImpl* This,
3370 SmallBlockChainStream** ppsbChain)
3372 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3373 ULARGE_INTEGER size, offset;
3374 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3375 ULONG propertyIndex;
3376 BOOL successRead, successWrite;
3377 StgProperty chainProperty;
3378 BYTE *buffer;
3379 BlockChainStream *bbTempChain = NULL;
3380 BlockChainStream *bigBlockChain = NULL;
3383 * Create a temporary big block chain that doesn't have
3384 * an associated property. This temporary chain will be
3385 * used to copy data from small blocks to big blocks.
3387 bbTempChain = BlockChainStream_Construct(This,
3388 &bbHeadOfChain,
3389 PROPERTY_NULL);
3390 if(!bbTempChain) return NULL;
3392 * Grow the big block chain.
3394 size = SmallBlockChainStream_GetSize(*ppsbChain);
3395 BlockChainStream_SetSize(bbTempChain, size);
3398 * Copy the contents of the small block chain to the big block chain
3399 * by small block size increments.
3401 offset.u.LowPart = 0;
3402 offset.u.HighPart = 0;
3403 cbTotalRead = 0;
3404 cbTotalWritten = 0;
3406 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3409 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3410 offset,
3411 DEF_SMALL_BLOCK_SIZE,
3412 buffer,
3413 &cbRead);
3414 cbTotalRead += cbRead;
3416 successWrite = BlockChainStream_WriteAt(bbTempChain,
3417 offset,
3418 cbRead,
3419 buffer,
3420 &cbWritten);
3421 cbTotalWritten += cbWritten;
3423 offset.u.LowPart += This->smallBlockSize;
3425 } while (successRead && successWrite);
3426 HeapFree(GetProcessHeap(),0,buffer);
3428 assert(cbTotalRead == cbTotalWritten);
3431 * Destroy the small block chain.
3433 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3434 size.u.HighPart = 0;
3435 size.u.LowPart = 0;
3436 SmallBlockChainStream_SetSize(*ppsbChain, size);
3437 SmallBlockChainStream_Destroy(*ppsbChain);
3438 *ppsbChain = 0;
3441 * Change the property information. This chain is now a big block chain
3442 * and it doesn't reside in the small blocks chain anymore.
3444 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3446 chainProperty.startingBlock = bbHeadOfChain;
3448 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3451 * Destroy the temporary propertyless big block chain.
3452 * Create a new big block chain associated with this property.
3454 BlockChainStream_Destroy(bbTempChain);
3455 bigBlockChain = BlockChainStream_Construct(This,
3456 NULL,
3457 propertyIndex);
3459 return bigBlockChain;
3462 /******************************************************************************
3463 ** Storage32InternalImpl implementation
3466 StorageInternalImpl* StorageInternalImpl_Construct(
3467 StorageImpl* ancestorStorage,
3468 ULONG rootPropertyIndex)
3470 StorageInternalImpl* newStorage;
3473 * Allocate space for the new storage object
3475 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3477 if (newStorage!=0)
3479 memset(newStorage, 0, sizeof(StorageInternalImpl));
3482 * Initialize the virtual function table.
3484 newStorage->lpVtbl = &Storage32InternalImpl_Vtbl;
3485 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3488 * Keep the ancestor storage pointer and nail a reference to it.
3490 newStorage->ancestorStorage = ancestorStorage;
3491 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3494 * Keep the index of the root property set for this storage,
3496 newStorage->rootPropertySetIndex = rootPropertyIndex;
3498 return newStorage;
3501 return 0;
3504 void StorageInternalImpl_Destroy(
3505 StorageInternalImpl* This)
3507 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3508 HeapFree(GetProcessHeap(), 0, This);
3511 /******************************************************************************
3513 ** Storage32InternalImpl_Commit
3515 ** The non-root storages cannot be opened in transacted mode thus this function
3516 ** does nothing.
3518 HRESULT WINAPI StorageInternalImpl_Commit(
3519 IStorage* iface,
3520 DWORD grfCommitFlags) /* [in] */
3522 return S_OK;
3525 /******************************************************************************
3527 ** Storage32InternalImpl_Revert
3529 ** The non-root storages cannot be opened in transacted mode thus this function
3530 ** does nothing.
3532 HRESULT WINAPI StorageInternalImpl_Revert(
3533 IStorage* iface)
3535 return S_OK;
3538 /******************************************************************************
3539 ** IEnumSTATSTGImpl implementation
3542 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3543 StorageImpl* parentStorage,
3544 ULONG firstPropertyNode)
3546 IEnumSTATSTGImpl* newEnumeration;
3548 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3550 if (newEnumeration!=0)
3553 * Set-up the virtual function table and reference count.
3555 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3556 newEnumeration->ref = 0;
3559 * We want to nail-down the reference to the storage in case the
3560 * enumeration out-lives the storage in the client application.
3562 newEnumeration->parentStorage = parentStorage;
3563 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3565 newEnumeration->firstPropertyNode = firstPropertyNode;
3568 * Initialize the search stack
3570 newEnumeration->stackSize = 0;
3571 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3572 newEnumeration->stackToVisit =
3573 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3576 * Make sure the current node of the iterator is the first one.
3578 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3581 return newEnumeration;
3584 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3586 IStorage_Release((IStorage*)This->parentStorage);
3587 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3588 HeapFree(GetProcessHeap(), 0, This);
3591 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3592 IEnumSTATSTG* iface,
3593 REFIID riid,
3594 void** ppvObject)
3596 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3599 * Perform a sanity check on the parameters.
3601 if (ppvObject==0)
3602 return E_INVALIDARG;
3605 * Initialize the return parameter.
3607 *ppvObject = 0;
3610 * Compare the riid with the interface IDs implemented by this object.
3612 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3614 *ppvObject = (IEnumSTATSTG*)This;
3616 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3618 *ppvObject = (IEnumSTATSTG*)This;
3622 * Check that we obtained an interface.
3624 if ((*ppvObject)==0)
3625 return E_NOINTERFACE;
3628 * Query Interface always increases the reference count by one when it is
3629 * successful
3631 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3633 return S_OK;
3636 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3637 IEnumSTATSTG* iface)
3639 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3641 This->ref++;
3642 return This->ref;
3645 ULONG WINAPI IEnumSTATSTGImpl_Release(
3646 IEnumSTATSTG* iface)
3648 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3650 ULONG newRef;
3652 This->ref--;
3653 newRef = This->ref;
3656 * If the reference count goes down to 0, perform suicide.
3658 if (newRef==0)
3660 IEnumSTATSTGImpl_Destroy(This);
3663 return newRef;
3666 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3667 IEnumSTATSTG* iface,
3668 ULONG celt,
3669 STATSTG* rgelt,
3670 ULONG* pceltFetched)
3672 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3674 StgProperty currentProperty;
3675 STATSTG* currentReturnStruct = rgelt;
3676 ULONG objectFetched = 0;
3677 ULONG currentSearchNode;
3680 * Perform a sanity check on the parameters.
3682 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3683 return E_INVALIDARG;
3686 * To avoid the special case, get another pointer to a ULONG value if
3687 * the caller didn't supply one.
3689 if (pceltFetched==0)
3690 pceltFetched = &objectFetched;
3693 * Start the iteration, we will iterate until we hit the end of the
3694 * linked list or until we hit the number of items to iterate through
3696 *pceltFetched = 0;
3699 * Start with the node at the top of the stack.
3701 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3703 while ( ( *pceltFetched < celt) &&
3704 ( currentSearchNode!=PROPERTY_NULL) )
3707 * Remove the top node from the stack
3709 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3712 * Read the property from the storage.
3714 StorageImpl_ReadProperty(This->parentStorage,
3715 currentSearchNode,
3716 &currentProperty);
3719 * Copy the information to the return buffer.
3721 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3722 &currentProperty,
3723 STATFLAG_DEFAULT);
3726 * Step to the next item in the iteration
3728 (*pceltFetched)++;
3729 currentReturnStruct++;
3732 * Push the next search node in the search stack.
3734 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3737 * continue the iteration.
3739 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3742 if (*pceltFetched == celt)
3743 return S_OK;
3745 return S_FALSE;
3749 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3750 IEnumSTATSTG* iface,
3751 ULONG celt)
3753 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3755 StgProperty currentProperty;
3756 ULONG objectFetched = 0;
3757 ULONG currentSearchNode;
3760 * Start with the node at the top of the stack.
3762 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3764 while ( (objectFetched < celt) &&
3765 (currentSearchNode!=PROPERTY_NULL) )
3768 * Remove the top node from the stack
3770 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3773 * Read the property from the storage.
3775 StorageImpl_ReadProperty(This->parentStorage,
3776 currentSearchNode,
3777 &currentProperty);
3780 * Step to the next item in the iteration
3782 objectFetched++;
3785 * Push the next search node in the search stack.
3787 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3790 * continue the iteration.
3792 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3795 if (objectFetched == celt)
3796 return S_OK;
3798 return S_FALSE;
3801 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3802 IEnumSTATSTG* iface)
3804 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3806 StgProperty rootProperty;
3807 BOOL readSuccessful;
3810 * Re-initialize the search stack to an empty stack
3812 This->stackSize = 0;
3815 * Read the root property from the storage.
3817 readSuccessful = StorageImpl_ReadProperty(
3818 This->parentStorage,
3819 This->firstPropertyNode,
3820 &rootProperty);
3822 if (readSuccessful)
3824 assert(rootProperty.sizeOfNameString!=0);
3827 * Push the search node in the search stack.
3829 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3832 return S_OK;
3835 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3836 IEnumSTATSTG* iface,
3837 IEnumSTATSTG** ppenum)
3839 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3841 IEnumSTATSTGImpl* newClone;
3844 * Perform a sanity check on the parameters.
3846 if (ppenum==0)
3847 return E_INVALIDARG;
3849 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3850 This->firstPropertyNode);
3854 * The new clone enumeration must point to the same current node as
3855 * the ole one.
3857 newClone->stackSize = This->stackSize ;
3858 newClone->stackMaxSize = This->stackMaxSize ;
3859 newClone->stackToVisit =
3860 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3862 memcpy(
3863 newClone->stackToVisit,
3864 This->stackToVisit,
3865 sizeof(ULONG) * newClone->stackSize);
3867 *ppenum = (IEnumSTATSTG*)newClone;
3870 * Don't forget to nail down a reference to the clone before
3871 * returning it.
3873 IEnumSTATSTGImpl_AddRef(*ppenum);
3875 return S_OK;
3878 INT IEnumSTATSTGImpl_FindParentProperty(
3879 IEnumSTATSTGImpl *This,
3880 ULONG childProperty,
3881 StgProperty *currentProperty,
3882 ULONG *thisNodeId)
3884 ULONG currentSearchNode;
3885 ULONG foundNode;
3888 * To avoid the special case, get another pointer to a ULONG value if
3889 * the caller didn't supply one.
3891 if (thisNodeId==0)
3892 thisNodeId = &foundNode;
3895 * Start with the node at the top of the stack.
3897 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3900 while (currentSearchNode!=PROPERTY_NULL)
3903 * Store the current node in the returned parameters
3905 *thisNodeId = currentSearchNode;
3908 * Remove the top node from the stack
3910 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3913 * Read the property from the storage.
3915 StorageImpl_ReadProperty(
3916 This->parentStorage,
3917 currentSearchNode,
3918 currentProperty);
3920 if (currentProperty->previousProperty == childProperty)
3921 return PROPERTY_RELATION_PREVIOUS;
3923 else if (currentProperty->nextProperty == childProperty)
3924 return PROPERTY_RELATION_NEXT;
3926 else if (currentProperty->dirProperty == childProperty)
3927 return PROPERTY_RELATION_DIR;
3930 * Push the next search node in the search stack.
3932 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3935 * continue the iteration.
3937 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3940 return PROPERTY_NULL;
3943 ULONG IEnumSTATSTGImpl_FindProperty(
3944 IEnumSTATSTGImpl* This,
3945 const OLECHAR* lpszPropName,
3946 StgProperty* currentProperty)
3948 ULONG currentSearchNode;
3951 * Start with the node at the top of the stack.
3953 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3955 while (currentSearchNode!=PROPERTY_NULL)
3958 * Remove the top node from the stack
3960 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3963 * Read the property from the storage.
3965 StorageImpl_ReadProperty(This->parentStorage,
3966 currentSearchNode,
3967 currentProperty);
3969 if ( propertyNameCmp(
3970 (OLECHAR*)currentProperty->name,
3971 (OLECHAR*)lpszPropName) == 0)
3972 return currentSearchNode;
3975 * Push the next search node in the search stack.
3977 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3980 * continue the iteration.
3982 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3985 return PROPERTY_NULL;
3988 void IEnumSTATSTGImpl_PushSearchNode(
3989 IEnumSTATSTGImpl* This,
3990 ULONG nodeToPush)
3992 StgProperty rootProperty;
3993 BOOL readSuccessful;
3996 * First, make sure we're not trying to push an unexisting node.
3998 if (nodeToPush==PROPERTY_NULL)
3999 return;
4002 * First push the node to the stack
4004 if (This->stackSize == This->stackMaxSize)
4006 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4008 This->stackToVisit = HeapReAlloc(
4009 GetProcessHeap(),
4011 This->stackToVisit,
4012 sizeof(ULONG) * This->stackMaxSize);
4015 This->stackToVisit[This->stackSize] = nodeToPush;
4016 This->stackSize++;
4019 * Read the root property from the storage.
4021 readSuccessful = StorageImpl_ReadProperty(
4022 This->parentStorage,
4023 nodeToPush,
4024 &rootProperty);
4026 if (readSuccessful)
4028 assert(rootProperty.sizeOfNameString!=0);
4031 * Push the previous search node in the search stack.
4033 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4037 ULONG IEnumSTATSTGImpl_PopSearchNode(
4038 IEnumSTATSTGImpl* This,
4039 BOOL remove)
4041 ULONG topNode;
4043 if (This->stackSize == 0)
4044 return PROPERTY_NULL;
4046 topNode = This->stackToVisit[This->stackSize-1];
4048 if (remove)
4049 This->stackSize--;
4051 return topNode;
4054 /******************************************************************************
4055 ** StorageUtl implementation
4058 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4060 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4063 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4065 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4068 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4070 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4073 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4075 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4078 void StorageUtl_ReadGUID(void* 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, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4087 void StorageUtl_WriteGUID(void* buffer, ULONG offset, 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((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4096 void StorageUtl_CopyPropertyToSTATSTG(
4097 STATSTG* destination,
4098 StgProperty* source,
4099 int statFlags)
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;
4110 else
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:
4121 case PROPTYPE_ROOT:
4122 destination->type = STGTY_STORAGE;
4123 break;
4124 case PROPTYPE_STREAM:
4125 destination->type = STGTY_STREAM;
4126 break;
4127 default:
4128 destination->type = STGTY_STREAM;
4129 break;
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;
4155 ULONG blockIndex;
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(
4174 parentStorage,
4175 blockIndex,
4176 &blockIndex)))
4178 HeapFree(GetProcessHeap(), 0, newStream);
4179 return NULL;
4183 return 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,
4212 &chainProperty);
4214 if (readSuccessful)
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)
4232 ULONG blockIndex;
4233 ULONG count = 0;
4235 blockIndex = BlockChainStream_GetHeadOfChain(This);
4237 while (blockIndex != BLOCK_END_OF_CHAIN)
4239 count++;
4241 if(FAILED(StorageImpl_GetNextBlockInChain(
4242 This->parentStorage,
4243 blockIndex,
4244 &blockIndex)))
4245 return 0;
4248 return count;
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,
4260 ULONG size,
4261 void* buffer,
4262 ULONG* bytesRead)
4264 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4265 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4266 ULONG bytesToReadInBuffer;
4267 ULONG blockIndex;
4268 BYTE* bufferWalker;
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;
4281 else
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)))
4293 return FALSE;
4294 blockNoInSequence--;
4297 This->lastBlockNoInSequenceIndex = blockIndex;
4300 * Start reading the buffer.
4302 *bytesRead = 0;
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
4316 bigBlockBuffer =
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)))
4327 return FALSE;
4329 bufferWalker += bytesToReadInBuffer;
4330 size -= bytesToReadInBuffer;
4331 *bytesRead += bytesToReadInBuffer;
4332 offsetInBlock = 0; /* There is no offset on the next block */
4336 return (size == 0);
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,
4348 ULONG size,
4349 const void* buffer,
4350 ULONG* bytesWritten)
4352 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4353 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4354 ULONG bytesToWrite;
4355 ULONG blockIndex;
4356 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;
4369 else
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,
4381 &blockIndex)))
4382 return FALSE;
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.
4392 *bytesWritten = 0;
4393 bufferWalker = (BYTE*)buffer;
4395 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4398 * Calculate how many bytes we can copy from this big block.
4400 bytesToWrite =
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,
4416 &blockIndex)))
4417 return FALSE;
4418 bufferWalker += bytesToWrite;
4419 size -= bytesToWrite;
4420 *bytesWritten += bytesToWrite;
4421 offsetInBlock = 0; /* There is no offset on the next block */
4424 return (size == 0);
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;
4436 ULONG numBlocks;
4437 ULONG count = 1;
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)
4451 numBlocks++;
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,
4461 &blockIndex)))
4462 return FALSE;
4463 count++;
4466 /* Get the next block before marking the new end */
4467 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4468 &extraBlock)))
4469 return FALSE;
4471 /* Mark the new end of chain */
4472 StorageImpl_SetNextBlockInChain(
4473 This->parentStorage,
4474 blockIndex,
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,
4486 &blockIndex)))
4487 return FALSE;
4488 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4489 extraBlock = blockIndex;
4492 return TRUE;
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;
4504 ULONG newNumBlocks;
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,
4516 blockIndex,
4517 BLOCK_END_OF_CHAIN);
4519 if (This->headOfStreamPlaceHolder != 0)
4521 *(This->headOfStreamPlaceHolder) = blockIndex;
4523 else
4525 StgProperty chainProp;
4526 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4528 StorageImpl_ReadProperty(
4529 This->parentStorage,
4530 This->ownerPropertyIndex,
4531 &chainProp);
4533 chainProp.startingBlock = blockIndex;
4535 StorageImpl_WriteProperty(
4536 This->parentStorage,
4537 This->ownerPropertyIndex,
4538 &chainProp);
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)
4551 newNumBlocks++;
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)
4562 This->numBlocks++;
4563 currentBlock = blockIndex;
4565 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4566 &blockIndex)))
4567 return FALSE;
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,
4587 currentBlock,
4588 blockIndex);
4590 StorageImpl_SetNextBlockInChain(
4591 This->parentStorage,
4592 blockIndex,
4593 BLOCK_END_OF_CHAIN);
4595 currentBlock = blockIndex;
4596 oldNumBlocks++;
4599 This->tailIndex = blockIndex;
4600 This->numBlocks = newNumBlocks;
4603 return TRUE;
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)
4623 return TRUE;
4625 if (newSize.u.LowPart < size.u.LowPart)
4627 BlockChainStream_Shrink(This, newSize);
4629 else
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);
4649 return TRUE;
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,
4671 &chainProperty);
4673 return chainProperty.size;
4675 else
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
4680 * size of them
4682 ULARGE_INTEGER result;
4683 result.u.HighPart = 0;
4685 result.u.LowPart =
4686 BlockChainStream_GetCount(This) *
4687 This->parentStorage->bigBlockSize;
4689 return result;
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;
4708 return newStream;
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,
4733 &chainProperty);
4735 if (readSuccessful)
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.
4750 * Return Values:
4751 * - BLOCK_END_OF_CHAIN: end of this chain
4752 * - BLOCK_UNUSED: small block 'blockIndex' is free
4754 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4755 SmallBlockChainStream* This,
4756 ULONG blockIndex,
4757 ULONG* nextBlockInChain)
4759 ULARGE_INTEGER offsetOfBlockInDepot;
4760 DWORD buffer;
4761 ULONG bytesRead;
4762 BOOL success;
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,
4775 sizeof(DWORD),
4776 &buffer,
4777 &bytesRead);
4779 if (success)
4781 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4782 return S_OK;
4785 return STG_E_READFAULT;
4788 /******************************************************************************
4789 * SmallBlockChainStream_SetNextBlockInChain
4791 * Writes the index of the next block of the specified block in the small
4792 * block depot.
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,
4798 ULONG blockIndex,
4799 ULONG nextBlock)
4801 ULARGE_INTEGER offsetOfBlockInDepot;
4802 DWORD buffer;
4803 ULONG bytesWritten;
4805 offsetOfBlockInDepot.u.HighPart = 0;
4806 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4808 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4811 * Read those bytes in the buffer from the small block file.
4813 BlockChainStream_WriteAt(
4814 This->parentStorage->smallBlockDepotChain,
4815 offsetOfBlockInDepot,
4816 sizeof(DWORD),
4817 &buffer,
4818 &bytesWritten);
4821 /******************************************************************************
4822 * SmallBlockChainStream_FreeBlock
4824 * Flag small block 'blockIndex' as free in the small block depot.
4826 void SmallBlockChainStream_FreeBlock(
4827 SmallBlockChainStream* This,
4828 ULONG blockIndex)
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
4838 * necessary.
4840 ULONG SmallBlockChainStream_GetNextFreeBlock(
4841 SmallBlockChainStream* This)
4843 ULARGE_INTEGER offsetOfBlockInDepot;
4844 DWORD buffer;
4845 ULONG bytesRead;
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,
4863 sizeof(DWORD),
4864 &buffer,
4865 &bytesRead);
4868 * If we run out of space for the small block depot, enlarge it
4870 if (success)
4872 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4874 if (nextBlockIndex != BLOCK_UNUSED)
4875 blockIndex++;
4877 else
4879 ULONG count =
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,
4897 sbdIndex,
4898 newsbdIndex);
4900 StorageImpl_SetNextBlockInChain(
4901 This->parentStorage,
4902 newsbdIndex,
4903 BLOCK_END_OF_CHAIN);
4906 * Initialize all the small blocks to free
4908 smallBlockDepot =
4909 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4911 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4912 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4914 if (count == 0)
4917 * We have just created the small block depot.
4919 StgProperty rootProp;
4920 ULONG sbStartIndex;
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
4931 sbStartIndex =
4932 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4934 StorageImpl_SetNextBlockInChain(
4935 This->parentStorage,
4936 sbStartIndex,
4937 BLOCK_END_OF_CHAIN);
4939 StorageImpl_ReadProperty(
4940 This->parentStorage,
4941 This->parentStorage->rootPropertySetIndex,
4942 &rootProp);
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->rootPropertySetIndex,
4951 &rootProp);
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->rootPropertySetIndex,
4970 &rootProp);
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,
4979 rootProp.size);
4981 StorageImpl_WriteProperty(
4982 This->parentStorage,
4983 This->parentStorage->rootPropertySetIndex,
4984 &rootProp);
4988 return blockIndex;
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,
5001 ULONG size,
5002 void* buffer,
5003 ULONG* bytesRead)
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;
5011 ULONG blockIndex;
5012 ULONG bytesReadFromBigBlockFile;
5013 BYTE* bufferWalker;
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,
5028 &blockIndex)))
5029 return FALSE;
5030 blockNoInSequence--;
5034 * Start reading the buffer.
5036 *bytesRead = 0;
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,
5062 bufferWalker,
5063 &bytesReadFromBigBlockFile);
5065 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5068 * Step to the next big block.
5070 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5071 return FALSE;
5072 bufferWalker += bytesToReadInBuffer;
5073 size -= bytesToReadInBuffer;
5074 *bytesRead += bytesToReadInBuffer;
5075 offsetInBlock = 0; /* There is no offset on the next block */
5078 return (size == 0);
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,
5091 ULONG size,
5092 const void* buffer,
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;
5101 ULONG blockIndex;
5102 ULONG bytesWrittenFromBigBlockFile;
5103 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)))
5118 return FALSE;
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.
5128 *bytesWritten = 0;
5129 bufferWalker = (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,
5153 bufferWalker,
5154 &bytesWrittenFromBigBlockFile);
5156 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5159 * Step to the next big block.
5161 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5162 &blockIndex)))
5163 return FALSE;
5164 bufferWalker += bytesToWriteInBuffer;
5165 size -= bytesToWriteInBuffer;
5166 *bytesWritten += bytesToWriteInBuffer;
5167 offsetInBlock = 0; /* There is no offset on the next block */
5170 return (size == 0);
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;
5183 ULONG numBlocks;
5184 ULONG count = 0;
5186 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5188 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5189 numBlocks++;
5191 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5194 * Go to the new end of chain
5196 while (count < numBlocks)
5198 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5199 &blockIndex)))
5200 return FALSE;
5201 count++;
5205 * If the count is 0, we have a special case, the head of the chain was
5206 * just freed.
5208 if (count == 0)
5210 StgProperty chainProp;
5212 StorageImpl_ReadProperty(This->parentStorage,
5213 This->ownerPropertyIndex,
5214 &chainProp);
5216 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5218 StorageImpl_WriteProperty(This->parentStorage,
5219 This->ownerPropertyIndex,
5220 &chainProp);
5223 * We start freeing the chain at the head block.
5225 extraBlock = blockIndex;
5227 else
5229 /* Get the next block before marking the new end */
5230 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5231 &extraBlock)))
5232 return FALSE;
5234 /* Mark the new end of chain */
5235 SmallBlockChainStream_SetNextBlockInChain(
5236 This,
5237 blockIndex,
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,
5247 &blockIndex)))
5248 return FALSE;
5249 SmallBlockChainStream_FreeBlock(This, extraBlock);
5250 extraBlock = blockIndex;
5253 return TRUE;
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;
5266 ULONG newNumBlocks;
5267 ULONG oldNumBlocks = 0;
5269 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5272 * Empty chain
5274 if (blockIndex == BLOCK_END_OF_CHAIN)
5277 StgProperty chainProp;
5279 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5280 &chainProp);
5282 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5284 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5285 &chainProp);
5287 blockIndex = chainProp.startingBlock;
5288 SmallBlockChainStream_SetNextBlockInChain(
5289 This,
5290 blockIndex,
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)
5302 newNumBlocks++;
5305 * Go to the current end of chain
5307 while (blockIndex != BLOCK_END_OF_CHAIN)
5309 oldNumBlocks++;
5310 currentBlock = blockIndex;
5311 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5312 return FALSE;
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(
5324 This,
5325 blockIndex,
5326 BLOCK_END_OF_CHAIN);
5328 currentBlock = blockIndex;
5329 oldNumBlocks++;
5332 return TRUE;
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)
5343 ULONG blockIndex;
5344 ULONG count = 0;
5346 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5348 while (blockIndex != BLOCK_END_OF_CHAIN)
5350 count++;
5352 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5353 return 0;
5356 return count;
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)
5376 return TRUE;
5378 if (newSize.u.LowPart < size.u.LowPart)
5380 SmallBlockChainStream_Shrink(This, newSize);
5382 else
5384 SmallBlockChainStream_Enlarge(This, newSize);
5387 return TRUE;
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,
5402 &chainProperty);
5404 return chainProperty.size;
5407 /******************************************************************************
5408 * StgCreateDocfile [OLE32.@]
5410 HRESULT WINAPI StgCreateDocfile(
5411 LPCOLESTR pwcsName,
5412 DWORD grfMode,
5413 DWORD reserved,
5414 IStorage **ppstgOpen)
5416 StorageImpl* newStorage = 0;
5417 HANDLE hFile = INVALID_HANDLE_VALUE;
5418 HRESULT hr = S_OK;
5419 DWORD shareMode;
5420 DWORD accessMode;
5421 DWORD creationMode;
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
5432 if (ppstgOpen == 0)
5433 return STG_E_INVALIDPOINTER;
5436 * Validate the STGM flags
5438 if ( FAILED( validateSTGM(grfMode) ))
5439 return STG_E_INVALIDFLAG;
5442 * Generate a unique name.
5444 if (pwcsName == 0)
5446 WCHAR tempPath[MAX_PATH];
5447 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5449 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5450 return STG_E_INVALIDFLAG;
5451 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5452 return STG_E_INVALIDFLAG;
5454 memset(tempPath, 0, sizeof(tempPath));
5455 memset(tempFileName, 0, sizeof(tempFileName));
5457 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5458 tempPath[0] = '.';
5460 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5461 pwcsName = tempFileName;
5462 else
5463 return STG_E_INSUFFICIENTMEMORY;
5465 creationMode = TRUNCATE_EXISTING;
5467 else
5469 creationMode = GetCreationModeFromSTGM(grfMode);
5473 * Interpret the STGM value grfMode
5475 shareMode = GetShareModeFromSTGM(grfMode);
5476 accessMode = GetAccessModeFromSTGM(grfMode);
5478 if (grfMode & STGM_DELETEONRELEASE)
5479 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5480 else
5481 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5483 if (grfMode & STGM_TRANSACTED)
5484 FIXME("Transacted mode not implemented.\n");
5487 * Initialize the "out" parameter.
5489 *ppstgOpen = 0;
5491 hFile = CreateFileW(pwcsName,
5492 accessMode,
5493 shareMode,
5494 NULL,
5495 creationMode,
5496 fileAttributes,
5499 if (hFile == INVALID_HANDLE_VALUE)
5501 return E_FAIL;
5505 * Allocate and initialize the new IStorage32object.
5507 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5509 if (newStorage == 0)
5510 return STG_E_INSUFFICIENTMEMORY;
5512 hr = StorageImpl_Construct(
5513 newStorage,
5514 hFile,
5515 pwcsName,
5516 NULL,
5517 grfMode,
5518 TRUE,
5519 TRUE);
5521 if (FAILED(hr))
5523 HeapFree(GetProcessHeap(), 0, newStorage);
5524 return hr;
5528 * Get an "out" pointer for the caller.
5530 hr = StorageBaseImpl_QueryInterface(
5531 (IStorage*)newStorage,
5532 (REFIID)&IID_IStorage,
5533 (void**)ppstgOpen);
5535 return hr;
5538 /******************************************************************************
5539 * StgCreateStorageEx [OLE32.@]
5541 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5543 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5544 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5545 return STG_E_UNIMPLEMENTEDFUNCTION;
5548 /******************************************************************************
5549 * StgOpenStorage [OLE32.@]
5551 HRESULT WINAPI StgOpenStorage(
5552 const OLECHAR *pwcsName,
5553 IStorage *pstgPriority,
5554 DWORD grfMode,
5555 SNB snbExclude,
5556 DWORD reserved,
5557 IStorage **ppstgOpen)
5559 StorageImpl* newStorage = 0;
5560 HRESULT hr = S_OK;
5561 HANDLE hFile = 0;
5562 DWORD shareMode;
5563 DWORD accessMode;
5564 WCHAR fullname[MAX_PATH];
5565 DWORD length;
5567 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5568 debugstr_w(pwcsName), pstgPriority, grfMode,
5569 snbExclude, reserved, ppstgOpen);
5572 * Perform a sanity check
5574 if (( pwcsName == 0) || (ppstgOpen == 0) )
5576 hr = STG_E_INVALIDPOINTER;
5577 goto end;
5581 * Validate the STGM flags
5583 if ( FAILED( validateSTGM(grfMode) ))
5585 hr = STG_E_INVALIDFLAG;
5586 goto end;
5590 * Interpret the STGM value grfMode
5592 shareMode = GetShareModeFromSTGM(grfMode);
5593 accessMode = GetAccessModeFromSTGM(grfMode);
5596 * Initialize the "out" parameter.
5598 *ppstgOpen = 0;
5600 hFile = CreateFileW( pwcsName,
5601 accessMode,
5602 shareMode,
5603 NULL,
5604 OPEN_EXISTING,
5605 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5608 length = GetFileSize(hFile, NULL);
5610 if (hFile==INVALID_HANDLE_VALUE)
5612 DWORD last_error = GetLastError();
5614 hr = E_FAIL;
5616 switch (last_error)
5618 case ERROR_FILE_NOT_FOUND:
5619 hr = STG_E_FILENOTFOUND;
5620 break;
5622 case ERROR_PATH_NOT_FOUND:
5623 hr = STG_E_PATHNOTFOUND;
5624 break;
5626 case ERROR_ACCESS_DENIED:
5627 case ERROR_WRITE_PROTECT:
5628 hr = STG_E_ACCESSDENIED;
5629 break;
5631 case ERROR_SHARING_VIOLATION:
5632 hr = STG_E_SHAREVIOLATION;
5633 break;
5635 default:
5636 hr = E_FAIL;
5639 goto end;
5643 * Allocate and initialize the new IStorage32object.
5645 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5647 if (newStorage == 0)
5649 hr = STG_E_INSUFFICIENTMEMORY;
5650 goto end;
5653 /* if the file's length was zero, initialize the storage */
5654 hr = StorageImpl_Construct(
5655 newStorage,
5656 hFile,
5657 pwcsName,
5658 NULL,
5659 grfMode,
5660 TRUE,
5661 !length );
5663 if (FAILED(hr))
5665 HeapFree(GetProcessHeap(), 0, newStorage);
5667 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5669 if(hr == STG_E_INVALIDHEADER)
5670 hr = STG_E_FILEALREADYEXISTS;
5671 goto end;
5674 /* prepare the file name string given in lieu of the root property name */
5675 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5676 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5677 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5680 * Get an "out" pointer for the caller.
5682 hr = StorageBaseImpl_QueryInterface(
5683 (IStorage*)newStorage,
5684 (REFIID)&IID_IStorage,
5685 (void**)ppstgOpen);
5687 end:
5688 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5689 return hr;
5692 /******************************************************************************
5693 * StgCreateDocfileOnILockBytes [OLE32.@]
5695 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5696 ILockBytes *plkbyt,
5697 DWORD grfMode,
5698 DWORD reserved,
5699 IStorage** ppstgOpen)
5701 StorageImpl* newStorage = 0;
5702 HRESULT hr = S_OK;
5705 * Validate the parameters
5707 if ((ppstgOpen == 0) || (plkbyt == 0))
5708 return STG_E_INVALIDPOINTER;
5711 * Allocate and initialize the new IStorage object.
5713 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5715 if (newStorage == 0)
5716 return STG_E_INSUFFICIENTMEMORY;
5718 hr = StorageImpl_Construct(
5719 newStorage,
5722 plkbyt,
5723 grfMode,
5724 FALSE,
5725 TRUE);
5727 if (FAILED(hr))
5729 HeapFree(GetProcessHeap(), 0, newStorage);
5730 return hr;
5734 * Get an "out" pointer for the caller.
5736 hr = StorageBaseImpl_QueryInterface(
5737 (IStorage*)newStorage,
5738 (REFIID)&IID_IStorage,
5739 (void**)ppstgOpen);
5741 return hr;
5744 /******************************************************************************
5745 * StgOpenStorageOnILockBytes [OLE32.@]
5747 HRESULT WINAPI StgOpenStorageOnILockBytes(
5748 ILockBytes *plkbyt,
5749 IStorage *pstgPriority,
5750 DWORD grfMode,
5751 SNB snbExclude,
5752 DWORD reserved,
5753 IStorage **ppstgOpen)
5755 StorageImpl* newStorage = 0;
5756 HRESULT hr = S_OK;
5759 * Perform a sanity check
5761 if ((plkbyt == 0) || (ppstgOpen == 0))
5762 return STG_E_INVALIDPOINTER;
5765 * Validate the STGM flags
5767 if ( FAILED( validateSTGM(grfMode) ))
5768 return STG_E_INVALIDFLAG;
5771 * Initialize the "out" parameter.
5773 *ppstgOpen = 0;
5776 * Allocate and initialize the new IStorage object.
5778 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5780 if (newStorage == 0)
5781 return STG_E_INSUFFICIENTMEMORY;
5783 hr = StorageImpl_Construct(
5784 newStorage,
5787 plkbyt,
5788 grfMode,
5789 FALSE,
5790 FALSE);
5792 if (FAILED(hr))
5794 HeapFree(GetProcessHeap(), 0, newStorage);
5795 return hr;
5799 * Get an "out" pointer for the caller.
5801 hr = StorageBaseImpl_QueryInterface(
5802 (IStorage*)newStorage,
5803 (REFIID)&IID_IStorage,
5804 (void**)ppstgOpen);
5806 return hr;
5809 /******************************************************************************
5810 * StgSetTimes [ole32.@]
5811 * StgSetTimes [OLE32.@]
5815 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *a,
5816 FILETIME const *b, FILETIME const *c )
5818 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5819 return S_OK;
5822 /******************************************************************************
5823 * StgIsStorageILockBytes [OLE32.@]
5825 * Determines if the ILockBytes contains a storage object.
5827 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5829 BYTE sig[8];
5830 ULARGE_INTEGER offset;
5832 offset.u.HighPart = 0;
5833 offset.u.LowPart = 0;
5835 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5837 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5838 return S_OK;
5840 return S_FALSE;
5843 /******************************************************************************
5844 * WriteClassStg [OLE32.@]
5846 * This method will store the specified CLSID in the specified storage object
5848 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5850 HRESULT hRes;
5852 assert(pStg != 0);
5854 hRes = IStorage_SetClass(pStg, rclsid);
5856 return hRes;
5859 /***********************************************************************
5860 * ReadClassStg (OLE32.@)
5862 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5864 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5866 STATSTG pstatstg;
5867 HRESULT hRes;
5869 TRACE("()\n");
5871 if(pclsid==NULL)
5872 return E_POINTER;
5874 * read a STATSTG structure (contains the clsid) from the storage
5876 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5878 if(SUCCEEDED(hRes))
5879 *pclsid=pstatstg.clsid;
5881 return hRes;
5884 /***********************************************************************
5885 * OleLoadFromStream (OLE32.@)
5887 * This function loads an object from stream
5889 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5891 CLSID clsid;
5892 HRESULT res;
5893 LPPERSISTSTREAM xstm;
5895 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5897 res=ReadClassStm(pStm,&clsid);
5898 if (!SUCCEEDED(res))
5899 return res;
5900 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5901 if (!SUCCEEDED(res))
5902 return res;
5903 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5904 if (!SUCCEEDED(res)) {
5905 IUnknown_Release((IUnknown*)*ppvObj);
5906 return res;
5908 res=IPersistStream_Load(xstm,pStm);
5909 IPersistStream_Release(xstm);
5910 /* FIXME: all refcounts ok at this point? I think they should be:
5911 * pStm : unchanged
5912 * ppvObj : 1
5913 * xstm : 0 (released)
5915 return res;
5918 /***********************************************************************
5919 * OleSaveToStream (OLE32.@)
5921 * This function saves an object with the IPersistStream interface on it
5922 * to the specified stream.
5924 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5927 CLSID clsid;
5928 HRESULT res;
5930 TRACE("(%p,%p)\n",pPStm,pStm);
5932 res=IPersistStream_GetClassID(pPStm,&clsid);
5934 if (SUCCEEDED(res)){
5936 res=WriteClassStm(pStm,&clsid);
5938 if (SUCCEEDED(res))
5940 res=IPersistStream_Save(pPStm,pStm,TRUE);
5943 TRACE("Finished Save\n");
5944 return res;
5947 /****************************************************************************
5948 * This method validate a STGM parameter that can contain the values below
5950 * STGM_DIRECT 0x00000000
5951 * STGM_TRANSACTED 0x00010000
5952 * STGM_SIMPLE 0x08000000
5954 * STGM_READ 0x00000000
5955 * STGM_WRITE 0x00000001
5956 * STGM_READWRITE 0x00000002
5958 * STGM_SHARE_DENY_NONE 0x00000040
5959 * STGM_SHARE_DENY_READ 0x00000030
5960 * STGM_SHARE_DENY_WRITE 0x00000020
5961 * STGM_SHARE_EXCLUSIVE 0x00000010
5963 * STGM_PRIORITY 0x00040000
5964 * STGM_DELETEONRELEASE 0x04000000
5966 * STGM_CREATE 0x00001000
5967 * STGM_CONVERT 0x00020000
5968 * STGM_FAILIFTHERE 0x00000000
5970 * STGM_NOSCRATCH 0x00100000
5971 * STGM_NOSNAPSHOT 0x00200000
5973 static HRESULT validateSTGM(DWORD stgm)
5975 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5976 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5977 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5979 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5980 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5981 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5983 BOOL bSTGM_SHARE_DENY_NONE =
5984 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5986 BOOL bSTGM_SHARE_DENY_READ =
5987 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5989 BOOL bSTGM_SHARE_DENY_WRITE =
5990 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5992 BOOL bSTGM_SHARE_EXCLUSIVE =
5993 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5995 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5996 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5998 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5999 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
6002 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6004 if ( ! bSTGM_DIRECT )
6005 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
6006 return E_FAIL;
6009 * STGM_WRITE | STGM_READWRITE | STGM_READ
6011 if ( ! bSTGM_READ )
6012 if( bSTGM_WRITE && bSTGM_READWRITE )
6013 return E_FAIL;
6016 * STGM_SHARE_DENY_NONE | others
6017 * (I assume here that DENY_READ implies DENY_WRITE)
6019 if ( bSTGM_SHARE_DENY_NONE )
6020 if ( bSTGM_SHARE_DENY_READ ||
6021 bSTGM_SHARE_DENY_WRITE ||
6022 bSTGM_SHARE_EXCLUSIVE)
6023 return E_FAIL;
6026 * STGM_CREATE | STGM_CONVERT
6027 * if both are false, STGM_FAILIFTHERE is set to TRUE
6029 if ( bSTGM_CREATE && bSTGM_CONVERT )
6030 return E_FAIL;
6033 * STGM_NOSCRATCH requires STGM_TRANSACTED
6035 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6036 return E_FAIL;
6039 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6040 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6042 if (bSTGM_NOSNAPSHOT)
6044 if ( ! ( bSTGM_TRANSACTED &&
6045 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6046 return E_FAIL;
6049 return S_OK;
6052 /****************************************************************************
6053 * GetShareModeFromSTGM
6055 * This method will return a share mode flag from a STGM value.
6056 * The STGM value is assumed valid.
6058 static DWORD GetShareModeFromSTGM(DWORD stgm)
6060 DWORD dwShareMode = 0;
6061 BOOL bSTGM_SHARE_DENY_NONE =
6062 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6064 BOOL bSTGM_SHARE_DENY_READ =
6065 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6067 BOOL bSTGM_SHARE_DENY_WRITE =
6068 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6070 BOOL bSTGM_SHARE_EXCLUSIVE =
6071 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6073 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6074 dwShareMode = 0;
6076 if (bSTGM_SHARE_DENY_NONE)
6077 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6079 if (bSTGM_SHARE_DENY_WRITE)
6080 dwShareMode = FILE_SHARE_READ;
6082 return dwShareMode;
6085 /****************************************************************************
6086 * GetAccessModeFromSTGM
6088 * This method will return an access mode flag from a STGM value.
6089 * The STGM value is assumed valid.
6091 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6093 DWORD dwDesiredAccess = GENERIC_READ;
6094 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6095 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6096 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6098 if (bSTGM_READ)
6099 dwDesiredAccess = GENERIC_READ;
6101 if (bSTGM_WRITE)
6102 dwDesiredAccess |= GENERIC_WRITE;
6104 if (bSTGM_READWRITE)
6105 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6107 return dwDesiredAccess;
6110 /****************************************************************************
6111 * GetCreationModeFromSTGM
6113 * This method will return a creation mode flag from a STGM value.
6114 * The STGM value is assumed valid.
6116 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6118 if ( stgm & STGM_CREATE)
6119 return CREATE_ALWAYS;
6120 if (stgm & STGM_CONVERT) {
6121 FIXME("STGM_CONVERT not implemented!\n");
6122 return CREATE_NEW;
6124 /* All other cases */
6125 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6126 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6127 return CREATE_NEW;
6131 /*************************************************************************
6132 * OLECONVERT_LoadOLE10 [Internal]
6134 * Loads the OLE10 STREAM to memory
6136 * PARAMS
6137 * pOleStream [I] The OLESTREAM
6138 * pData [I] Data Structure for the OLESTREAM Data
6140 * RETURNS
6141 * Success: S_OK
6142 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6143 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6145 * NOTES
6146 * This function is used by OleConvertOLESTREAMToIStorage only.
6148 * Memory allocated for pData must be freed by the caller
6150 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6152 DWORD dwSize;
6153 HRESULT hRes = S_OK;
6154 int nTryCnt=0;
6155 int max_try = 6;
6157 pData->pData = NULL;
6158 pData->pstrOleObjFileName = (CHAR *) NULL;
6160 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6162 /* Get the OleID */
6163 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6164 if(dwSize != sizeof(pData->dwOleID))
6166 hRes = CONVERT10_E_OLESTREAM_GET;
6168 else if(pData->dwOleID != OLESTREAM_ID)
6170 hRes = CONVERT10_E_OLESTREAM_FMT;
6172 else
6174 hRes = S_OK;
6175 break;
6179 if(hRes == S_OK)
6181 /* Get the TypeID...more info needed for this field */
6182 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6183 if(dwSize != sizeof(pData->dwTypeID))
6185 hRes = CONVERT10_E_OLESTREAM_GET;
6188 if(hRes == S_OK)
6190 if(pData->dwTypeID != 0)
6192 /* Get the length of the OleTypeName */
6193 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6194 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6196 hRes = CONVERT10_E_OLESTREAM_GET;
6199 if(hRes == S_OK)
6201 if(pData->dwOleTypeNameLength > 0)
6203 /* Get the OleTypeName */
6204 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6205 if(dwSize != pData->dwOleTypeNameLength)
6207 hRes = CONVERT10_E_OLESTREAM_GET;
6211 if(bStrem1)
6213 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6214 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6216 hRes = CONVERT10_E_OLESTREAM_GET;
6218 if(hRes == S_OK)
6220 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6221 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6222 pData->pstrOleObjFileName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6223 if(pData->pstrOleObjFileName)
6225 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6226 if(dwSize != pData->dwOleObjFileNameLength)
6228 hRes = CONVERT10_E_OLESTREAM_GET;
6231 else
6232 hRes = CONVERT10_E_OLESTREAM_GET;
6235 else
6237 /* Get the Width of the Metafile */
6238 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6239 if(dwSize != sizeof(pData->dwMetaFileWidth))
6241 hRes = CONVERT10_E_OLESTREAM_GET;
6243 if(hRes == S_OK)
6245 /* Get the Height of the Metafile */
6246 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6247 if(dwSize != sizeof(pData->dwMetaFileHeight))
6249 hRes = CONVERT10_E_OLESTREAM_GET;
6253 if(hRes == S_OK)
6255 /* Get the Length of the Data */
6256 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6257 if(dwSize != sizeof(pData->dwDataLength))
6259 hRes = CONVERT10_E_OLESTREAM_GET;
6263 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6265 if(!bStrem1) /* if it is a second OLE stream data */
6267 pData->dwDataLength -= 8;
6268 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6269 if(dwSize != sizeof(pData->strUnknown))
6271 hRes = CONVERT10_E_OLESTREAM_GET;
6275 if(hRes == S_OK)
6277 if(pData->dwDataLength > 0)
6279 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6281 /* Get Data (ex. IStorage, Metafile, or BMP) */
6282 if(pData->pData)
6284 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6285 if(dwSize != pData->dwDataLength)
6287 hRes = CONVERT10_E_OLESTREAM_GET;
6290 else
6292 hRes = CONVERT10_E_OLESTREAM_GET;
6298 return hRes;
6301 /*************************************************************************
6302 * OLECONVERT_SaveOLE10 [Internal]
6304 * Saves the OLE10 STREAM From memory
6306 * PARAMS
6307 * pData [I] Data Structure for the OLESTREAM Data
6308 * pOleStream [I] The OLESTREAM to save
6310 * RETURNS
6311 * Success: S_OK
6312 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6314 * NOTES
6315 * This function is used by OleConvertIStorageToOLESTREAM only.
6318 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6320 DWORD dwSize;
6321 HRESULT hRes = S_OK;
6324 /* Set the OleID */
6325 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6326 if(dwSize != sizeof(pData->dwOleID))
6328 hRes = CONVERT10_E_OLESTREAM_PUT;
6331 if(hRes == S_OK)
6333 /* Set the TypeID */
6334 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6335 if(dwSize != sizeof(pData->dwTypeID))
6337 hRes = CONVERT10_E_OLESTREAM_PUT;
6341 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6343 /* Set the Length of the OleTypeName */
6344 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6345 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6347 hRes = CONVERT10_E_OLESTREAM_PUT;
6350 if(hRes == S_OK)
6352 if(pData->dwOleTypeNameLength > 0)
6354 /* Set the OleTypeName */
6355 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6356 if(dwSize != pData->dwOleTypeNameLength)
6358 hRes = CONVERT10_E_OLESTREAM_PUT;
6363 if(hRes == S_OK)
6365 /* Set the width of the Metafile */
6366 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6367 if(dwSize != sizeof(pData->dwMetaFileWidth))
6369 hRes = CONVERT10_E_OLESTREAM_PUT;
6373 if(hRes == S_OK)
6375 /* Set the height of the Metafile */
6376 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6377 if(dwSize != sizeof(pData->dwMetaFileHeight))
6379 hRes = CONVERT10_E_OLESTREAM_PUT;
6383 if(hRes == S_OK)
6385 /* Set the length of the Data */
6386 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6387 if(dwSize != sizeof(pData->dwDataLength))
6389 hRes = CONVERT10_E_OLESTREAM_PUT;
6393 if(hRes == S_OK)
6395 if(pData->dwDataLength > 0)
6397 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6398 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6399 if(dwSize != pData->dwDataLength)
6401 hRes = CONVERT10_E_OLESTREAM_PUT;
6406 return hRes;
6409 /*************************************************************************
6410 * OLECONVERT_GetOLE20FromOLE10[Internal]
6412 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6413 * opens it, and copies the content to the dest IStorage for
6414 * OleConvertOLESTREAMToIStorage
6417 * PARAMS
6418 * pDestStorage [I] The IStorage to copy the data to
6419 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6420 * nBufferLength [I] The size of the buffer
6422 * RETURNS
6423 * Nothing
6425 * NOTES
6429 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6431 HRESULT hRes;
6432 HANDLE hFile;
6433 IStorage *pTempStorage;
6434 DWORD dwNumOfBytesWritten;
6435 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6436 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6438 /* Create a temp File */
6439 GetTempPathW(MAX_PATH, wstrTempDir);
6440 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6441 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6443 if(hFile != INVALID_HANDLE_VALUE)
6445 /* Write IStorage Data to File */
6446 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6447 CloseHandle(hFile);
6449 /* Open and copy temp storage to the Dest Storage */
6450 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6451 if(hRes == S_OK)
6453 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6454 StorageBaseImpl_Release(pTempStorage);
6456 DeleteFileW(wstrTempFile);
6461 /*************************************************************************
6462 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6464 * Saves the OLE10 STREAM From memory
6466 * PARAMS
6467 * pStorage [I] The Src IStorage to copy
6468 * pData [I] The Dest Memory to write to.
6470 * RETURNS
6471 * The size in bytes allocated for pData
6473 * NOTES
6474 * Memory allocated for pData must be freed by the caller
6476 * Used by OleConvertIStorageToOLESTREAM only.
6479 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6481 HANDLE hFile;
6482 HRESULT hRes;
6483 DWORD nDataLength = 0;
6484 IStorage *pTempStorage;
6485 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6486 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6488 *pData = NULL;
6490 /* Create temp Storage */
6491 GetTempPathW(MAX_PATH, wstrTempDir);
6492 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6493 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6495 if(hRes == S_OK)
6497 /* Copy Src Storage to the Temp Storage */
6498 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6499 StorageBaseImpl_Release(pTempStorage);
6501 /* Open Temp Storage as a file and copy to memory */
6502 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6503 if(hFile != INVALID_HANDLE_VALUE)
6505 nDataLength = GetFileSize(hFile, NULL);
6506 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6507 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6508 CloseHandle(hFile);
6510 DeleteFileW(wstrTempFile);
6512 return nDataLength;
6515 /*************************************************************************
6516 * OLECONVERT_CreateOleStream [Internal]
6518 * Creates the "\001OLE" stream in the IStorage if necessary.
6520 * PARAMS
6521 * pStorage [I] Dest storage to create the stream in
6523 * RETURNS
6524 * Nothing
6526 * NOTES
6527 * This function is used by OleConvertOLESTREAMToIStorage only.
6529 * This stream is still unknown, MS Word seems to have extra data
6530 * but since the data is stored in the OLESTREAM there should be
6531 * no need to recreate the stream. If the stream is manually
6532 * deleted it will create it with this default data.
6535 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6537 HRESULT hRes;
6538 IStream *pStream;
6539 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6540 BYTE pOleStreamHeader [] =
6542 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6543 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6544 0x00, 0x00, 0x00, 0x00
6547 /* Create stream if not present */
6548 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6549 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6551 if(hRes == S_OK)
6553 /* Write default Data */
6554 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6555 IStream_Release(pStream);
6559 /* write a string to a stream, preceded by its length */
6560 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6562 HRESULT r;
6563 LPSTR str;
6564 DWORD len = 0;
6566 if( string )
6567 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6568 r = IStream_Write( stm, &len, sizeof(len), NULL);
6569 if( FAILED( r ) )
6570 return r;
6571 if(len == 0)
6572 return r;
6573 str = CoTaskMemAlloc( len );
6574 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6575 r = IStream_Write( stm, str, len, NULL);
6576 CoTaskMemFree( str );
6577 return r;
6580 /* read a string preceded by its length from a stream */
6581 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6583 HRESULT r;
6584 DWORD len, count = 0;
6585 LPSTR str;
6586 LPWSTR wstr;
6588 r = IStream_Read( stm, &len, sizeof(len), &count );
6589 if( FAILED( r ) )
6590 return r;
6591 if( count != sizeof(len) )
6592 return E_OUTOFMEMORY;
6594 TRACE("%ld bytes\n",len);
6596 str = CoTaskMemAlloc( len );
6597 if( !str )
6598 return E_OUTOFMEMORY;
6599 count = 0;
6600 r = IStream_Read( stm, str, len, &count );
6601 if( FAILED( r ) )
6602 return r;
6603 if( count != len )
6605 CoTaskMemFree( str );
6606 return E_OUTOFMEMORY;
6609 TRACE("Read string %s\n",debugstr_an(str,len));
6611 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6612 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6613 if( wstr )
6614 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6615 CoTaskMemFree( str );
6617 *string = wstr;
6619 return r;
6623 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6624 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6626 IStream *pstm;
6627 HRESULT r = S_OK;
6628 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6630 static const BYTE unknown1[12] =
6631 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6632 0xFF, 0xFF, 0xFF, 0xFF};
6633 static const BYTE unknown2[16] =
6634 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6635 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6637 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6638 debugstr_w(lpszUserType), debugstr_w(szClipName),
6639 debugstr_w(szProgIDName));
6641 /* Create a CompObj stream if it doesn't exist */
6642 r = IStorage_CreateStream(pstg, szwStreamName,
6643 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6644 if( FAILED (r) )
6645 return r;
6647 /* Write CompObj Structure to stream */
6648 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6650 if( SUCCEEDED( r ) )
6651 r = WriteClassStm( pstm, clsid );
6653 if( SUCCEEDED( r ) )
6654 r = STREAM_WriteString( pstm, lpszUserType );
6655 if( SUCCEEDED( r ) )
6656 r = STREAM_WriteString( pstm, szClipName );
6657 if( SUCCEEDED( r ) )
6658 r = STREAM_WriteString( pstm, szProgIDName );
6659 if( SUCCEEDED( r ) )
6660 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6662 IStream_Release( pstm );
6664 return r;
6667 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6668 static HRESULT CLSIDFromUserType(LPCWSTR lpszUserType, CLSID *clsid)
6670 LONG r, count, i, len;
6671 WCHAR szKey[0x40];
6672 HKEY hkey, hkeyclsid;
6673 LPWSTR buffer = NULL;
6674 BOOL found = FALSE;
6675 static const WCHAR szclsid[] = { 'C','L','S','I','D',0 };
6677 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType));
6679 r = RegOpenKeyW( HKEY_CLASSES_ROOT, szclsid, &hkeyclsid );
6680 if( r )
6681 return E_INVALIDARG;
6683 len = lstrlenW( lpszUserType ) + 1;
6684 buffer = CoTaskMemAlloc( len * sizeof (WCHAR) );
6685 if( !buffer )
6686 goto end;
6688 for(i=0; !found; i++ )
6690 r = RegEnumKeyW( hkeyclsid, i, szKey, sizeof(szKey)/sizeof(WCHAR));
6691 if( r != ERROR_SUCCESS )
6692 break;
6693 hkey = 0;
6694 r = RegOpenKeyW( hkeyclsid, szKey, &hkey );
6695 if( r != ERROR_SUCCESS )
6696 break;
6697 count = len * sizeof (WCHAR);
6698 r = RegQueryValueW( hkey, NULL, buffer, &count );
6699 found = ( r == ERROR_SUCCESS ) &&
6700 ( count == len*sizeof(WCHAR) ) &&
6701 !lstrcmpW( buffer, lpszUserType ) ;
6702 RegCloseKey( hkey );
6705 end:
6706 if( buffer )
6707 CoTaskMemFree( buffer );
6708 RegCloseKey( hkeyclsid );
6710 if ( !found )
6711 return E_INVALIDARG;
6713 TRACE("clsid is %s\n", debugstr_w( szKey ) );
6715 r = CLSIDFromString( szKey, clsid );
6717 return r;
6721 /***********************************************************************
6722 * WriteFmtUserTypeStg (OLE32.@)
6724 HRESULT WINAPI WriteFmtUserTypeStg(
6725 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6727 HRESULT r;
6728 WCHAR szwClipName[0x40];
6729 WCHAR szCLSIDName[OLESTREAM_MAX_STR_LEN];
6730 CLSID clsid;
6731 LPWSTR wstrProgID;
6732 DWORD n;
6733 LPMALLOC allocator = NULL;
6735 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6737 r = CoGetMalloc(0, &allocator);
6738 if( FAILED( r) )
6739 return E_OUTOFMEMORY;
6741 /* get the clipboard format name */
6742 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6743 szwClipName[n]=0;
6745 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6747 /* Get the CLSID */
6748 szCLSIDName[0]=0;
6749 r = CLSIDFromUserType(lpszUserType, &clsid);
6750 if( FAILED( r ) )
6751 return r;
6753 TRACE("CLSID is %s\n",debugstr_guid(&clsid));
6755 /* get the real program ID */
6756 r = ProgIDFromCLSID( &clsid, &wstrProgID);
6757 if( FAILED( r ) )
6758 return r;
6760 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6762 /* if we have a good string, write the stream */
6763 if( wstrProgID )
6764 r = STORAGE_WriteCompObj( pstg, &clsid,
6765 lpszUserType, szwClipName, wstrProgID );
6766 else
6767 r = E_OUTOFMEMORY;
6769 IMalloc_Free( allocator, wstrProgID);
6771 return r;
6775 /******************************************************************************
6776 * ReadFmtUserTypeStg [OLE32.@]
6778 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6780 HRESULT r;
6781 IStream *stm = 0;
6782 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6783 unsigned char unknown1[12];
6784 unsigned char unknown2[16];
6785 DWORD count;
6786 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6787 CLSID clsid;
6789 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6791 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6792 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6793 if( FAILED ( r ) )
6795 ERR("Failed to open stream\n");
6796 return r;
6799 /* read the various parts of the structure */
6800 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6801 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6802 goto end;
6803 r = ReadClassStm( stm, &clsid );
6804 if( FAILED( r ) )
6805 goto end;
6807 r = STREAM_ReadString( stm, &szCLSIDName );
6808 if( FAILED( r ) )
6809 goto end;
6811 r = STREAM_ReadString( stm, &szOleTypeName );
6812 if( FAILED( r ) )
6813 goto end;
6815 r = STREAM_ReadString( stm, &szProgIDName );
6816 if( FAILED( r ) )
6817 goto end;
6819 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6820 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6821 goto end;
6823 /* ok, success... now we just need to store what we found */
6824 if( pcf )
6825 *pcf = RegisterClipboardFormatW( szOleTypeName );
6826 CoTaskMemFree( szOleTypeName );
6828 if( lplpszUserType )
6829 *lplpszUserType = szCLSIDName;
6830 CoTaskMemFree( szProgIDName );
6832 end:
6833 IStream_Release( stm );
6835 return r;
6839 /*************************************************************************
6840 * OLECONVERT_CreateCompObjStream [Internal]
6842 * Creates a "\001CompObj" is the destination IStorage if necessary.
6844 * PARAMS
6845 * pStorage [I] The dest IStorage to create the CompObj Stream
6846 * if necessary.
6847 * strOleTypeName [I] The ProgID
6849 * RETURNS
6850 * Success: S_OK
6851 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6853 * NOTES
6854 * This function is used by OleConvertOLESTREAMToIStorage only.
6856 * The stream data is stored in the OLESTREAM and there should be
6857 * no need to recreate the stream. If the stream is manually
6858 * deleted it will attempt to create it by querying the registry.
6862 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6864 IStream *pStream;
6865 HRESULT hStorageRes, hRes = S_OK;
6866 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6867 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6868 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
6870 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6871 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6873 /* Initialize the CompObj structure */
6874 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6875 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6876 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6879 /* Create a CompObj stream if it doesn't exist */
6880 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6881 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6882 if(hStorageRes == S_OK)
6884 /* copy the OleTypeName to the compobj struct */
6885 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6886 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6888 /* copy the OleTypeName to the compobj struct */
6889 /* Note: in the test made, these were Identical */
6890 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6891 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6893 /* Get the CLSID */
6894 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
6895 bufferW, OLESTREAM_MAX_STR_LEN );
6896 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
6898 if(hRes == S_OK)
6900 HKEY hKey;
6901 LONG hErr;
6902 /* Get the CLSID Default Name from the Registry */
6903 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6904 if(hErr == ERROR_SUCCESS)
6906 char strTemp[OLESTREAM_MAX_STR_LEN];
6907 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6908 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6909 if(hErr == ERROR_SUCCESS)
6911 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6913 RegCloseKey(hKey);
6917 /* Write CompObj Structure to stream */
6918 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6920 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6922 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6923 if(IStorageCompObj.dwCLSIDNameLength > 0)
6925 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6927 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6928 if(IStorageCompObj.dwOleTypeNameLength > 0)
6930 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6932 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6933 if(IStorageCompObj.dwProgIDNameLength > 0)
6935 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6937 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6938 IStream_Release(pStream);
6940 return hRes;
6944 /*************************************************************************
6945 * OLECONVERT_CreateOlePresStream[Internal]
6947 * Creates the "\002OlePres000" Stream with the Metafile data
6949 * PARAMS
6950 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6951 * dwExtentX [I] Width of the Metafile
6952 * dwExtentY [I] Height of the Metafile
6953 * pData [I] Metafile data
6954 * dwDataLength [I] Size of the Metafile data
6956 * RETURNS
6957 * Success: S_OK
6958 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6960 * NOTES
6961 * This function is used by OleConvertOLESTREAMToIStorage only.
6964 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6966 HRESULT hRes;
6967 IStream *pStream;
6968 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6969 BYTE pOlePresStreamHeader [] =
6971 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6972 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6973 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6974 0x00, 0x00, 0x00, 0x00
6977 BYTE pOlePresStreamHeaderEmpty [] =
6979 0x00, 0x00, 0x00, 0x00,
6980 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6981 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6982 0x00, 0x00, 0x00, 0x00
6985 /* Create the OlePres000 Stream */
6986 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6987 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6989 if(hRes == S_OK)
6991 DWORD nHeaderSize;
6992 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6994 memset(&OlePres, 0, sizeof(OlePres));
6995 /* Do we have any metafile data to save */
6996 if(dwDataLength > 0)
6998 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6999 nHeaderSize = sizeof(pOlePresStreamHeader);
7001 else
7003 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7004 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7006 /* Set width and height of the metafile */
7007 OlePres.dwExtentX = dwExtentX;
7008 OlePres.dwExtentY = -dwExtentY;
7010 /* Set Data and Length */
7011 if(dwDataLength > sizeof(METAFILEPICT16))
7013 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7014 OlePres.pData = &(pData[8]);
7016 /* Save OlePres000 Data to Stream */
7017 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7018 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7019 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7020 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7021 if(OlePres.dwSize > 0)
7023 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7025 IStream_Release(pStream);
7029 /*************************************************************************
7030 * OLECONVERT_CreateOle10NativeStream [Internal]
7032 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7034 * PARAMS
7035 * pStorage [I] Dest storage to create the stream in
7036 * pData [I] Ole10 Native Data (ex. bmp)
7037 * dwDataLength [I] Size of the Ole10 Native Data
7039 * RETURNS
7040 * Nothing
7042 * NOTES
7043 * This function is used by OleConvertOLESTREAMToIStorage only.
7045 * Might need to verify the data and return appropriate error message
7048 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7050 HRESULT hRes;
7051 IStream *pStream;
7052 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7054 /* Create the Ole10Native Stream */
7055 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7056 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7058 if(hRes == S_OK)
7060 /* Write info to stream */
7061 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7062 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7063 IStream_Release(pStream);
7068 /*************************************************************************
7069 * OLECONVERT_GetOLE10ProgID [Internal]
7071 * Finds the ProgID (or OleTypeID) from the IStorage
7073 * PARAMS
7074 * pStorage [I] The Src IStorage to get the ProgID
7075 * strProgID [I] the ProgID string to get
7076 * dwSize [I] the size of the string
7078 * RETURNS
7079 * Success: S_OK
7080 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7082 * NOTES
7083 * This function is used by OleConvertIStorageToOLESTREAM only.
7087 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7089 HRESULT hRes;
7090 IStream *pStream;
7091 LARGE_INTEGER iSeekPos;
7092 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7093 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7095 /* Open the CompObj Stream */
7096 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7097 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7098 if(hRes == S_OK)
7101 /*Get the OleType from the CompObj Stream */
7102 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7103 iSeekPos.u.HighPart = 0;
7105 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7106 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7107 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7108 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7109 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7110 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7111 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7113 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7114 if(*dwSize > 0)
7116 IStream_Read(pStream, strProgID, *dwSize, NULL);
7118 IStream_Release(pStream);
7120 else
7122 STATSTG stat;
7123 LPOLESTR wstrProgID;
7125 /* Get the OleType from the registry */
7126 REFCLSID clsid = &(stat.clsid);
7127 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7128 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7129 if(hRes == S_OK)
7131 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7135 return hRes;
7138 /*************************************************************************
7139 * OLECONVERT_GetOle10PresData [Internal]
7141 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7143 * PARAMS
7144 * pStorage [I] Src IStroage
7145 * pOleStream [I] Dest OleStream Mem Struct
7147 * RETURNS
7148 * Nothing
7150 * NOTES
7151 * This function is used by OleConvertIStorageToOLESTREAM only.
7153 * Memory allocated for pData must be freed by the caller
7157 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7160 HRESULT hRes;
7161 IStream *pStream;
7162 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7164 /* Initialize Default data for OLESTREAM */
7165 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7166 pOleStreamData[0].dwTypeID = 2;
7167 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7168 pOleStreamData[1].dwTypeID = 0;
7169 pOleStreamData[0].dwMetaFileWidth = 0;
7170 pOleStreamData[0].dwMetaFileHeight = 0;
7171 pOleStreamData[0].pData = NULL;
7172 pOleStreamData[1].pData = NULL;
7174 /* Open Ole10Native Stream */
7175 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7176 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7177 if(hRes == S_OK)
7180 /* Read Size and Data */
7181 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7182 if(pOleStreamData->dwDataLength > 0)
7184 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7185 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7187 IStream_Release(pStream);
7193 /*************************************************************************
7194 * OLECONVERT_GetOle20PresData[Internal]
7196 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7198 * PARAMS
7199 * pStorage [I] Src IStroage
7200 * pOleStreamData [I] Dest OleStream Mem Struct
7202 * RETURNS
7203 * Nothing
7205 * NOTES
7206 * This function is used by OleConvertIStorageToOLESTREAM only.
7208 * Memory allocated for pData must be freed by the caller
7210 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7212 HRESULT hRes;
7213 IStream *pStream;
7214 OLECONVERT_ISTORAGE_OLEPRES olePress;
7215 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7217 /* Initialize Default data for OLESTREAM */
7218 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7219 pOleStreamData[0].dwTypeID = 2;
7220 pOleStreamData[0].dwMetaFileWidth = 0;
7221 pOleStreamData[0].dwMetaFileHeight = 0;
7222 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7223 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7224 pOleStreamData[1].dwTypeID = 0;
7225 pOleStreamData[1].dwOleTypeNameLength = 0;
7226 pOleStreamData[1].strOleTypeName[0] = 0;
7227 pOleStreamData[1].dwMetaFileWidth = 0;
7228 pOleStreamData[1].dwMetaFileHeight = 0;
7229 pOleStreamData[1].pData = NULL;
7230 pOleStreamData[1].dwDataLength = 0;
7233 /* Open OlePress000 stream */
7234 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7235 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7236 if(hRes == S_OK)
7238 LARGE_INTEGER iSeekPos;
7239 METAFILEPICT16 MetaFilePict;
7240 static const char strMetafilePictName[] = "METAFILEPICT";
7242 /* Set the TypeID for a Metafile */
7243 pOleStreamData[1].dwTypeID = 5;
7245 /* Set the OleTypeName to Metafile */
7246 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7247 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7249 iSeekPos.u.HighPart = 0;
7250 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7252 /* Get Presentation Data */
7253 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7254 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7255 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7256 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7258 /*Set width and Height */
7259 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7260 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7261 if(olePress.dwSize > 0)
7263 /* Set Length */
7264 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7266 /* Set MetaFilePict struct */
7267 MetaFilePict.mm = 8;
7268 MetaFilePict.xExt = olePress.dwExtentX;
7269 MetaFilePict.yExt = olePress.dwExtentY;
7270 MetaFilePict.hMF = 0;
7272 /* Get Metafile Data */
7273 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7274 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7275 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7277 IStream_Release(pStream);
7281 /*************************************************************************
7282 * OleConvertOLESTREAMToIStorage [OLE32.@]
7284 * Read info on MSDN
7286 * TODO
7287 * DVTARGETDEVICE paramenter is not handled
7288 * Still unsure of some mem fields for OLE 10 Stream
7289 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7290 * and "\001OLE" streams
7293 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7294 LPOLESTREAM pOleStream,
7295 LPSTORAGE pstg,
7296 const DVTARGETDEVICE* ptd)
7298 int i;
7299 HRESULT hRes=S_OK;
7300 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7302 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7304 if(ptd != NULL)
7306 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7309 if(pstg == NULL || pOleStream == NULL)
7311 hRes = E_INVALIDARG;
7314 if(hRes == S_OK)
7316 /* Load the OLESTREAM to Memory */
7317 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7320 if(hRes == S_OK)
7322 /* Load the OLESTREAM to Memory (part 2)*/
7323 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7326 if(hRes == S_OK)
7329 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7331 /* Do we have the IStorage Data in the OLESTREAM */
7332 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7334 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7335 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7337 else
7339 /* It must be an original OLE 1.0 source */
7340 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7343 else
7345 /* It must be an original OLE 1.0 source */
7346 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7349 /* Create CompObj Stream if necessary */
7350 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7351 if(hRes == S_OK)
7353 /*Create the Ole Stream if necessary */
7354 OLECONVERT_CreateOleStream(pstg);
7359 /* Free allocated memory */
7360 for(i=0; i < 2; i++)
7362 if(pOleStreamData[i].pData != NULL)
7364 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7366 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7368 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7369 pOleStreamData[i].pstrOleObjFileName = NULL;
7372 return hRes;
7375 /*************************************************************************
7376 * OleConvertIStorageToOLESTREAM [OLE32.@]
7378 * Read info on MSDN
7380 * Read info on MSDN
7382 * TODO
7383 * Still unsure of some mem fields for OLE 10 Stream
7384 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7385 * and "\001OLE" streams.
7388 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7389 LPSTORAGE pstg,
7390 LPOLESTREAM pOleStream)
7392 int i;
7393 HRESULT hRes = S_OK;
7394 IStream *pStream;
7395 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7396 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7399 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7401 if(pstg == NULL || pOleStream == NULL)
7403 hRes = E_INVALIDARG;
7405 if(hRes == S_OK)
7407 /* Get the ProgID */
7408 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7409 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7411 if(hRes == S_OK)
7413 /* Was it originally Ole10 */
7414 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7415 if(hRes == S_OK)
7417 IStream_Release(pStream);
7418 /* Get Presentation Data for Ole10Native */
7419 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7421 else
7423 /* Get Presentation Data (OLE20) */
7424 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7427 /* Save OLESTREAM */
7428 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7429 if(hRes == S_OK)
7431 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7436 /* Free allocated memory */
7437 for(i=0; i < 2; i++)
7439 if(pOleStreamData[i].pData != NULL)
7441 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7445 return hRes;
7448 /***********************************************************************
7449 * GetConvertStg (OLE32.@)
7451 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7452 FIXME("unimplemented stub!\n");
7453 return E_FAIL;
7456 /******************************************************************************
7457 * StgIsStorageFile [OLE32.@]
7459 HRESULT WINAPI
7460 StgIsStorageFile(LPCOLESTR fn)
7462 HANDLE hf;
7463 BYTE magic[8];
7464 DWORD bytes_read;
7466 TRACE("(\'%s\')\n", debugstr_w(fn));
7467 hf = CreateFileW(fn, GENERIC_READ,
7468 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7469 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7471 if (hf == INVALID_HANDLE_VALUE)
7472 return STG_E_FILENOTFOUND;
7474 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7476 WARN(" unable to read file\n");
7477 CloseHandle(hf);
7478 return S_FALSE;
7481 CloseHandle(hf);
7483 if (bytes_read != 8) {
7484 WARN(" too short\n");
7485 return S_FALSE;
7488 if (!memcmp(magic,STORAGE_magic,8)) {
7489 WARN(" -> YES\n");
7490 return S_OK;
7493 WARN(" -> Invalid header.\n");
7494 return S_FALSE;