Added some TRACE statements.
[wine/dcerpc.git] / dlls / ole32 / storage32.c
blobaf46cce1d7ab78adc327f12ac81f3226b13acb96
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 COBJMACROS
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winuser.h"
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
44 #include "storage32.h"
45 #include "ole2.h" /* For Write/ReadClassStm */
47 #include "winreg.h"
48 #include "wine/wingdi16.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(storage);
52 #define FILE_BEGIN 0
54 #define STGM_SHARE_MODE(stgm) ((stgm)&0xf0)
56 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
57 #define OLESTREAM_ID 0x501
58 #define OLESTREAM_MAX_STR_LEN 255
60 static const char rootPropertyName[] = "Root Entry";
63 /* OLESTREAM memory structure to use for Get and Put Routines */
64 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
65 typedef struct
67 DWORD dwOleID;
68 DWORD dwTypeID;
69 DWORD dwOleTypeNameLength;
70 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
71 CHAR *pstrOleObjFileName;
72 DWORD dwOleObjFileNameLength;
73 DWORD dwMetaFileWidth;
74 DWORD dwMetaFileHeight;
75 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
76 DWORD dwDataLength;
77 BYTE *pData;
78 }OLECONVERT_OLESTREAM_DATA;
80 /* CompObj Stream structure */
81 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
82 typedef struct
84 BYTE byUnknown1[12];
85 CLSID clsid;
86 DWORD dwCLSIDNameLength;
87 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
88 DWORD dwOleTypeNameLength;
89 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
90 DWORD dwProgIDNameLength;
91 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
92 BYTE byUnknown2[16];
93 }OLECONVERT_ISTORAGE_COMPOBJ;
96 /* Ole Presention Stream structure */
97 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
98 typedef struct
100 BYTE byUnknown1[28];
101 DWORD dwExtentX;
102 DWORD dwExtentY;
103 DWORD dwSize;
104 BYTE *pData;
105 }OLECONVERT_ISTORAGE_OLEPRES;
109 /***********************************************************************
110 * Forward declaration of internal functions used by the method DestroyElement
112 static HRESULT deleteStorageProperty(
113 StorageImpl *parentStorage,
114 ULONG foundPropertyIndexToDelete,
115 StgProperty propertyToDelete);
117 static HRESULT deleteStreamProperty(
118 StorageImpl *parentStorage,
119 ULONG foundPropertyIndexToDelete,
120 StgProperty propertyToDelete);
122 static HRESULT findPlaceholder(
123 StorageImpl *storage,
124 ULONG propertyIndexToStore,
125 ULONG storagePropertyIndex,
126 INT typeOfRelation);
128 static HRESULT adjustPropertyChain(
129 StorageImpl *This,
130 StgProperty propertyToDelete,
131 StgProperty parentProperty,
132 ULONG parentPropertyId,
133 INT typeOfRelation);
135 /***********************************************************************
136 * Declaration of the functions used to manipulate StgProperty
139 static ULONG getFreeProperty(
140 StorageImpl *storage);
142 static void updatePropertyChain(
143 StorageImpl *storage,
144 ULONG newPropertyIndex,
145 StgProperty newProperty);
147 static LONG propertyNameCmp(
148 const OLECHAR *newProperty,
149 const OLECHAR *currentProperty);
152 /***********************************************************************
153 * Declaration of miscellaneous functions...
155 static HRESULT validateSTGM(DWORD stgmValue);
157 static DWORD GetShareModeFromSTGM(DWORD stgm);
158 static DWORD GetAccessModeFromSTGM(DWORD stgm);
159 static DWORD GetCreationModeFromSTGM(DWORD stgm);
162 * Virtual function table for the IStorage32Impl class.
164 static IStorageVtbl Storage32Impl_Vtbl =
166 StorageBaseImpl_QueryInterface,
167 StorageBaseImpl_AddRef,
168 StorageBaseImpl_Release,
169 StorageBaseImpl_CreateStream,
170 StorageBaseImpl_OpenStream,
171 StorageImpl_CreateStorage,
172 StorageBaseImpl_OpenStorage,
173 StorageImpl_CopyTo,
174 StorageImpl_MoveElementTo,
175 StorageImpl_Commit,
176 StorageImpl_Revert,
177 StorageBaseImpl_EnumElements,
178 StorageImpl_DestroyElement,
179 StorageBaseImpl_RenameElement,
180 StorageImpl_SetElementTimes,
181 StorageBaseImpl_SetClass,
182 StorageImpl_SetStateBits,
183 StorageImpl_Stat
187 * Virtual function table for the Storage32InternalImpl class.
189 static IStorageVtbl Storage32InternalImpl_Vtbl =
191 StorageBaseImpl_QueryInterface,
192 StorageBaseImpl_AddRef,
193 StorageBaseImpl_Release,
194 StorageBaseImpl_CreateStream,
195 StorageBaseImpl_OpenStream,
196 StorageImpl_CreateStorage,
197 StorageBaseImpl_OpenStorage,
198 StorageImpl_CopyTo,
199 StorageImpl_MoveElementTo,
200 StorageInternalImpl_Commit,
201 StorageInternalImpl_Revert,
202 StorageBaseImpl_EnumElements,
203 StorageImpl_DestroyElement,
204 StorageBaseImpl_RenameElement,
205 StorageImpl_SetElementTimes,
206 StorageBaseImpl_SetClass,
207 StorageImpl_SetStateBits,
208 StorageBaseImpl_Stat
212 * Virtual function table for the IEnumSTATSTGImpl class.
214 static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
216 IEnumSTATSTGImpl_QueryInterface,
217 IEnumSTATSTGImpl_AddRef,
218 IEnumSTATSTGImpl_Release,
219 IEnumSTATSTGImpl_Next,
220 IEnumSTATSTGImpl_Skip,
221 IEnumSTATSTGImpl_Reset,
222 IEnumSTATSTGImpl_Clone
225 extern IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
229 /************************************************************************
230 ** Storage32BaseImpl implementatiion
233 /************************************************************************
234 * Storage32BaseImpl_QueryInterface (IUnknown)
236 * This method implements the common QueryInterface for all IStorage32
237 * implementations contained in this file.
239 * See Windows documentation for more details on IUnknown methods.
241 HRESULT WINAPI StorageBaseImpl_QueryInterface(
242 IStorage* iface,
243 REFIID riid,
244 void** ppvObject)
246 StorageBaseImpl *This = (StorageBaseImpl *)iface;
248 * Perform a sanity check on the parameters.
250 if ( (This==0) || (ppvObject==0) )
251 return E_INVALIDARG;
254 * Initialize the return parameter.
256 *ppvObject = 0;
259 * Compare the riid with the interface IDs implemented by this object.
261 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
263 *ppvObject = (IStorage*)This;
265 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
267 *ppvObject = (IStorage*)This;
269 else if (memcmp(&IID_IPropertySetStorage, riid, sizeof(IID_IPropertySetStorage)) == 0)
271 *ppvObject = (IStorage*)&This->pssVtbl;
275 * Check that we obtained an interface.
277 if ((*ppvObject)==0)
278 return E_NOINTERFACE;
281 * Query Interface always increases the reference count by one when it is
282 * successful
284 StorageBaseImpl_AddRef(iface);
286 return S_OK;
289 /************************************************************************
290 * Storage32BaseImpl_AddRef (IUnknown)
292 * This method implements the common AddRef for all IStorage32
293 * implementations contained in this file.
295 * See Windows documentation for more details on IUnknown methods.
297 ULONG WINAPI StorageBaseImpl_AddRef(
298 IStorage* iface)
300 StorageBaseImpl *This = (StorageBaseImpl *)iface;
301 ULONG ref = InterlockedIncrement(&This->ref);
303 TRACE("(%p) AddRef to %ld\n", This, ref);
305 return ref;
308 /************************************************************************
309 * Storage32BaseImpl_Release (IUnknown)
311 * This method implements the common Release for all IStorage32
312 * implementations contained in this file.
314 * See Windows documentation for more details on IUnknown methods.
316 ULONG WINAPI StorageBaseImpl_Release(
317 IStorage* iface)
319 StorageBaseImpl *This = (StorageBaseImpl *)iface;
321 * Decrease the reference count on this object.
323 ULONG ref = InterlockedDecrement(&This->ref);
325 TRACE("(%p) ReleaseRef to %ld\n", This, ref);
328 * If the reference count goes down to 0, perform suicide.
330 if (ref == 0)
333 * Since we are using a system of base-classes, we want to call the
334 * destructor of the appropriate derived class. To do this, we are
335 * using virtual functions to implement the destructor.
337 This->v_destructor(This);
340 return ref;
343 /************************************************************************
344 * Storage32BaseImpl_OpenStream (IStorage)
346 * This method will open the specified stream object from the current storage.
348 * See Windows documentation for more details on IStorage methods.
350 HRESULT WINAPI StorageBaseImpl_OpenStream(
351 IStorage* iface,
352 const OLECHAR* pwcsName, /* [string][in] */
353 void* reserved1, /* [unique][in] */
354 DWORD grfMode, /* [in] */
355 DWORD reserved2, /* [in] */
356 IStream** ppstm) /* [out] */
358 StorageBaseImpl *This = (StorageBaseImpl *)iface;
359 IEnumSTATSTGImpl* propertyEnumeration;
360 StgStreamImpl* newStream;
361 StgProperty currentProperty;
362 ULONG foundPropertyIndex;
363 HRESULT res = STG_E_UNKNOWN;
365 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
366 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
369 * Perform a sanity check on the parameters.
371 if ( (pwcsName==NULL) || (ppstm==0) )
373 res = E_INVALIDARG;
374 goto end;
378 * Initialize the out parameter
380 *ppstm = NULL;
383 * Validate the STGM flags
385 if ( FAILED( validateSTGM(grfMode) ))
387 res = STG_E_INVALIDFLAG;
388 goto end;
392 * As documented.
394 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
395 (grfMode & STGM_DELETEONRELEASE) ||
396 (grfMode & STGM_TRANSACTED) )
398 res = STG_E_INVALIDFUNCTION;
399 goto end;
403 * Create a property enumeration to search the properties
405 propertyEnumeration = IEnumSTATSTGImpl_Construct(
406 This->ancestorStorage,
407 This->rootPropertySetIndex);
410 * Search the enumeration for the property with the given name
412 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
413 propertyEnumeration,
414 pwcsName,
415 &currentProperty);
418 * Delete the property enumeration since we don't need it anymore
420 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
423 * If it was found, construct the stream object and return a pointer to it.
425 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
426 (currentProperty.propertyType==PROPTYPE_STREAM) )
428 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
430 if (newStream!=0)
432 newStream->grfMode = grfMode;
433 *ppstm = (IStream*)newStream;
436 * Since we are returning a pointer to the interface, we have to
437 * nail down the reference.
439 StgStreamImpl_AddRef(*ppstm);
441 res = S_OK;
442 goto end;
445 res = E_OUTOFMEMORY;
446 goto end;
449 res = STG_E_FILENOTFOUND;
451 end:
452 if (res == S_OK)
453 TRACE("<-- IStream %p\n", *ppstm);
454 TRACE("<-- %08lx\n", res);
455 return res;
458 /************************************************************************
459 * Storage32BaseImpl_OpenStorage (IStorage)
461 * This method will open a new storage object from the current storage.
463 * See Windows documentation for more details on IStorage methods.
465 HRESULT WINAPI StorageBaseImpl_OpenStorage(
466 IStorage* iface,
467 const OLECHAR* pwcsName, /* [string][unique][in] */
468 IStorage* pstgPriority, /* [unique][in] */
469 DWORD grfMode, /* [in] */
470 SNB snbExclude, /* [unique][in] */
471 DWORD reserved, /* [in] */
472 IStorage** ppstg) /* [out] */
474 StorageBaseImpl *This = (StorageBaseImpl *)iface;
475 StorageInternalImpl* newStorage;
476 IEnumSTATSTGImpl* propertyEnumeration;
477 StgProperty currentProperty;
478 ULONG foundPropertyIndex;
479 HRESULT res = STG_E_UNKNOWN;
481 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
482 iface, debugstr_w(pwcsName), pstgPriority,
483 grfMode, snbExclude, reserved, ppstg);
486 * Perform a sanity check on the parameters.
488 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
490 res = E_INVALIDARG;
491 goto end;
494 /* as documented */
495 if (snbExclude != NULL)
497 res = STG_E_INVALIDPARAMETER;
498 goto end;
502 * Validate the STGM flags
504 if ( FAILED( validateSTGM(grfMode) ))
506 res = STG_E_INVALIDFLAG;
507 goto end;
511 * As documented.
513 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
514 (grfMode & STGM_DELETEONRELEASE) ||
515 (grfMode & STGM_PRIORITY) )
517 res = STG_E_INVALIDFUNCTION;
518 goto end;
522 * Initialize the out parameter
524 *ppstg = NULL;
527 * Create a property enumeration to search the properties
529 propertyEnumeration = IEnumSTATSTGImpl_Construct(
530 This->ancestorStorage,
531 This->rootPropertySetIndex);
534 * Search the enumeration for the property with the given name
536 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
537 propertyEnumeration,
538 pwcsName,
539 &currentProperty);
542 * Delete the property enumeration since we don't need it anymore
544 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
547 * If it was found, construct the stream object and return a pointer to it.
549 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
550 (currentProperty.propertyType==PROPTYPE_STORAGE) )
553 * Construct a new Storage object
555 newStorage = StorageInternalImpl_Construct(
556 This->ancestorStorage,
557 foundPropertyIndex);
559 if (newStorage != 0)
561 *ppstg = (IStorage*)newStorage;
564 * Since we are returning a pointer to the interface,
565 * we have to nail down the reference.
567 StorageBaseImpl_AddRef(*ppstg);
569 res = S_OK;
570 goto end;
573 res = STG_E_INSUFFICIENTMEMORY;
574 goto end;
577 res = STG_E_FILENOTFOUND;
579 end:
580 TRACE("<-- %08lx\n", res);
581 return res;
584 /************************************************************************
585 * Storage32BaseImpl_EnumElements (IStorage)
587 * This method will create an enumerator object that can be used to
588 * retrieve informatino about all the properties in the storage object.
590 * See Windows documentation for more details on IStorage methods.
592 HRESULT WINAPI StorageBaseImpl_EnumElements(
593 IStorage* iface,
594 DWORD reserved1, /* [in] */
595 void* reserved2, /* [size_is][unique][in] */
596 DWORD reserved3, /* [in] */
597 IEnumSTATSTG** ppenum) /* [out] */
599 StorageBaseImpl *This = (StorageBaseImpl *)iface;
600 IEnumSTATSTGImpl* newEnum;
602 TRACE("(%p, %ld, %p, %ld, %p)\n",
603 iface, reserved1, reserved2, reserved3, ppenum);
606 * Perform a sanity check on the parameters.
608 if ( (This==0) || (ppenum==0))
609 return E_INVALIDARG;
612 * Construct the enumerator.
614 newEnum = IEnumSTATSTGImpl_Construct(
615 This->ancestorStorage,
616 This->rootPropertySetIndex);
618 if (newEnum!=0)
620 *ppenum = (IEnumSTATSTG*)newEnum;
623 * Don't forget to nail down a reference to the new object before
624 * returning it.
626 IEnumSTATSTGImpl_AddRef(*ppenum);
628 return S_OK;
631 return E_OUTOFMEMORY;
634 /************************************************************************
635 * Storage32BaseImpl_Stat (IStorage)
637 * This method will retrieve information about this storage object.
639 * See Windows documentation for more details on IStorage methods.
641 HRESULT WINAPI StorageBaseImpl_Stat(
642 IStorage* iface,
643 STATSTG* pstatstg, /* [out] */
644 DWORD grfStatFlag) /* [in] */
646 StorageBaseImpl *This = (StorageBaseImpl *)iface;
647 StgProperty curProperty;
648 BOOL readSuccessful;
649 HRESULT res = STG_E_UNKNOWN;
651 TRACE("(%p, %p, %lx)\n",
652 iface, pstatstg, grfStatFlag);
655 * Perform a sanity check on the parameters.
657 if ( (This==0) || (pstatstg==0))
659 res = E_INVALIDARG;
660 goto end;
664 * Read the information from the property.
666 readSuccessful = StorageImpl_ReadProperty(
667 This->ancestorStorage,
668 This->rootPropertySetIndex,
669 &curProperty);
671 if (readSuccessful)
673 StorageUtl_CopyPropertyToSTATSTG(
674 pstatstg,
675 &curProperty,
676 grfStatFlag);
678 res = S_OK;
679 goto end;
682 res = E_FAIL;
684 end:
685 if (res == S_OK)
687 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);
689 TRACE("<-- %08lx\n", res);
690 return res;
693 /************************************************************************
694 * Storage32BaseImpl_RenameElement (IStorage)
696 * This method will rename the specified element.
698 * See Windows documentation for more details on IStorage methods.
700 * Implementation notes: The method used to rename consists of creating a clone
701 * of the deleted StgProperty object setting it with the new name and to
702 * perform a DestroyElement of the old StgProperty.
704 HRESULT WINAPI StorageBaseImpl_RenameElement(
705 IStorage* iface,
706 const OLECHAR* pwcsOldName, /* [in] */
707 const OLECHAR* pwcsNewName) /* [in] */
709 StorageBaseImpl *This = (StorageBaseImpl *)iface;
710 IEnumSTATSTGImpl* propertyEnumeration;
711 StgProperty currentProperty;
712 ULONG foundPropertyIndex;
714 TRACE("(%p, %s, %s)\n",
715 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
718 * Create a property enumeration to search the properties
720 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
721 This->rootPropertySetIndex);
724 * Search the enumeration for the new property name
726 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
727 pwcsNewName,
728 &currentProperty);
730 if (foundPropertyIndex != PROPERTY_NULL)
733 * There is already a property with the new name
735 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
736 return STG_E_FILEALREADYEXISTS;
739 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
742 * Search the enumeration for the old property name
744 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
745 pwcsOldName,
746 &currentProperty);
749 * Delete the property enumeration since we don't need it anymore
751 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
753 if (foundPropertyIndex != PROPERTY_NULL)
755 StgProperty renamedProperty;
756 ULONG renamedPropertyIndex;
759 * Setup a new property for the renamed property
761 renamedProperty.sizeOfNameString =
762 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
764 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
765 return STG_E_INVALIDNAME;
767 strcpyW(renamedProperty.name, pwcsNewName);
769 renamedProperty.propertyType = currentProperty.propertyType;
770 renamedProperty.startingBlock = currentProperty.startingBlock;
771 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
772 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
774 renamedProperty.previousProperty = PROPERTY_NULL;
775 renamedProperty.nextProperty = PROPERTY_NULL;
778 * Bring the dirProperty link in case it is a storage and in which
779 * case the renamed storage elements don't require to be reorganized.
781 renamedProperty.dirProperty = currentProperty.dirProperty;
783 /* call CoFileTime to get the current time
784 renamedProperty.timeStampS1
785 renamedProperty.timeStampD1
786 renamedProperty.timeStampS2
787 renamedProperty.timeStampD2
788 renamedProperty.propertyUniqueID
792 * Obtain a free property in the property chain
794 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
797 * Save the new property into the new property spot
799 StorageImpl_WriteProperty(
800 This->ancestorStorage,
801 renamedPropertyIndex,
802 &renamedProperty);
805 * Find a spot in the property chain for our newly created property.
807 updatePropertyChain(
808 (StorageImpl*)This,
809 renamedPropertyIndex,
810 renamedProperty);
813 * At this point the renamed property has been inserted in the tree,
814 * now, before to Destroy the old property we must zeroed it's dirProperty
815 * otherwise the DestroyProperty below will zap it all and we do not want
816 * this to happen.
817 * Also, we fake that the old property is a storage so the DestroyProperty
818 * will not do a SetSize(0) on the stream data.
820 * This means that we need to tweek the StgProperty if it is a stream or a
821 * non empty storage.
823 StorageImpl_ReadProperty(This->ancestorStorage,
824 foundPropertyIndex,
825 &currentProperty);
827 currentProperty.dirProperty = PROPERTY_NULL;
828 currentProperty.propertyType = PROPTYPE_STORAGE;
829 StorageImpl_WriteProperty(
830 This->ancestorStorage,
831 foundPropertyIndex,
832 &currentProperty);
835 * Invoke Destroy to get rid of the ole property and automatically redo
836 * the linking of it's previous and next members...
838 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
841 else
844 * There is no property with the old name
846 return STG_E_FILENOTFOUND;
849 return S_OK;
852 /************************************************************************
853 * Storage32BaseImpl_CreateStream (IStorage)
855 * This method will create a stream object within this storage
857 * See Windows documentation for more details on IStorage methods.
859 HRESULT WINAPI StorageBaseImpl_CreateStream(
860 IStorage* iface,
861 const OLECHAR* pwcsName, /* [string][in] */
862 DWORD grfMode, /* [in] */
863 DWORD reserved1, /* [in] */
864 DWORD reserved2, /* [in] */
865 IStream** ppstm) /* [out] */
867 StorageBaseImpl *This = (StorageBaseImpl *)iface;
868 IEnumSTATSTGImpl* propertyEnumeration;
869 StgStreamImpl* newStream;
870 StgProperty currentProperty, newStreamProperty;
871 ULONG foundPropertyIndex, newPropertyIndex;
873 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
874 iface, debugstr_w(pwcsName), grfMode,
875 reserved1, reserved2, ppstm);
878 * Validate parameters
880 if (ppstm == 0)
881 return STG_E_INVALIDPOINTER;
883 if (pwcsName == 0)
884 return STG_E_INVALIDNAME;
886 if (reserved1 || reserved2)
887 return STG_E_INVALIDPARAMETER;
890 * Validate the STGM flags
892 if ( FAILED( validateSTGM(grfMode) ))
893 return STG_E_INVALIDFLAG;
895 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) )
896 return STG_E_INVALIDFLAG;
899 * As documented.
901 if ((grfMode & STGM_DELETEONRELEASE) ||
902 (grfMode & STGM_TRANSACTED))
903 return STG_E_INVALIDFUNCTION;
906 * Initialize the out parameter
908 *ppstm = 0;
911 * Create a property enumeration to search the properties
913 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
914 This->rootPropertySetIndex);
916 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
917 pwcsName,
918 &currentProperty);
920 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
922 if (foundPropertyIndex != PROPERTY_NULL)
925 * An element with this name already exists
927 if (grfMode & STGM_CREATE)
929 IStorage_DestroyElement(iface, pwcsName);
931 else
932 return STG_E_FILEALREADYEXISTS;
936 * memset the empty property
938 memset(&newStreamProperty, 0, sizeof(StgProperty));
940 newStreamProperty.sizeOfNameString =
941 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
943 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
944 return STG_E_INVALIDNAME;
946 strcpyW(newStreamProperty.name, pwcsName);
948 newStreamProperty.propertyType = PROPTYPE_STREAM;
949 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
950 newStreamProperty.size.u.LowPart = 0;
951 newStreamProperty.size.u.HighPart = 0;
953 newStreamProperty.previousProperty = PROPERTY_NULL;
954 newStreamProperty.nextProperty = PROPERTY_NULL;
955 newStreamProperty.dirProperty = PROPERTY_NULL;
957 /* call CoFileTime to get the current time
958 newStreamProperty.timeStampS1
959 newStreamProperty.timeStampD1
960 newStreamProperty.timeStampS2
961 newStreamProperty.timeStampD2
964 /* newStreamProperty.propertyUniqueID */
967 * Get a free property or create a new one
969 newPropertyIndex = getFreeProperty(This->ancestorStorage);
972 * Save the new property into the new property spot
974 StorageImpl_WriteProperty(
975 This->ancestorStorage,
976 newPropertyIndex,
977 &newStreamProperty);
980 * Find a spot in the property chain for our newly created property.
982 updatePropertyChain(
983 (StorageImpl*)This,
984 newPropertyIndex,
985 newStreamProperty);
988 * Open the stream to return it.
990 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
992 if (newStream != 0)
994 *ppstm = (IStream*)newStream;
997 * Since we are returning a pointer to the interface, we have to nail down
998 * the reference.
1000 StgStreamImpl_AddRef(*ppstm);
1002 else
1004 return STG_E_INSUFFICIENTMEMORY;
1007 return S_OK;
1010 /************************************************************************
1011 * Storage32BaseImpl_SetClass (IStorage)
1013 * This method will write the specified CLSID in the property of this
1014 * storage.
1016 * See Windows documentation for more details on IStorage methods.
1018 HRESULT WINAPI StorageBaseImpl_SetClass(
1019 IStorage* iface,
1020 REFCLSID clsid) /* [in] */
1022 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1023 HRESULT hRes = E_FAIL;
1024 StgProperty curProperty;
1025 BOOL success;
1027 TRACE("(%p, %p)\n", iface, clsid);
1029 success = StorageImpl_ReadProperty(This->ancestorStorage,
1030 This->rootPropertySetIndex,
1031 &curProperty);
1032 if (success)
1034 curProperty.propertyUniqueID = *clsid;
1036 success = StorageImpl_WriteProperty(This->ancestorStorage,
1037 This->rootPropertySetIndex,
1038 &curProperty);
1039 if (success)
1040 hRes = S_OK;
1043 return hRes;
1046 /************************************************************************
1047 ** Storage32Impl implementation
1050 /************************************************************************
1051 * Storage32Impl_CreateStorage (IStorage)
1053 * This method will create the storage object within the provided storage.
1055 * See Windows documentation for more details on IStorage methods.
1057 HRESULT WINAPI StorageImpl_CreateStorage(
1058 IStorage* iface,
1059 const OLECHAR *pwcsName, /* [string][in] */
1060 DWORD grfMode, /* [in] */
1061 DWORD reserved1, /* [in] */
1062 DWORD reserved2, /* [in] */
1063 IStorage **ppstg) /* [out] */
1065 StorageImpl* const This=(StorageImpl*)iface;
1067 IEnumSTATSTGImpl *propertyEnumeration;
1068 StgProperty currentProperty;
1069 StgProperty newProperty;
1070 ULONG foundPropertyIndex;
1071 ULONG newPropertyIndex;
1072 HRESULT hr;
1074 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1075 iface, debugstr_w(pwcsName), grfMode,
1076 reserved1, reserved2, ppstg);
1079 * Validate parameters
1081 if (ppstg == 0)
1082 return STG_E_INVALIDPOINTER;
1084 if (pwcsName == 0)
1085 return STG_E_INVALIDNAME;
1088 * Validate the STGM flags
1090 if ( FAILED( validateSTGM(grfMode) ) ||
1091 (grfMode & STGM_DELETEONRELEASE) )
1092 return STG_E_INVALIDFLAG;
1095 * Initialize the out parameter
1097 *ppstg = 0;
1100 * Create a property enumeration and search the properties
1102 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1103 This->base.rootPropertySetIndex);
1105 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1106 pwcsName,
1107 &currentProperty);
1108 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1110 if (foundPropertyIndex != PROPERTY_NULL)
1113 * An element with this name already exists
1115 if (grfMode & STGM_CREATE)
1116 IStorage_DestroyElement(iface, pwcsName);
1117 else
1118 return STG_E_FILEALREADYEXISTS;
1122 * memset the empty property
1124 memset(&newProperty, 0, sizeof(StgProperty));
1126 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1128 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1129 return STG_E_INVALIDNAME;
1131 strcpyW(newProperty.name, pwcsName);
1133 newProperty.propertyType = PROPTYPE_STORAGE;
1134 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1135 newProperty.size.u.LowPart = 0;
1136 newProperty.size.u.HighPart = 0;
1138 newProperty.previousProperty = PROPERTY_NULL;
1139 newProperty.nextProperty = PROPERTY_NULL;
1140 newProperty.dirProperty = PROPERTY_NULL;
1142 /* call CoFileTime to get the current time
1143 newProperty.timeStampS1
1144 newProperty.timeStampD1
1145 newProperty.timeStampS2
1146 newProperty.timeStampD2
1149 /* newStorageProperty.propertyUniqueID */
1152 * Obtain a free property in the property chain
1154 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1157 * Save the new property into the new property spot
1159 StorageImpl_WriteProperty(
1160 This->base.ancestorStorage,
1161 newPropertyIndex,
1162 &newProperty);
1165 * Find a spot in the property chain for our newly created property.
1167 updatePropertyChain(
1168 This,
1169 newPropertyIndex,
1170 newProperty);
1173 * Open it to get a pointer to return.
1175 hr = IStorage_OpenStorage(
1176 iface,
1177 (const OLECHAR*)pwcsName,
1179 grfMode,
1182 ppstg);
1184 if( (hr != S_OK) || (*ppstg == NULL))
1186 return hr;
1190 return S_OK;
1194 /***************************************************************************
1196 * Internal Method
1198 * Get a free property or create a new one.
1200 static ULONG getFreeProperty(
1201 StorageImpl *storage)
1203 ULONG currentPropertyIndex = 0;
1204 ULONG newPropertyIndex = PROPERTY_NULL;
1205 BOOL readSuccessful = TRUE;
1206 StgProperty currentProperty;
1211 * Start by reading the root property
1213 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1214 currentPropertyIndex,
1215 &currentProperty);
1216 if (readSuccessful)
1218 if (currentProperty.sizeOfNameString == 0)
1221 * The property existis and is available, we found it.
1223 newPropertyIndex = currentPropertyIndex;
1226 else
1229 * We exhausted the property list, we will create more space below
1231 newPropertyIndex = currentPropertyIndex;
1233 currentPropertyIndex++;
1235 } while (newPropertyIndex == PROPERTY_NULL);
1238 * grow the property chain
1240 if (! readSuccessful)
1242 StgProperty emptyProperty;
1243 ULARGE_INTEGER newSize;
1244 ULONG propertyIndex;
1245 ULONG lastProperty = 0;
1246 ULONG blockCount = 0;
1249 * obtain the new count of property blocks
1251 blockCount = BlockChainStream_GetCount(
1252 storage->base.ancestorStorage->rootBlockChain)+1;
1255 * initialize the size used by the property stream
1257 newSize.u.HighPart = 0;
1258 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1261 * add a property block to the property chain
1263 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1266 * memset the empty property in order to initialize the unused newly
1267 * created property
1269 memset(&emptyProperty, 0, sizeof(StgProperty));
1272 * initialize them
1274 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1276 for(
1277 propertyIndex = newPropertyIndex;
1278 propertyIndex < lastProperty;
1279 propertyIndex++)
1281 StorageImpl_WriteProperty(
1282 storage->base.ancestorStorage,
1283 propertyIndex,
1284 &emptyProperty);
1288 return newPropertyIndex;
1291 /****************************************************************************
1293 * Internal Method
1295 * Case insensitive comparaison of StgProperty.name by first considering
1296 * their size.
1298 * Returns <0 when newPrpoerty < currentProperty
1299 * >0 when newPrpoerty > currentProperty
1300 * 0 when newPrpoerty == currentProperty
1302 static LONG propertyNameCmp(
1303 const OLECHAR *newProperty,
1304 const OLECHAR *currentProperty)
1306 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1308 if (diff == 0)
1311 * We compare the string themselves only when they are of the same length
1313 diff = lstrcmpiW( newProperty, currentProperty);
1316 return diff;
1319 /****************************************************************************
1321 * Internal Method
1323 * Properly link this new element in the property chain.
1325 static void updatePropertyChain(
1326 StorageImpl *storage,
1327 ULONG newPropertyIndex,
1328 StgProperty newProperty)
1330 StgProperty currentProperty;
1333 * Read the root property
1335 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1336 storage->base.rootPropertySetIndex,
1337 &currentProperty);
1339 if (currentProperty.dirProperty != PROPERTY_NULL)
1342 * The root storage contains some element, therefore, start the research
1343 * for the appropriate location.
1345 BOOL found = 0;
1346 ULONG current, next, previous, currentPropertyId;
1349 * Keep the StgProperty sequence number of the storage first property
1351 currentPropertyId = currentProperty.dirProperty;
1354 * Read
1356 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1357 currentProperty.dirProperty,
1358 &currentProperty);
1360 previous = currentProperty.previousProperty;
1361 next = currentProperty.nextProperty;
1362 current = currentPropertyId;
1364 while (found == 0)
1366 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1368 if (diff < 0)
1370 if (previous != PROPERTY_NULL)
1372 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1373 previous,
1374 &currentProperty);
1375 current = previous;
1377 else
1379 currentProperty.previousProperty = newPropertyIndex;
1380 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1381 current,
1382 &currentProperty);
1383 found = 1;
1386 else if (diff > 0)
1388 if (next != PROPERTY_NULL)
1390 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1391 next,
1392 &currentProperty);
1393 current = next;
1395 else
1397 currentProperty.nextProperty = newPropertyIndex;
1398 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1399 current,
1400 &currentProperty);
1401 found = 1;
1404 else
1407 * Trying to insert an item with the same name in the
1408 * subtree structure.
1410 assert(FALSE);
1413 previous = currentProperty.previousProperty;
1414 next = currentProperty.nextProperty;
1417 else
1420 * The root storage is empty, link the new property to it's dir property
1422 currentProperty.dirProperty = newPropertyIndex;
1423 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1424 storage->base.rootPropertySetIndex,
1425 &currentProperty);
1430 /*************************************************************************
1431 * CopyTo (IStorage)
1433 HRESULT WINAPI StorageImpl_CopyTo(
1434 IStorage* iface,
1435 DWORD ciidExclude, /* [in] */
1436 const IID* rgiidExclude, /* [size_is][unique][in] */
1437 SNB snbExclude, /* [unique][in] */
1438 IStorage* pstgDest) /* [unique][in] */
1440 IEnumSTATSTG *elements = 0;
1441 STATSTG curElement, strStat;
1442 HRESULT hr;
1443 IStorage *pstgTmp, *pstgChild;
1444 IStream *pstrTmp, *pstrChild;
1446 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1447 FIXME("Exclude option not implemented\n");
1449 TRACE("(%p, %ld, %p, %p, %p)\n",
1450 iface, ciidExclude, rgiidExclude,
1451 snbExclude, pstgDest);
1454 * Perform a sanity check
1456 if ( pstgDest == 0 )
1457 return STG_E_INVALIDPOINTER;
1460 * Enumerate the elements
1462 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1464 if ( hr != S_OK )
1465 return hr;
1468 * set the class ID
1470 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1471 IStorage_SetClass( pstgDest, &curElement.clsid );
1476 * Obtain the next element
1478 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1480 if ( hr == S_FALSE )
1482 hr = S_OK; /* done, every element has been copied */
1483 break;
1486 if (curElement.type == STGTY_STORAGE)
1489 * open child source storage
1491 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1492 STGM_READ|STGM_SHARE_EXCLUSIVE,
1493 NULL, 0, &pstgChild );
1495 if (hr != S_OK)
1496 break;
1499 * Check if destination storage is not a child of the source
1500 * storage, which will cause an infinite loop
1502 if (pstgChild == pstgDest)
1504 IEnumSTATSTG_Release(elements);
1506 return STG_E_ACCESSDENIED;
1510 * create a new storage in destination storage
1512 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1513 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1514 0, 0,
1515 &pstgTmp );
1517 * if it already exist, don't create a new one use this one
1519 if (hr == STG_E_FILEALREADYEXISTS)
1521 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1522 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1523 NULL, 0, &pstgTmp );
1526 if (hr != S_OK)
1527 break;
1531 * do the copy recursively
1533 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1534 snbExclude, pstgTmp );
1536 IStorage_Release( pstgTmp );
1537 IStorage_Release( pstgChild );
1539 else if (curElement.type == STGTY_STREAM)
1542 * create a new stream in destination storage. If the stream already
1543 * exist, it will be deleted and a new one will be created.
1545 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1546 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1547 0, 0, &pstrTmp );
1549 if (hr != S_OK)
1550 break;
1553 * open child stream storage
1555 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1556 STGM_READ|STGM_SHARE_EXCLUSIVE,
1557 0, &pstrChild );
1559 if (hr != S_OK)
1560 break;
1563 * Get the size of the source stream
1565 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1568 * Set the size of the destination stream.
1570 IStream_SetSize(pstrTmp, strStat.cbSize);
1573 * do the copy
1575 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1576 NULL, NULL );
1578 IStream_Release( pstrTmp );
1579 IStream_Release( pstrChild );
1581 else
1583 WARN("unknown element type: %ld\n", curElement.type);
1586 } while (hr == S_OK);
1589 * Clean-up
1591 IEnumSTATSTG_Release(elements);
1593 return hr;
1596 /*************************************************************************
1597 * MoveElementTo (IStorage)
1599 HRESULT WINAPI StorageImpl_MoveElementTo(
1600 IStorage* iface,
1601 const OLECHAR *pwcsName, /* [string][in] */
1602 IStorage *pstgDest, /* [unique][in] */
1603 const OLECHAR *pwcsNewName,/* [string][in] */
1604 DWORD grfFlags) /* [in] */
1606 FIXME("not implemented!\n");
1607 return E_NOTIMPL;
1610 /*************************************************************************
1611 * Commit (IStorage)
1613 HRESULT WINAPI StorageImpl_Commit(
1614 IStorage* iface,
1615 DWORD grfCommitFlags)/* [in] */
1617 FIXME("(%ld): stub!\n", grfCommitFlags);
1618 return S_OK;
1621 /*************************************************************************
1622 * Revert (IStorage)
1624 HRESULT WINAPI StorageImpl_Revert(
1625 IStorage* iface)
1627 FIXME("not implemented!\n");
1628 return E_NOTIMPL;
1631 /*************************************************************************
1632 * DestroyElement (IStorage)
1634 * Stategy: This implementation is build this way for simplicity not for speed.
1635 * I always delete the top most element of the enumeration and adjust
1636 * the deleted element pointer all the time. This takes longer to
1637 * do but allow to reinvoke DestroyElement whenever we encounter a
1638 * storage object. The optimisation reside in the usage of another
1639 * enumeration stategy that would give all the leaves of a storage
1640 * first. (postfix order)
1642 HRESULT WINAPI StorageImpl_DestroyElement(
1643 IStorage* iface,
1644 const OLECHAR *pwcsName)/* [string][in] */
1646 StorageImpl* const This=(StorageImpl*)iface;
1648 IEnumSTATSTGImpl* propertyEnumeration;
1649 HRESULT hr = S_OK;
1650 BOOL res;
1651 StgProperty propertyToDelete;
1652 StgProperty parentProperty;
1653 ULONG foundPropertyIndexToDelete;
1654 ULONG typeOfRelation;
1655 ULONG parentPropertyId;
1657 TRACE("(%p, %s)\n",
1658 iface, debugstr_w(pwcsName));
1661 * Perform a sanity check on the parameters.
1663 if (pwcsName==NULL)
1664 return STG_E_INVALIDPOINTER;
1667 * Create a property enumeration to search the property with the given name
1669 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1670 This->base.ancestorStorage,
1671 This->base.rootPropertySetIndex);
1673 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1674 propertyEnumeration,
1675 pwcsName,
1676 &propertyToDelete);
1678 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1680 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1682 return STG_E_FILENOTFOUND;
1686 * Find the parent property of the property to delete (the one that
1687 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1688 * the parent is This. Otherwise, the parent is one of it's sibling...
1692 * First, read This's StgProperty..
1694 res = StorageImpl_ReadProperty(
1695 This->base.ancestorStorage,
1696 This->base.rootPropertySetIndex,
1697 &parentProperty);
1699 assert(res);
1702 * Second, check to see if by any chance the actual storage (This) is not
1703 * the parent of the property to delete... We never know...
1705 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1708 * Set data as it would have been done in the else part...
1710 typeOfRelation = PROPERTY_RELATION_DIR;
1711 parentPropertyId = This->base.rootPropertySetIndex;
1713 else
1716 * Create a property enumeration to search the parent properties, and
1717 * delete it once done.
1719 IEnumSTATSTGImpl* propertyEnumeration2;
1721 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1722 This->base.ancestorStorage,
1723 This->base.rootPropertySetIndex);
1725 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1726 propertyEnumeration2,
1727 foundPropertyIndexToDelete,
1728 &parentProperty,
1729 &parentPropertyId);
1731 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1734 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1736 hr = deleteStorageProperty(
1737 This,
1738 foundPropertyIndexToDelete,
1739 propertyToDelete);
1741 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1743 hr = deleteStreamProperty(
1744 This,
1745 foundPropertyIndexToDelete,
1746 propertyToDelete);
1749 if (hr!=S_OK)
1750 return hr;
1753 * Adjust the property chain
1755 hr = adjustPropertyChain(
1756 This,
1757 propertyToDelete,
1758 parentProperty,
1759 parentPropertyId,
1760 typeOfRelation);
1762 return hr;
1766 /************************************************************************
1767 * StorageImpl_Stat (IStorage)
1769 * This method will retrieve information about this storage object.
1771 * See Windows documentation for more details on IStorage methods.
1773 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1774 STATSTG* pstatstg, /* [out] */
1775 DWORD grfStatFlag) /* [in] */
1777 StorageImpl* const This = (StorageImpl*)iface;
1778 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1780 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1782 CoTaskMemFree(pstatstg->pwcsName);
1783 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1784 strcpyW(pstatstg->pwcsName, This->pwcsName);
1787 return result;
1792 /*********************************************************************
1794 * Internal Method
1796 * Perform the deletion of a complete storage node
1799 static HRESULT deleteStorageProperty(
1800 StorageImpl *parentStorage,
1801 ULONG indexOfPropertyToDelete,
1802 StgProperty propertyToDelete)
1804 IEnumSTATSTG *elements = 0;
1805 IStorage *childStorage = 0;
1806 STATSTG currentElement;
1807 HRESULT hr;
1808 HRESULT destroyHr = S_OK;
1811 * Open the storage and enumerate it
1813 hr = StorageBaseImpl_OpenStorage(
1814 (IStorage*)parentStorage,
1815 propertyToDelete.name,
1817 STGM_SHARE_EXCLUSIVE,
1820 &childStorage);
1822 if (hr != S_OK)
1824 return hr;
1828 * Enumerate the elements
1830 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1835 * Obtain the next element
1837 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1838 if (hr==S_OK)
1840 destroyHr = StorageImpl_DestroyElement(
1841 (IStorage*)childStorage,
1842 (OLECHAR*)currentElement.pwcsName);
1844 CoTaskMemFree(currentElement.pwcsName);
1848 * We need to Reset the enumeration every time because we delete elements
1849 * and the enumeration could be invalid
1851 IEnumSTATSTG_Reset(elements);
1853 } while ((hr == S_OK) && (destroyHr == S_OK));
1856 * Invalidate the property by zeroing it's name member.
1858 propertyToDelete.sizeOfNameString = 0;
1860 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1861 indexOfPropertyToDelete,
1862 &propertyToDelete);
1864 IStorage_Release(childStorage);
1865 IEnumSTATSTG_Release(elements);
1867 return destroyHr;
1870 /*********************************************************************
1872 * Internal Method
1874 * Perform the deletion of a stream node
1877 static HRESULT deleteStreamProperty(
1878 StorageImpl *parentStorage,
1879 ULONG indexOfPropertyToDelete,
1880 StgProperty propertyToDelete)
1882 IStream *pis;
1883 HRESULT hr;
1884 ULARGE_INTEGER size;
1886 size.u.HighPart = 0;
1887 size.u.LowPart = 0;
1889 hr = StorageBaseImpl_OpenStream(
1890 (IStorage*)parentStorage,
1891 (OLECHAR*)propertyToDelete.name,
1892 NULL,
1893 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1895 &pis);
1897 if (hr!=S_OK)
1899 return(hr);
1903 * Zap the stream
1905 hr = IStream_SetSize(pis, size);
1907 if(hr != S_OK)
1909 return hr;
1913 * Release the stream object.
1915 IStream_Release(pis);
1918 * Invalidate the property by zeroing it's name member.
1920 propertyToDelete.sizeOfNameString = 0;
1923 * Here we should re-read the property so we get the updated pointer
1924 * but since we are here to zap it, I don't do it...
1926 StorageImpl_WriteProperty(
1927 parentStorage->base.ancestorStorage,
1928 indexOfPropertyToDelete,
1929 &propertyToDelete);
1931 return S_OK;
1934 /*********************************************************************
1936 * Internal Method
1938 * Finds a placeholder for the StgProperty within the Storage
1941 static HRESULT findPlaceholder(
1942 StorageImpl *storage,
1943 ULONG propertyIndexToStore,
1944 ULONG storePropertyIndex,
1945 INT typeOfRelation)
1947 StgProperty storeProperty;
1948 HRESULT hr = S_OK;
1949 BOOL res = TRUE;
1952 * Read the storage property
1954 res = StorageImpl_ReadProperty(
1955 storage->base.ancestorStorage,
1956 storePropertyIndex,
1957 &storeProperty);
1959 if(! res)
1961 return E_FAIL;
1964 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1966 if (storeProperty.previousProperty != PROPERTY_NULL)
1968 return findPlaceholder(
1969 storage,
1970 propertyIndexToStore,
1971 storeProperty.previousProperty,
1972 typeOfRelation);
1974 else
1976 storeProperty.previousProperty = propertyIndexToStore;
1979 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1981 if (storeProperty.nextProperty != PROPERTY_NULL)
1983 return findPlaceholder(
1984 storage,
1985 propertyIndexToStore,
1986 storeProperty.nextProperty,
1987 typeOfRelation);
1989 else
1991 storeProperty.nextProperty = propertyIndexToStore;
1994 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1996 if (storeProperty.dirProperty != PROPERTY_NULL)
1998 return findPlaceholder(
1999 storage,
2000 propertyIndexToStore,
2001 storeProperty.dirProperty,
2002 typeOfRelation);
2004 else
2006 storeProperty.dirProperty = propertyIndexToStore;
2010 hr = StorageImpl_WriteProperty(
2011 storage->base.ancestorStorage,
2012 storePropertyIndex,
2013 &storeProperty);
2015 if(! hr)
2017 return E_FAIL;
2020 return S_OK;
2023 /*************************************************************************
2025 * Internal Method
2027 * This method takes the previous and the next property link of a property
2028 * to be deleted and find them a place in the Storage.
2030 static HRESULT adjustPropertyChain(
2031 StorageImpl *This,
2032 StgProperty propertyToDelete,
2033 StgProperty parentProperty,
2034 ULONG parentPropertyId,
2035 INT typeOfRelation)
2037 ULONG newLinkProperty = PROPERTY_NULL;
2038 BOOL needToFindAPlaceholder = FALSE;
2039 ULONG storeNode = PROPERTY_NULL;
2040 ULONG toStoreNode = PROPERTY_NULL;
2041 INT relationType = 0;
2042 HRESULT hr = S_OK;
2043 BOOL res = TRUE;
2045 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2047 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2050 * Set the parent previous to the property to delete previous
2052 newLinkProperty = propertyToDelete.previousProperty;
2054 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2057 * We also need to find a storage for the other link, setup variables
2058 * to do this at the end...
2060 needToFindAPlaceholder = TRUE;
2061 storeNode = propertyToDelete.previousProperty;
2062 toStoreNode = propertyToDelete.nextProperty;
2063 relationType = PROPERTY_RELATION_NEXT;
2066 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2069 * Set the parent previous to the property to delete next
2071 newLinkProperty = propertyToDelete.nextProperty;
2075 * Link it for real...
2077 parentProperty.previousProperty = newLinkProperty;
2080 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2082 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2085 * Set the parent next to the property to delete next previous
2087 newLinkProperty = propertyToDelete.previousProperty;
2089 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2092 * We also need to find a storage for the other link, setup variables
2093 * to do this at the end...
2095 needToFindAPlaceholder = TRUE;
2096 storeNode = propertyToDelete.previousProperty;
2097 toStoreNode = propertyToDelete.nextProperty;
2098 relationType = PROPERTY_RELATION_NEXT;
2101 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2104 * Set the parent next to the property to delete next
2106 newLinkProperty = propertyToDelete.nextProperty;
2110 * Link it for real...
2112 parentProperty.nextProperty = newLinkProperty;
2114 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2116 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2119 * Set the parent dir to the property to delete previous
2121 newLinkProperty = propertyToDelete.previousProperty;
2123 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2126 * We also need to find a storage for the other link, setup variables
2127 * to do this at the end...
2129 needToFindAPlaceholder = TRUE;
2130 storeNode = propertyToDelete.previousProperty;
2131 toStoreNode = propertyToDelete.nextProperty;
2132 relationType = PROPERTY_RELATION_NEXT;
2135 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2138 * Set the parent dir to the property to delete next
2140 newLinkProperty = propertyToDelete.nextProperty;
2144 * Link it for real...
2146 parentProperty.dirProperty = newLinkProperty;
2150 * Write back the parent property
2152 res = StorageImpl_WriteProperty(
2153 This->base.ancestorStorage,
2154 parentPropertyId,
2155 &parentProperty);
2156 if(! res)
2158 return E_FAIL;
2162 * If a placeholder is required for the other link, then, find one and
2163 * get out of here...
2165 if (needToFindAPlaceholder)
2167 hr = findPlaceholder(
2168 This,
2169 toStoreNode,
2170 storeNode,
2171 relationType);
2174 return hr;
2178 /******************************************************************************
2179 * SetElementTimes (IStorage)
2181 HRESULT WINAPI StorageImpl_SetElementTimes(
2182 IStorage* iface,
2183 const OLECHAR *pwcsName,/* [string][in] */
2184 const FILETIME *pctime, /* [in] */
2185 const FILETIME *patime, /* [in] */
2186 const FILETIME *pmtime) /* [in] */
2188 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2189 return S_OK;
2192 /******************************************************************************
2193 * SetStateBits (IStorage)
2195 HRESULT WINAPI StorageImpl_SetStateBits(
2196 IStorage* iface,
2197 DWORD grfStateBits,/* [in] */
2198 DWORD grfMask) /* [in] */
2200 FIXME("not implemented!\n");
2201 return E_NOTIMPL;
2204 HRESULT StorageImpl_Construct(
2205 StorageImpl* This,
2206 HANDLE hFile,
2207 LPCOLESTR pwcsName,
2208 ILockBytes* pLkbyt,
2209 DWORD openFlags,
2210 BOOL fileBased,
2211 BOOL fileCreate)
2213 HRESULT hr = S_OK;
2214 StgProperty currentProperty;
2215 BOOL readSuccessful;
2216 ULONG currentPropertyIndex;
2218 if ( FAILED( validateSTGM(openFlags) ))
2219 return STG_E_INVALIDFLAG;
2221 memset(This, 0, sizeof(StorageImpl));
2224 * Initialize the virtual function table.
2226 This->base.lpVtbl = &Storage32Impl_Vtbl;
2227 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2228 This->base.v_destructor = &StorageImpl_Destroy;
2231 * This is the top-level storage so initialize the ancestor pointer
2232 * to this.
2234 This->base.ancestorStorage = This;
2237 * Initialize the physical support of the storage.
2239 This->hFile = hFile;
2242 * Store copy of file path.
2244 if(pwcsName) {
2245 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2246 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2247 if (!This->pwcsName)
2248 return STG_E_INSUFFICIENTMEMORY;
2249 strcpyW(This->pwcsName, pwcsName);
2253 * Initialize the big block cache.
2255 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2256 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2257 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2258 pLkbyt,
2259 openFlags,
2260 This->bigBlockSize,
2261 fileBased);
2263 if (This->bigBlockFile == 0)
2264 return E_FAIL;
2266 if (fileCreate)
2268 ULARGE_INTEGER size;
2269 BYTE* bigBlockBuffer;
2272 * Initialize all header variables:
2273 * - The big block depot consists of one block and it is at block 0
2274 * - The properties start at block 1
2275 * - There is no small block depot
2277 memset( This->bigBlockDepotStart,
2278 BLOCK_UNUSED,
2279 sizeof(This->bigBlockDepotStart));
2281 This->bigBlockDepotCount = 1;
2282 This->bigBlockDepotStart[0] = 0;
2283 This->rootStartBlock = 1;
2284 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2285 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2286 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2287 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2288 This->extBigBlockDepotCount = 0;
2290 StorageImpl_SaveFileHeader(This);
2293 * Add one block for the big block depot and one block for the properties
2295 size.u.HighPart = 0;
2296 size.u.LowPart = This->bigBlockSize * 3;
2297 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2300 * Initialize the big block depot
2302 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2303 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2304 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2305 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2306 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2308 else
2311 * Load the header for the file.
2313 hr = StorageImpl_LoadFileHeader(This);
2315 if (FAILED(hr))
2317 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2319 return hr;
2324 * There is no block depot cached yet.
2326 This->indexBlockDepotCached = 0xFFFFFFFF;
2329 * Start searching for free blocks with block 0.
2331 This->prevFreeBlock = 0;
2334 * Create the block chain abstractions.
2336 if(!(This->rootBlockChain =
2337 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2338 return STG_E_READFAULT;
2340 if(!(This->smallBlockDepotChain =
2341 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2342 PROPERTY_NULL)))
2343 return STG_E_READFAULT;
2346 * Write the root property
2348 if (fileCreate)
2350 StgProperty rootProp;
2352 * Initialize the property chain
2354 memset(&rootProp, 0, sizeof(rootProp));
2355 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2356 sizeof(rootProp.name)/sizeof(WCHAR) );
2357 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2358 rootProp.propertyType = PROPTYPE_ROOT;
2359 rootProp.previousProperty = PROPERTY_NULL;
2360 rootProp.nextProperty = PROPERTY_NULL;
2361 rootProp.dirProperty = PROPERTY_NULL;
2362 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2363 rootProp.size.u.HighPart = 0;
2364 rootProp.size.u.LowPart = 0;
2366 StorageImpl_WriteProperty(This, 0, &rootProp);
2370 * Find the ID of the root in the property sets.
2372 currentPropertyIndex = 0;
2376 readSuccessful = StorageImpl_ReadProperty(
2377 This,
2378 currentPropertyIndex,
2379 &currentProperty);
2381 if (readSuccessful)
2383 if ( (currentProperty.sizeOfNameString != 0 ) &&
2384 (currentProperty.propertyType == PROPTYPE_ROOT) )
2386 This->base.rootPropertySetIndex = currentPropertyIndex;
2390 currentPropertyIndex++;
2392 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2394 if (!readSuccessful)
2396 /* TODO CLEANUP */
2397 return STG_E_READFAULT;
2401 * Create the block chain abstraction for the small block root chain.
2403 if(!(This->smallBlockRootChain =
2404 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2405 return STG_E_READFAULT;
2407 return hr;
2410 void StorageImpl_Destroy(StorageBaseImpl* iface)
2412 StorageImpl *This = (StorageImpl*) iface;
2413 TRACE("(%p)\n", This);
2415 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2417 BlockChainStream_Destroy(This->smallBlockRootChain);
2418 BlockChainStream_Destroy(This->rootBlockChain);
2419 BlockChainStream_Destroy(This->smallBlockDepotChain);
2421 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2422 return;
2425 /******************************************************************************
2426 * Storage32Impl_GetNextFreeBigBlock
2428 * Returns the index of the next free big block.
2429 * If the big block depot is filled, this method will enlarge it.
2432 ULONG StorageImpl_GetNextFreeBigBlock(
2433 StorageImpl* This)
2435 ULONG depotBlockIndexPos;
2436 void *depotBuffer;
2437 ULONG depotBlockOffset;
2438 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2439 ULONG nextBlockIndex = BLOCK_SPECIAL;
2440 int depotIndex = 0;
2441 ULONG freeBlock = BLOCK_UNUSED;
2443 depotIndex = This->prevFreeBlock / blocksPerDepot;
2444 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2447 * Scan the entire big block depot until we find a block marked free
2449 while (nextBlockIndex != BLOCK_UNUSED)
2451 if (depotIndex < COUNT_BBDEPOTINHEADER)
2453 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2456 * Grow the primary depot.
2458 if (depotBlockIndexPos == BLOCK_UNUSED)
2460 depotBlockIndexPos = depotIndex*blocksPerDepot;
2463 * Add a block depot.
2465 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2466 This->bigBlockDepotCount++;
2467 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2470 * Flag it as a block depot.
2472 StorageImpl_SetNextBlockInChain(This,
2473 depotBlockIndexPos,
2474 BLOCK_SPECIAL);
2476 /* Save new header information.
2478 StorageImpl_SaveFileHeader(This);
2481 else
2483 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2485 if (depotBlockIndexPos == BLOCK_UNUSED)
2488 * Grow the extended depot.
2490 ULONG extIndex = BLOCK_UNUSED;
2491 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2492 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2494 if (extBlockOffset == 0)
2496 /* We need an extended block.
2498 extIndex = Storage32Impl_AddExtBlockDepot(This);
2499 This->extBigBlockDepotCount++;
2500 depotBlockIndexPos = extIndex + 1;
2502 else
2503 depotBlockIndexPos = depotIndex * blocksPerDepot;
2506 * Add a block depot and mark it in the extended block.
2508 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2509 This->bigBlockDepotCount++;
2510 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2512 /* Flag the block depot.
2514 StorageImpl_SetNextBlockInChain(This,
2515 depotBlockIndexPos,
2516 BLOCK_SPECIAL);
2518 /* If necessary, flag the extended depot block.
2520 if (extIndex != BLOCK_UNUSED)
2521 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2523 /* Save header information.
2525 StorageImpl_SaveFileHeader(This);
2529 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2531 if (depotBuffer != 0)
2533 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2534 ( nextBlockIndex != BLOCK_UNUSED))
2536 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2538 if (nextBlockIndex == BLOCK_UNUSED)
2540 freeBlock = (depotIndex * blocksPerDepot) +
2541 (depotBlockOffset/sizeof(ULONG));
2544 depotBlockOffset += sizeof(ULONG);
2547 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2550 depotIndex++;
2551 depotBlockOffset = 0;
2554 This->prevFreeBlock = freeBlock;
2556 return freeBlock;
2559 /******************************************************************************
2560 * Storage32Impl_AddBlockDepot
2562 * This will create a depot block, essentially it is a block initialized
2563 * to BLOCK_UNUSEDs.
2565 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2567 BYTE* blockBuffer;
2569 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2572 * Initialize blocks as free
2574 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2576 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2579 /******************************************************************************
2580 * Storage32Impl_GetExtDepotBlock
2582 * Returns the index of the block that corresponds to the specified depot
2583 * index. This method is only for depot indexes equal or greater than
2584 * COUNT_BBDEPOTINHEADER.
2586 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2588 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2589 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2590 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2591 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2592 ULONG blockIndex = BLOCK_UNUSED;
2593 ULONG extBlockIndex = This->extBigBlockDepotStart;
2595 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2597 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2598 return BLOCK_UNUSED;
2600 while (extBlockCount > 0)
2602 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2603 extBlockCount--;
2606 if (extBlockIndex != BLOCK_UNUSED)
2608 BYTE* depotBuffer;
2610 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2612 if (depotBuffer != 0)
2614 StorageUtl_ReadDWord(depotBuffer,
2615 extBlockOffset * sizeof(ULONG),
2616 &blockIndex);
2618 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2622 return blockIndex;
2625 /******************************************************************************
2626 * Storage32Impl_SetExtDepotBlock
2628 * Associates the specified block index to the specified depot index.
2629 * This method is only for depot indexes equal or greater than
2630 * COUNT_BBDEPOTINHEADER.
2632 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2633 ULONG depotIndex,
2634 ULONG blockIndex)
2636 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2637 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2638 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2639 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2640 ULONG extBlockIndex = This->extBigBlockDepotStart;
2642 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2644 while (extBlockCount > 0)
2646 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2647 extBlockCount--;
2650 if (extBlockIndex != BLOCK_UNUSED)
2652 BYTE* depotBuffer;
2654 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2656 if (depotBuffer != 0)
2658 StorageUtl_WriteDWord(depotBuffer,
2659 extBlockOffset * sizeof(ULONG),
2660 blockIndex);
2662 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2667 /******************************************************************************
2668 * Storage32Impl_AddExtBlockDepot
2670 * Creates an extended depot block.
2672 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2674 ULONG numExtBlocks = This->extBigBlockDepotCount;
2675 ULONG nextExtBlock = This->extBigBlockDepotStart;
2676 BYTE* depotBuffer = NULL;
2677 ULONG index = BLOCK_UNUSED;
2678 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2679 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2680 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2682 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2683 blocksPerDepotBlock;
2685 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2688 * The first extended block.
2690 This->extBigBlockDepotStart = index;
2692 else
2694 unsigned int i;
2696 * Follow the chain to the last one.
2698 for (i = 0; i < (numExtBlocks - 1); i++)
2700 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2704 * Add the new extended block to the chain.
2706 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2707 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2708 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2712 * Initialize this block.
2714 depotBuffer = StorageImpl_GetBigBlock(This, index);
2715 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2716 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2718 return index;
2721 /******************************************************************************
2722 * Storage32Impl_FreeBigBlock
2724 * This method will flag the specified block as free in the big block depot.
2726 void StorageImpl_FreeBigBlock(
2727 StorageImpl* This,
2728 ULONG blockIndex)
2730 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2732 if (blockIndex < This->prevFreeBlock)
2733 This->prevFreeBlock = blockIndex;
2736 /************************************************************************
2737 * Storage32Impl_GetNextBlockInChain
2739 * This method will retrieve the block index of the next big block in
2740 * in the chain.
2742 * Params: This - Pointer to the Storage object.
2743 * blockIndex - Index of the block to retrieve the chain
2744 * for.
2745 * nextBlockIndex - receives the return value.
2747 * Returns: This method returns the index of the next block in the chain.
2748 * It will return the constants:
2749 * BLOCK_SPECIAL - If the block given was not part of a
2750 * chain.
2751 * BLOCK_END_OF_CHAIN - If the block given was the last in
2752 * a chain.
2753 * BLOCK_UNUSED - If the block given was not past of a chain
2754 * and is available.
2755 * BLOCK_EXTBBDEPOT - This block is part of the extended
2756 * big block depot.
2758 * See Windows documentation for more details on IStorage methods.
2760 HRESULT StorageImpl_GetNextBlockInChain(
2761 StorageImpl* This,
2762 ULONG blockIndex,
2763 ULONG* nextBlockIndex)
2765 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2766 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2767 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2768 void* depotBuffer;
2769 ULONG depotBlockIndexPos;
2770 int index;
2772 *nextBlockIndex = BLOCK_SPECIAL;
2774 if(depotBlockCount >= This->bigBlockDepotCount)
2776 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2777 This->bigBlockDepotCount);
2778 return STG_E_READFAULT;
2782 * Cache the currently accessed depot block.
2784 if (depotBlockCount != This->indexBlockDepotCached)
2786 This->indexBlockDepotCached = depotBlockCount;
2788 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2790 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2792 else
2795 * We have to look in the extended depot.
2797 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2800 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2802 if (!depotBuffer)
2803 return STG_E_READFAULT;
2805 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2807 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2808 This->blockDepotCached[index] = *nextBlockIndex;
2810 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2813 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2815 return S_OK;
2818 /******************************************************************************
2819 * Storage32Impl_GetNextExtendedBlock
2821 * Given an extended block this method will return the next extended block.
2823 * NOTES:
2824 * The last ULONG of an extended block is the block index of the next
2825 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2826 * depot.
2828 * Return values:
2829 * - The index of the next extended block
2830 * - BLOCK_UNUSED: there is no next extended block.
2831 * - Any other return values denotes failure.
2833 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2835 ULONG nextBlockIndex = BLOCK_SPECIAL;
2836 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2837 void* depotBuffer;
2839 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2841 if (depotBuffer!=0)
2843 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2845 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2848 return nextBlockIndex;
2851 /******************************************************************************
2852 * Storage32Impl_SetNextBlockInChain
2854 * This method will write the index of the specified block's next block
2855 * in the big block depot.
2857 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2858 * do the following
2860 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2861 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2862 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2865 void StorageImpl_SetNextBlockInChain(
2866 StorageImpl* This,
2867 ULONG blockIndex,
2868 ULONG nextBlock)
2870 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2871 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2872 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2873 ULONG depotBlockIndexPos;
2874 void* depotBuffer;
2876 assert(depotBlockCount < This->bigBlockDepotCount);
2877 assert(blockIndex != nextBlock);
2879 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2881 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2883 else
2886 * We have to look in the extended depot.
2888 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2891 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2893 if (depotBuffer!=0)
2895 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2896 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2900 * Update the cached block depot, if necessary.
2902 if (depotBlockCount == This->indexBlockDepotCached)
2904 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2908 /******************************************************************************
2909 * Storage32Impl_LoadFileHeader
2911 * This method will read in the file header, i.e. big block index -1.
2913 HRESULT StorageImpl_LoadFileHeader(
2914 StorageImpl* This)
2916 HRESULT hr = STG_E_FILENOTFOUND;
2917 void* headerBigBlock = NULL;
2918 int index;
2921 * Get a pointer to the big block of data containing the header.
2923 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2926 * Extract the information from the header.
2928 if (headerBigBlock!=0)
2931 * Check for the "magic number" signature and return an error if it is not
2932 * found.
2934 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2936 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2937 return STG_E_OLDFORMAT;
2940 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2942 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2943 return STG_E_INVALIDHEADER;
2946 StorageUtl_ReadWord(
2947 headerBigBlock,
2948 OFFSET_BIGBLOCKSIZEBITS,
2949 &This->bigBlockSizeBits);
2951 StorageUtl_ReadWord(
2952 headerBigBlock,
2953 OFFSET_SMALLBLOCKSIZEBITS,
2954 &This->smallBlockSizeBits);
2956 StorageUtl_ReadDWord(
2957 headerBigBlock,
2958 OFFSET_BBDEPOTCOUNT,
2959 &This->bigBlockDepotCount);
2961 StorageUtl_ReadDWord(
2962 headerBigBlock,
2963 OFFSET_ROOTSTARTBLOCK,
2964 &This->rootStartBlock);
2966 StorageUtl_ReadDWord(
2967 headerBigBlock,
2968 OFFSET_SBDEPOTSTART,
2969 &This->smallBlockDepotStart);
2971 StorageUtl_ReadDWord(
2972 headerBigBlock,
2973 OFFSET_EXTBBDEPOTSTART,
2974 &This->extBigBlockDepotStart);
2976 StorageUtl_ReadDWord(
2977 headerBigBlock,
2978 OFFSET_EXTBBDEPOTCOUNT,
2979 &This->extBigBlockDepotCount);
2981 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2983 StorageUtl_ReadDWord(
2984 headerBigBlock,
2985 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2986 &(This->bigBlockDepotStart[index]));
2990 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2992 if ((1 << 2) == 4)
2994 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2995 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2997 else
2999 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3000 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3004 * Right now, the code is making some assumptions about the size of the
3005 * blocks, just make sure they are what we're expecting.
3007 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3008 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3010 WARN("Broken OLE storage file\n");
3011 hr = STG_E_INVALIDHEADER;
3013 else
3014 hr = S_OK;
3017 * Release the block.
3019 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3022 return hr;
3025 /******************************************************************************
3026 * Storage32Impl_SaveFileHeader
3028 * This method will save to the file the header, i.e. big block -1.
3030 void StorageImpl_SaveFileHeader(
3031 StorageImpl* This)
3033 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3034 int index;
3035 BOOL success;
3038 * Get a pointer to the big block of data containing the header.
3040 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3043 * If the block read failed, the file is probably new.
3045 if (!success)
3048 * Initialize for all unknown fields.
3050 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3053 * Initialize the magic number.
3055 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3058 * And a bunch of things we don't know what they mean
3060 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3061 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3062 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3063 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3067 * Write the information to the header.
3069 StorageUtl_WriteWord(
3070 headerBigBlock,
3071 OFFSET_BIGBLOCKSIZEBITS,
3072 This->bigBlockSizeBits);
3074 StorageUtl_WriteWord(
3075 headerBigBlock,
3076 OFFSET_SMALLBLOCKSIZEBITS,
3077 This->smallBlockSizeBits);
3079 StorageUtl_WriteDWord(
3080 headerBigBlock,
3081 OFFSET_BBDEPOTCOUNT,
3082 This->bigBlockDepotCount);
3084 StorageUtl_WriteDWord(
3085 headerBigBlock,
3086 OFFSET_ROOTSTARTBLOCK,
3087 This->rootStartBlock);
3089 StorageUtl_WriteDWord(
3090 headerBigBlock,
3091 OFFSET_SBDEPOTSTART,
3092 This->smallBlockDepotStart);
3094 StorageUtl_WriteDWord(
3095 headerBigBlock,
3096 OFFSET_SBDEPOTCOUNT,
3097 This->smallBlockDepotChain ?
3098 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3100 StorageUtl_WriteDWord(
3101 headerBigBlock,
3102 OFFSET_EXTBBDEPOTSTART,
3103 This->extBigBlockDepotStart);
3105 StorageUtl_WriteDWord(
3106 headerBigBlock,
3107 OFFSET_EXTBBDEPOTCOUNT,
3108 This->extBigBlockDepotCount);
3110 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3112 StorageUtl_WriteDWord(
3113 headerBigBlock,
3114 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3115 (This->bigBlockDepotStart[index]));
3119 * Write the big block back to the file.
3121 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3124 /******************************************************************************
3125 * Storage32Impl_ReadProperty
3127 * This method will read the specified property from the property chain.
3129 BOOL StorageImpl_ReadProperty(
3130 StorageImpl* This,
3131 ULONG index,
3132 StgProperty* buffer)
3134 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3135 ULARGE_INTEGER offsetInPropSet;
3136 BOOL readSuccessful;
3137 ULONG bytesRead;
3139 offsetInPropSet.u.HighPart = 0;
3140 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3142 readSuccessful = BlockChainStream_ReadAt(
3143 This->rootBlockChain,
3144 offsetInPropSet,
3145 PROPSET_BLOCK_SIZE,
3146 currentProperty,
3147 &bytesRead);
3149 if (readSuccessful)
3151 /* replace the name of root entry (often "Root Entry") by the file name */
3152 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3153 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3155 memset(buffer->name, 0, sizeof(buffer->name));
3156 memcpy(
3157 buffer->name,
3158 propName,
3159 PROPERTY_NAME_BUFFER_LEN );
3160 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3162 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3164 StorageUtl_ReadWord(
3165 currentProperty,
3166 OFFSET_PS_NAMELENGTH,
3167 &buffer->sizeOfNameString);
3169 StorageUtl_ReadDWord(
3170 currentProperty,
3171 OFFSET_PS_PREVIOUSPROP,
3172 &buffer->previousProperty);
3174 StorageUtl_ReadDWord(
3175 currentProperty,
3176 OFFSET_PS_NEXTPROP,
3177 &buffer->nextProperty);
3179 StorageUtl_ReadDWord(
3180 currentProperty,
3181 OFFSET_PS_DIRPROP,
3182 &buffer->dirProperty);
3184 StorageUtl_ReadGUID(
3185 currentProperty,
3186 OFFSET_PS_GUID,
3187 &buffer->propertyUniqueID);
3189 StorageUtl_ReadDWord(
3190 currentProperty,
3191 OFFSET_PS_TSS1,
3192 &buffer->timeStampS1);
3194 StorageUtl_ReadDWord(
3195 currentProperty,
3196 OFFSET_PS_TSD1,
3197 &buffer->timeStampD1);
3199 StorageUtl_ReadDWord(
3200 currentProperty,
3201 OFFSET_PS_TSS2,
3202 &buffer->timeStampS2);
3204 StorageUtl_ReadDWord(
3205 currentProperty,
3206 OFFSET_PS_TSD2,
3207 &buffer->timeStampD2);
3209 StorageUtl_ReadDWord(
3210 currentProperty,
3211 OFFSET_PS_STARTBLOCK,
3212 &buffer->startingBlock);
3214 StorageUtl_ReadDWord(
3215 currentProperty,
3216 OFFSET_PS_SIZE,
3217 &buffer->size.u.LowPart);
3219 buffer->size.u.HighPart = 0;
3222 return readSuccessful;
3225 /*********************************************************************
3226 * Write the specified property into the property chain
3228 BOOL StorageImpl_WriteProperty(
3229 StorageImpl* This,
3230 ULONG index,
3231 StgProperty* buffer)
3233 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3234 ULARGE_INTEGER offsetInPropSet;
3235 BOOL writeSuccessful;
3236 ULONG bytesWritten;
3238 offsetInPropSet.u.HighPart = 0;
3239 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3241 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3243 memcpy(
3244 currentProperty + OFFSET_PS_NAME,
3245 buffer->name,
3246 PROPERTY_NAME_BUFFER_LEN );
3248 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3250 StorageUtl_WriteWord(
3251 currentProperty,
3252 OFFSET_PS_NAMELENGTH,
3253 buffer->sizeOfNameString);
3255 StorageUtl_WriteDWord(
3256 currentProperty,
3257 OFFSET_PS_PREVIOUSPROP,
3258 buffer->previousProperty);
3260 StorageUtl_WriteDWord(
3261 currentProperty,
3262 OFFSET_PS_NEXTPROP,
3263 buffer->nextProperty);
3265 StorageUtl_WriteDWord(
3266 currentProperty,
3267 OFFSET_PS_DIRPROP,
3268 buffer->dirProperty);
3270 StorageUtl_WriteGUID(
3271 currentProperty,
3272 OFFSET_PS_GUID,
3273 &buffer->propertyUniqueID);
3275 StorageUtl_WriteDWord(
3276 currentProperty,
3277 OFFSET_PS_TSS1,
3278 buffer->timeStampS1);
3280 StorageUtl_WriteDWord(
3281 currentProperty,
3282 OFFSET_PS_TSD1,
3283 buffer->timeStampD1);
3285 StorageUtl_WriteDWord(
3286 currentProperty,
3287 OFFSET_PS_TSS2,
3288 buffer->timeStampS2);
3290 StorageUtl_WriteDWord(
3291 currentProperty,
3292 OFFSET_PS_TSD2,
3293 buffer->timeStampD2);
3295 StorageUtl_WriteDWord(
3296 currentProperty,
3297 OFFSET_PS_STARTBLOCK,
3298 buffer->startingBlock);
3300 StorageUtl_WriteDWord(
3301 currentProperty,
3302 OFFSET_PS_SIZE,
3303 buffer->size.u.LowPart);
3305 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3306 offsetInPropSet,
3307 PROPSET_BLOCK_SIZE,
3308 currentProperty,
3309 &bytesWritten);
3310 return writeSuccessful;
3313 BOOL StorageImpl_ReadBigBlock(
3314 StorageImpl* This,
3315 ULONG blockIndex,
3316 void* buffer)
3318 void* bigBlockBuffer;
3320 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3322 if (bigBlockBuffer!=0)
3324 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3326 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3328 return TRUE;
3331 return FALSE;
3334 BOOL StorageImpl_WriteBigBlock(
3335 StorageImpl* This,
3336 ULONG blockIndex,
3337 void* buffer)
3339 void* bigBlockBuffer;
3341 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3343 if (bigBlockBuffer!=0)
3345 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3347 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3349 return TRUE;
3352 return FALSE;
3355 void* StorageImpl_GetROBigBlock(
3356 StorageImpl* This,
3357 ULONG blockIndex)
3359 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3362 void* StorageImpl_GetBigBlock(
3363 StorageImpl* This,
3364 ULONG blockIndex)
3366 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3369 void StorageImpl_ReleaseBigBlock(
3370 StorageImpl* This,
3371 void* pBigBlock)
3373 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3376 /******************************************************************************
3377 * Storage32Impl_SmallBlocksToBigBlocks
3379 * This method will convert a small block chain to a big block chain.
3380 * The small block chain will be destroyed.
3382 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3383 StorageImpl* This,
3384 SmallBlockChainStream** ppsbChain)
3386 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3387 ULARGE_INTEGER size, offset;
3388 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3389 ULONG propertyIndex;
3390 BOOL successRead, successWrite;
3391 StgProperty chainProperty;
3392 BYTE *buffer;
3393 BlockChainStream *bbTempChain = NULL;
3394 BlockChainStream *bigBlockChain = NULL;
3397 * Create a temporary big block chain that doesn't have
3398 * an associated property. This temporary chain will be
3399 * used to copy data from small blocks to big blocks.
3401 bbTempChain = BlockChainStream_Construct(This,
3402 &bbHeadOfChain,
3403 PROPERTY_NULL);
3404 if(!bbTempChain) return NULL;
3406 * Grow the big block chain.
3408 size = SmallBlockChainStream_GetSize(*ppsbChain);
3409 BlockChainStream_SetSize(bbTempChain, size);
3412 * Copy the contents of the small block chain to the big block chain
3413 * by small block size increments.
3415 offset.u.LowPart = 0;
3416 offset.u.HighPart = 0;
3417 cbTotalRead = 0;
3418 cbTotalWritten = 0;
3420 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3423 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3424 offset,
3425 DEF_SMALL_BLOCK_SIZE,
3426 buffer,
3427 &cbRead);
3428 cbTotalRead += cbRead;
3430 successWrite = BlockChainStream_WriteAt(bbTempChain,
3431 offset,
3432 cbRead,
3433 buffer,
3434 &cbWritten);
3435 cbTotalWritten += cbWritten;
3437 offset.u.LowPart += This->smallBlockSize;
3439 } while (successRead && successWrite);
3440 HeapFree(GetProcessHeap(),0,buffer);
3442 assert(cbTotalRead == cbTotalWritten);
3445 * Destroy the small block chain.
3447 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3448 size.u.HighPart = 0;
3449 size.u.LowPart = 0;
3450 SmallBlockChainStream_SetSize(*ppsbChain, size);
3451 SmallBlockChainStream_Destroy(*ppsbChain);
3452 *ppsbChain = 0;
3455 * Change the property information. This chain is now a big block chain
3456 * and it doesn't reside in the small blocks chain anymore.
3458 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3460 chainProperty.startingBlock = bbHeadOfChain;
3462 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3465 * Destroy the temporary propertyless big block chain.
3466 * Create a new big block chain associated with this property.
3468 BlockChainStream_Destroy(bbTempChain);
3469 bigBlockChain = BlockChainStream_Construct(This,
3470 NULL,
3471 propertyIndex);
3473 return bigBlockChain;
3476 /******************************************************************************
3477 ** Storage32InternalImpl implementation
3480 StorageInternalImpl* StorageInternalImpl_Construct(
3481 StorageImpl* ancestorStorage,
3482 ULONG rootPropertyIndex)
3484 StorageInternalImpl* newStorage;
3487 * Allocate space for the new storage object
3489 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3491 if (newStorage!=0)
3493 memset(newStorage, 0, sizeof(StorageInternalImpl));
3496 * Initialize the virtual function table.
3498 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
3499 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
3502 * Keep the ancestor storage pointer and nail a reference to it.
3504 newStorage->base.ancestorStorage = ancestorStorage;
3505 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
3508 * Keep the index of the root property set for this storage,
3510 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
3512 return newStorage;
3515 return 0;
3518 void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3520 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3522 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3523 HeapFree(GetProcessHeap(), 0, This);
3526 /******************************************************************************
3528 ** Storage32InternalImpl_Commit
3530 ** The non-root storages cannot be opened in transacted mode thus this function
3531 ** does nothing.
3533 HRESULT WINAPI StorageInternalImpl_Commit(
3534 IStorage* iface,
3535 DWORD grfCommitFlags) /* [in] */
3537 return S_OK;
3540 /******************************************************************************
3542 ** Storage32InternalImpl_Revert
3544 ** The non-root storages cannot be opened in transacted mode thus this function
3545 ** does nothing.
3547 HRESULT WINAPI StorageInternalImpl_Revert(
3548 IStorage* iface)
3550 return S_OK;
3553 /******************************************************************************
3554 ** IEnumSTATSTGImpl implementation
3557 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3558 StorageImpl* parentStorage,
3559 ULONG firstPropertyNode)
3561 IEnumSTATSTGImpl* newEnumeration;
3563 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3565 if (newEnumeration!=0)
3568 * Set-up the virtual function table and reference count.
3570 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3571 newEnumeration->ref = 0;
3574 * We want to nail-down the reference to the storage in case the
3575 * enumeration out-lives the storage in the client application.
3577 newEnumeration->parentStorage = parentStorage;
3578 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3580 newEnumeration->firstPropertyNode = firstPropertyNode;
3583 * Initialize the search stack
3585 newEnumeration->stackSize = 0;
3586 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3587 newEnumeration->stackToVisit =
3588 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3591 * Make sure the current node of the iterator is the first one.
3593 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3596 return newEnumeration;
3599 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3601 IStorage_Release((IStorage*)This->parentStorage);
3602 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3603 HeapFree(GetProcessHeap(), 0, This);
3606 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3607 IEnumSTATSTG* iface,
3608 REFIID riid,
3609 void** ppvObject)
3611 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3614 * Perform a sanity check on the parameters.
3616 if (ppvObject==0)
3617 return E_INVALIDARG;
3620 * Initialize the return parameter.
3622 *ppvObject = 0;
3625 * Compare the riid with the interface IDs implemented by this object.
3627 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3629 *ppvObject = (IEnumSTATSTG*)This;
3631 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3633 *ppvObject = (IEnumSTATSTG*)This;
3637 * Check that we obtained an interface.
3639 if ((*ppvObject)==0)
3640 return E_NOINTERFACE;
3643 * Query Interface always increases the reference count by one when it is
3644 * successful
3646 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3648 return S_OK;
3651 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3652 IEnumSTATSTG* iface)
3654 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3655 return InterlockedIncrement(&This->ref);
3658 ULONG WINAPI IEnumSTATSTGImpl_Release(
3659 IEnumSTATSTG* iface)
3661 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3663 ULONG newRef;
3665 newRef = InterlockedDecrement(&This->ref);
3668 * If the reference count goes down to 0, perform suicide.
3670 if (newRef==0)
3672 IEnumSTATSTGImpl_Destroy(This);
3675 return newRef;
3678 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3679 IEnumSTATSTG* iface,
3680 ULONG celt,
3681 STATSTG* rgelt,
3682 ULONG* pceltFetched)
3684 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3686 StgProperty currentProperty;
3687 STATSTG* currentReturnStruct = rgelt;
3688 ULONG objectFetched = 0;
3689 ULONG currentSearchNode;
3692 * Perform a sanity check on the parameters.
3694 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3695 return E_INVALIDARG;
3698 * To avoid the special case, get another pointer to a ULONG value if
3699 * the caller didn't supply one.
3701 if (pceltFetched==0)
3702 pceltFetched = &objectFetched;
3705 * Start the iteration, we will iterate until we hit the end of the
3706 * linked list or until we hit the number of items to iterate through
3708 *pceltFetched = 0;
3711 * Start with the node at the top of the stack.
3713 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3715 while ( ( *pceltFetched < celt) &&
3716 ( currentSearchNode!=PROPERTY_NULL) )
3719 * Remove the top node from the stack
3721 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3724 * Read the property from the storage.
3726 StorageImpl_ReadProperty(This->parentStorage,
3727 currentSearchNode,
3728 &currentProperty);
3731 * Copy the information to the return buffer.
3733 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3734 &currentProperty,
3735 STATFLAG_DEFAULT);
3738 * Step to the next item in the iteration
3740 (*pceltFetched)++;
3741 currentReturnStruct++;
3744 * Push the next search node in the search stack.
3746 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3749 * continue the iteration.
3751 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3754 if (*pceltFetched == celt)
3755 return S_OK;
3757 return S_FALSE;
3761 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3762 IEnumSTATSTG* iface,
3763 ULONG celt)
3765 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3767 StgProperty currentProperty;
3768 ULONG objectFetched = 0;
3769 ULONG currentSearchNode;
3772 * Start with the node at the top of the stack.
3774 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3776 while ( (objectFetched < celt) &&
3777 (currentSearchNode!=PROPERTY_NULL) )
3780 * Remove the top node from the stack
3782 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3785 * Read the property from the storage.
3787 StorageImpl_ReadProperty(This->parentStorage,
3788 currentSearchNode,
3789 &currentProperty);
3792 * Step to the next item in the iteration
3794 objectFetched++;
3797 * Push the next search node in the search stack.
3799 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3802 * continue the iteration.
3804 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3807 if (objectFetched == celt)
3808 return S_OK;
3810 return S_FALSE;
3813 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3814 IEnumSTATSTG* iface)
3816 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3818 StgProperty rootProperty;
3819 BOOL readSuccessful;
3822 * Re-initialize the search stack to an empty stack
3824 This->stackSize = 0;
3827 * Read the root property from the storage.
3829 readSuccessful = StorageImpl_ReadProperty(
3830 This->parentStorage,
3831 This->firstPropertyNode,
3832 &rootProperty);
3834 if (readSuccessful)
3836 assert(rootProperty.sizeOfNameString!=0);
3839 * Push the search node in the search stack.
3841 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3844 return S_OK;
3847 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3848 IEnumSTATSTG* iface,
3849 IEnumSTATSTG** ppenum)
3851 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3853 IEnumSTATSTGImpl* newClone;
3856 * Perform a sanity check on the parameters.
3858 if (ppenum==0)
3859 return E_INVALIDARG;
3861 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3862 This->firstPropertyNode);
3866 * The new clone enumeration must point to the same current node as
3867 * the ole one.
3869 newClone->stackSize = This->stackSize ;
3870 newClone->stackMaxSize = This->stackMaxSize ;
3871 newClone->stackToVisit =
3872 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3874 memcpy(
3875 newClone->stackToVisit,
3876 This->stackToVisit,
3877 sizeof(ULONG) * newClone->stackSize);
3879 *ppenum = (IEnumSTATSTG*)newClone;
3882 * Don't forget to nail down a reference to the clone before
3883 * returning it.
3885 IEnumSTATSTGImpl_AddRef(*ppenum);
3887 return S_OK;
3890 INT IEnumSTATSTGImpl_FindParentProperty(
3891 IEnumSTATSTGImpl *This,
3892 ULONG childProperty,
3893 StgProperty *currentProperty,
3894 ULONG *thisNodeId)
3896 ULONG currentSearchNode;
3897 ULONG foundNode;
3900 * To avoid the special case, get another pointer to a ULONG value if
3901 * the caller didn't supply one.
3903 if (thisNodeId==0)
3904 thisNodeId = &foundNode;
3907 * Start with the node at the top of the stack.
3909 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3912 while (currentSearchNode!=PROPERTY_NULL)
3915 * Store the current node in the returned parameters
3917 *thisNodeId = currentSearchNode;
3920 * Remove the top node from the stack
3922 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3925 * Read the property from the storage.
3927 StorageImpl_ReadProperty(
3928 This->parentStorage,
3929 currentSearchNode,
3930 currentProperty);
3932 if (currentProperty->previousProperty == childProperty)
3933 return PROPERTY_RELATION_PREVIOUS;
3935 else if (currentProperty->nextProperty == childProperty)
3936 return PROPERTY_RELATION_NEXT;
3938 else if (currentProperty->dirProperty == childProperty)
3939 return PROPERTY_RELATION_DIR;
3942 * Push the next search node in the search stack.
3944 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3947 * continue the iteration.
3949 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3952 return PROPERTY_NULL;
3955 ULONG IEnumSTATSTGImpl_FindProperty(
3956 IEnumSTATSTGImpl* This,
3957 const OLECHAR* lpszPropName,
3958 StgProperty* currentProperty)
3960 ULONG currentSearchNode;
3963 * Start with the node at the top of the stack.
3965 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3967 while (currentSearchNode!=PROPERTY_NULL)
3970 * Remove the top node from the stack
3972 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3975 * Read the property from the storage.
3977 StorageImpl_ReadProperty(This->parentStorage,
3978 currentSearchNode,
3979 currentProperty);
3981 if ( propertyNameCmp(
3982 (const OLECHAR*)currentProperty->name,
3983 (const OLECHAR*)lpszPropName) == 0)
3984 return currentSearchNode;
3987 * Push the next search node in the search stack.
3989 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3992 * continue the iteration.
3994 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3997 return PROPERTY_NULL;
4000 void IEnumSTATSTGImpl_PushSearchNode(
4001 IEnumSTATSTGImpl* This,
4002 ULONG nodeToPush)
4004 StgProperty rootProperty;
4005 BOOL readSuccessful;
4008 * First, make sure we're not trying to push an unexisting node.
4010 if (nodeToPush==PROPERTY_NULL)
4011 return;
4014 * First push the node to the stack
4016 if (This->stackSize == This->stackMaxSize)
4018 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4020 This->stackToVisit = HeapReAlloc(
4021 GetProcessHeap(),
4023 This->stackToVisit,
4024 sizeof(ULONG) * This->stackMaxSize);
4027 This->stackToVisit[This->stackSize] = nodeToPush;
4028 This->stackSize++;
4031 * Read the root property from the storage.
4033 readSuccessful = StorageImpl_ReadProperty(
4034 This->parentStorage,
4035 nodeToPush,
4036 &rootProperty);
4038 if (readSuccessful)
4040 assert(rootProperty.sizeOfNameString!=0);
4043 * Push the previous search node in the search stack.
4045 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4049 ULONG IEnumSTATSTGImpl_PopSearchNode(
4050 IEnumSTATSTGImpl* This,
4051 BOOL remove)
4053 ULONG topNode;
4055 if (This->stackSize == 0)
4056 return PROPERTY_NULL;
4058 topNode = This->stackToVisit[This->stackSize-1];
4060 if (remove)
4061 This->stackSize--;
4063 return topNode;
4066 /******************************************************************************
4067 ** StorageUtl implementation
4070 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4072 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4075 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4077 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4080 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4082 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4085 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4087 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4090 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4092 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4093 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4094 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4096 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4099 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4101 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4102 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4103 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4105 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4108 void StorageUtl_CopyPropertyToSTATSTG(
4109 STATSTG* destination,
4110 StgProperty* source,
4111 int statFlags)
4114 * The copy of the string occurs only when the flag is not set
4116 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4117 (source->name == NULL) ||
4118 (source->name[0] == 0) )
4120 destination->pwcsName = 0;
4122 else
4124 destination->pwcsName =
4125 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4127 strcpyW((LPWSTR)destination->pwcsName, source->name);
4130 switch (source->propertyType)
4132 case PROPTYPE_STORAGE:
4133 case PROPTYPE_ROOT:
4134 destination->type = STGTY_STORAGE;
4135 break;
4136 case PROPTYPE_STREAM:
4137 destination->type = STGTY_STREAM;
4138 break;
4139 default:
4140 destination->type = STGTY_STREAM;
4141 break;
4144 destination->cbSize = source->size;
4146 currentReturnStruct->mtime = {0}; TODO
4147 currentReturnStruct->ctime = {0};
4148 currentReturnStruct->atime = {0};
4150 destination->grfMode = 0;
4151 destination->grfLocksSupported = 0;
4152 destination->clsid = source->propertyUniqueID;
4153 destination->grfStateBits = 0;
4154 destination->reserved = 0;
4157 /******************************************************************************
4158 ** BlockChainStream implementation
4161 BlockChainStream* BlockChainStream_Construct(
4162 StorageImpl* parentStorage,
4163 ULONG* headOfStreamPlaceHolder,
4164 ULONG propertyIndex)
4166 BlockChainStream* newStream;
4167 ULONG blockIndex;
4169 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4171 newStream->parentStorage = parentStorage;
4172 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4173 newStream->ownerPropertyIndex = propertyIndex;
4174 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4175 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4176 newStream->numBlocks = 0;
4178 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4180 while (blockIndex != BLOCK_END_OF_CHAIN)
4182 newStream->numBlocks++;
4183 newStream->tailIndex = blockIndex;
4185 if(FAILED(StorageImpl_GetNextBlockInChain(
4186 parentStorage,
4187 blockIndex,
4188 &blockIndex)))
4190 HeapFree(GetProcessHeap(), 0, newStream);
4191 return NULL;
4195 return newStream;
4198 void BlockChainStream_Destroy(BlockChainStream* This)
4200 HeapFree(GetProcessHeap(), 0, This);
4203 /******************************************************************************
4204 * BlockChainStream_GetHeadOfChain
4206 * Returns the head of this stream chain.
4207 * Some special chains don't have properties, their heads are kept in
4208 * This->headOfStreamPlaceHolder.
4211 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4213 StgProperty chainProperty;
4214 BOOL readSuccessful;
4216 if (This->headOfStreamPlaceHolder != 0)
4217 return *(This->headOfStreamPlaceHolder);
4219 if (This->ownerPropertyIndex != PROPERTY_NULL)
4221 readSuccessful = StorageImpl_ReadProperty(
4222 This->parentStorage,
4223 This->ownerPropertyIndex,
4224 &chainProperty);
4226 if (readSuccessful)
4228 return chainProperty.startingBlock;
4232 return BLOCK_END_OF_CHAIN;
4235 /******************************************************************************
4236 * BlockChainStream_GetCount
4238 * Returns the number of blocks that comprises this chain.
4239 * This is not the size of the stream as the last block may not be full!
4242 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4244 ULONG blockIndex;
4245 ULONG count = 0;
4247 blockIndex = BlockChainStream_GetHeadOfChain(This);
4249 while (blockIndex != BLOCK_END_OF_CHAIN)
4251 count++;
4253 if(FAILED(StorageImpl_GetNextBlockInChain(
4254 This->parentStorage,
4255 blockIndex,
4256 &blockIndex)))
4257 return 0;
4260 return count;
4263 /******************************************************************************
4264 * BlockChainStream_ReadAt
4266 * Reads a specified number of bytes from this chain at the specified offset.
4267 * bytesRead may be NULL.
4268 * Failure will be returned if the specified number of bytes has not been read.
4270 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4271 ULARGE_INTEGER offset,
4272 ULONG size,
4273 void* buffer,
4274 ULONG* bytesRead)
4276 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4277 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4278 ULONG bytesToReadInBuffer;
4279 ULONG blockIndex;
4280 BYTE* bufferWalker;
4281 BYTE* bigBlockBuffer;
4284 * Find the first block in the stream that contains part of the buffer.
4286 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4287 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4288 (blockNoInSequence < This->lastBlockNoInSequence) )
4290 blockIndex = BlockChainStream_GetHeadOfChain(This);
4291 This->lastBlockNoInSequence = blockNoInSequence;
4293 else
4295 ULONG temp = blockNoInSequence;
4297 blockIndex = This->lastBlockNoInSequenceIndex;
4298 blockNoInSequence -= This->lastBlockNoInSequence;
4299 This->lastBlockNoInSequence = temp;
4302 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4304 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4305 return FALSE;
4306 blockNoInSequence--;
4309 This->lastBlockNoInSequenceIndex = blockIndex;
4312 * Start reading the buffer.
4314 *bytesRead = 0;
4315 bufferWalker = buffer;
4317 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4320 * Calculate how many bytes we can copy from this big block.
4322 bytesToReadInBuffer =
4323 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4326 * Copy those bytes to the buffer
4328 bigBlockBuffer =
4329 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4331 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4333 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4336 * Step to the next big block.
4338 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4339 return FALSE;
4341 bufferWalker += bytesToReadInBuffer;
4342 size -= bytesToReadInBuffer;
4343 *bytesRead += bytesToReadInBuffer;
4344 offsetInBlock = 0; /* There is no offset on the next block */
4348 return (size == 0);
4351 /******************************************************************************
4352 * BlockChainStream_WriteAt
4354 * Writes the specified number of bytes to this chain at the specified offset.
4355 * bytesWritten may be NULL.
4356 * Will fail if not all specified number of bytes have been written.
4358 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4359 ULARGE_INTEGER offset,
4360 ULONG size,
4361 const void* buffer,
4362 ULONG* bytesWritten)
4364 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4365 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4366 ULONG bytesToWrite;
4367 ULONG blockIndex;
4368 const BYTE* bufferWalker;
4369 BYTE* bigBlockBuffer;
4372 * Find the first block in the stream that contains part of the buffer.
4374 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4375 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4376 (blockNoInSequence < This->lastBlockNoInSequence) )
4378 blockIndex = BlockChainStream_GetHeadOfChain(This);
4379 This->lastBlockNoInSequence = blockNoInSequence;
4381 else
4383 ULONG temp = blockNoInSequence;
4385 blockIndex = This->lastBlockNoInSequenceIndex;
4386 blockNoInSequence -= This->lastBlockNoInSequence;
4387 This->lastBlockNoInSequence = temp;
4390 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4392 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4393 &blockIndex)))
4394 return FALSE;
4395 blockNoInSequence--;
4398 This->lastBlockNoInSequenceIndex = blockIndex;
4401 * Here, I'm casting away the constness on the buffer variable
4402 * This is OK since we don't intend to modify that buffer.
4404 *bytesWritten = 0;
4405 bufferWalker = (const BYTE*)buffer;
4407 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4410 * Calculate how many bytes we can copy from this big block.
4412 bytesToWrite =
4413 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4416 * Copy those bytes to the buffer
4418 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4420 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4422 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4425 * Step to the next big block.
4427 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4428 &blockIndex)))
4429 return FALSE;
4430 bufferWalker += bytesToWrite;
4431 size -= bytesToWrite;
4432 *bytesWritten += bytesToWrite;
4433 offsetInBlock = 0; /* There is no offset on the next block */
4436 return (size == 0);
4439 /******************************************************************************
4440 * BlockChainStream_Shrink
4442 * Shrinks this chain in the big block depot.
4444 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4445 ULARGE_INTEGER newSize)
4447 ULONG blockIndex, extraBlock;
4448 ULONG numBlocks;
4449 ULONG count = 1;
4452 * Reset the last accessed block cache.
4454 This->lastBlockNoInSequence = 0xFFFFFFFF;
4455 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4458 * Figure out how many blocks are needed to contain the new size
4460 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4462 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4463 numBlocks++;
4465 blockIndex = BlockChainStream_GetHeadOfChain(This);
4468 * Go to the new end of chain
4470 while (count < numBlocks)
4472 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4473 &blockIndex)))
4474 return FALSE;
4475 count++;
4478 /* Get the next block before marking the new end */
4479 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4480 &extraBlock)))
4481 return FALSE;
4483 /* Mark the new end of chain */
4484 StorageImpl_SetNextBlockInChain(
4485 This->parentStorage,
4486 blockIndex,
4487 BLOCK_END_OF_CHAIN);
4489 This->tailIndex = blockIndex;
4490 This->numBlocks = numBlocks;
4493 * Mark the extra blocks as free
4495 while (extraBlock != BLOCK_END_OF_CHAIN)
4497 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4498 &blockIndex)))
4499 return FALSE;
4500 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4501 extraBlock = blockIndex;
4504 return TRUE;
4507 /******************************************************************************
4508 * BlockChainStream_Enlarge
4510 * Grows this chain in the big block depot.
4512 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4513 ULARGE_INTEGER newSize)
4515 ULONG blockIndex, currentBlock;
4516 ULONG newNumBlocks;
4517 ULONG oldNumBlocks = 0;
4519 blockIndex = BlockChainStream_GetHeadOfChain(This);
4522 * Empty chain. Create the head.
4524 if (blockIndex == BLOCK_END_OF_CHAIN)
4526 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4527 StorageImpl_SetNextBlockInChain(This->parentStorage,
4528 blockIndex,
4529 BLOCK_END_OF_CHAIN);
4531 if (This->headOfStreamPlaceHolder != 0)
4533 *(This->headOfStreamPlaceHolder) = blockIndex;
4535 else
4537 StgProperty chainProp;
4538 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4540 StorageImpl_ReadProperty(
4541 This->parentStorage,
4542 This->ownerPropertyIndex,
4543 &chainProp);
4545 chainProp.startingBlock = blockIndex;
4547 StorageImpl_WriteProperty(
4548 This->parentStorage,
4549 This->ownerPropertyIndex,
4550 &chainProp);
4553 This->tailIndex = blockIndex;
4554 This->numBlocks = 1;
4558 * Figure out how many blocks are needed to contain this stream
4560 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4562 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4563 newNumBlocks++;
4566 * Go to the current end of chain
4568 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4570 currentBlock = blockIndex;
4572 while (blockIndex != BLOCK_END_OF_CHAIN)
4574 This->numBlocks++;
4575 currentBlock = blockIndex;
4577 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4578 &blockIndex)))
4579 return FALSE;
4582 This->tailIndex = currentBlock;
4585 currentBlock = This->tailIndex;
4586 oldNumBlocks = This->numBlocks;
4589 * Add new blocks to the chain
4591 if (oldNumBlocks < newNumBlocks)
4593 while (oldNumBlocks < newNumBlocks)
4595 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4597 StorageImpl_SetNextBlockInChain(
4598 This->parentStorage,
4599 currentBlock,
4600 blockIndex);
4602 StorageImpl_SetNextBlockInChain(
4603 This->parentStorage,
4604 blockIndex,
4605 BLOCK_END_OF_CHAIN);
4607 currentBlock = blockIndex;
4608 oldNumBlocks++;
4611 This->tailIndex = blockIndex;
4612 This->numBlocks = newNumBlocks;
4615 return TRUE;
4618 /******************************************************************************
4619 * BlockChainStream_SetSize
4621 * Sets the size of this stream. The big block depot will be updated.
4622 * The file will grow if we grow the chain.
4624 * TODO: Free the actual blocks in the file when we shrink the chain.
4625 * Currently, the blocks are still in the file. So the file size
4626 * doesn't shrink even if we shrink streams.
4628 BOOL BlockChainStream_SetSize(
4629 BlockChainStream* This,
4630 ULARGE_INTEGER newSize)
4632 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4634 if (newSize.u.LowPart == size.u.LowPart)
4635 return TRUE;
4637 if (newSize.u.LowPart < size.u.LowPart)
4639 BlockChainStream_Shrink(This, newSize);
4641 else
4643 ULARGE_INTEGER fileSize =
4644 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4646 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4649 * Make sure the file stays a multiple of blocksize
4651 if ((diff % This->parentStorage->bigBlockSize) != 0)
4652 diff += (This->parentStorage->bigBlockSize -
4653 (diff % This->parentStorage->bigBlockSize) );
4655 fileSize.u.LowPart += diff;
4656 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4658 BlockChainStream_Enlarge(This, newSize);
4661 return TRUE;
4664 /******************************************************************************
4665 * BlockChainStream_GetSize
4667 * Returns the size of this chain.
4668 * Will return the block count if this chain doesn't have a property.
4670 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4672 StgProperty chainProperty;
4674 if(This->headOfStreamPlaceHolder == NULL)
4677 * This chain is a data stream read the property and return
4678 * the appropriate size
4680 StorageImpl_ReadProperty(
4681 This->parentStorage,
4682 This->ownerPropertyIndex,
4683 &chainProperty);
4685 return chainProperty.size;
4687 else
4690 * this chain is a chain that does not have a property, figure out the
4691 * size by making the product number of used blocks times the
4692 * size of them
4694 ULARGE_INTEGER result;
4695 result.u.HighPart = 0;
4697 result.u.LowPart =
4698 BlockChainStream_GetCount(This) *
4699 This->parentStorage->bigBlockSize;
4701 return result;
4705 /******************************************************************************
4706 ** SmallBlockChainStream implementation
4709 SmallBlockChainStream* SmallBlockChainStream_Construct(
4710 StorageImpl* parentStorage,
4711 ULONG propertyIndex)
4713 SmallBlockChainStream* newStream;
4715 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4717 newStream->parentStorage = parentStorage;
4718 newStream->ownerPropertyIndex = propertyIndex;
4720 return newStream;
4723 void SmallBlockChainStream_Destroy(
4724 SmallBlockChainStream* This)
4726 HeapFree(GetProcessHeap(), 0, This);
4729 /******************************************************************************
4730 * SmallBlockChainStream_GetHeadOfChain
4732 * Returns the head of this chain of small blocks.
4734 ULONG SmallBlockChainStream_GetHeadOfChain(
4735 SmallBlockChainStream* This)
4737 StgProperty chainProperty;
4738 BOOL readSuccessful;
4740 if (This->ownerPropertyIndex)
4742 readSuccessful = StorageImpl_ReadProperty(
4743 This->parentStorage,
4744 This->ownerPropertyIndex,
4745 &chainProperty);
4747 if (readSuccessful)
4749 return chainProperty.startingBlock;
4754 return BLOCK_END_OF_CHAIN;
4757 /******************************************************************************
4758 * SmallBlockChainStream_GetNextBlockInChain
4760 * Returns the index of the next small block in this chain.
4762 * Return Values:
4763 * - BLOCK_END_OF_CHAIN: end of this chain
4764 * - BLOCK_UNUSED: small block 'blockIndex' is free
4766 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4767 SmallBlockChainStream* This,
4768 ULONG blockIndex,
4769 ULONG* nextBlockInChain)
4771 ULARGE_INTEGER offsetOfBlockInDepot;
4772 DWORD buffer;
4773 ULONG bytesRead;
4774 BOOL success;
4776 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4778 offsetOfBlockInDepot.u.HighPart = 0;
4779 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4782 * Read those bytes in the buffer from the small block file.
4784 success = BlockChainStream_ReadAt(
4785 This->parentStorage->smallBlockDepotChain,
4786 offsetOfBlockInDepot,
4787 sizeof(DWORD),
4788 &buffer,
4789 &bytesRead);
4791 if (success)
4793 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4794 return S_OK;
4797 return STG_E_READFAULT;
4800 /******************************************************************************
4801 * SmallBlockChainStream_SetNextBlockInChain
4803 * Writes the index of the next block of the specified block in the small
4804 * block depot.
4805 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4806 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4808 void SmallBlockChainStream_SetNextBlockInChain(
4809 SmallBlockChainStream* This,
4810 ULONG blockIndex,
4811 ULONG nextBlock)
4813 ULARGE_INTEGER offsetOfBlockInDepot;
4814 DWORD buffer;
4815 ULONG bytesWritten;
4817 offsetOfBlockInDepot.u.HighPart = 0;
4818 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4820 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4823 * Read those bytes in the buffer from the small block file.
4825 BlockChainStream_WriteAt(
4826 This->parentStorage->smallBlockDepotChain,
4827 offsetOfBlockInDepot,
4828 sizeof(DWORD),
4829 &buffer,
4830 &bytesWritten);
4833 /******************************************************************************
4834 * SmallBlockChainStream_FreeBlock
4836 * Flag small block 'blockIndex' as free in the small block depot.
4838 void SmallBlockChainStream_FreeBlock(
4839 SmallBlockChainStream* This,
4840 ULONG blockIndex)
4842 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4845 /******************************************************************************
4846 * SmallBlockChainStream_GetNextFreeBlock
4848 * Returns the index of a free small block. The small block depot will be
4849 * enlarged if necessary. The small block chain will also be enlarged if
4850 * necessary.
4852 ULONG SmallBlockChainStream_GetNextFreeBlock(
4853 SmallBlockChainStream* This)
4855 ULARGE_INTEGER offsetOfBlockInDepot;
4856 DWORD buffer;
4857 ULONG bytesRead;
4858 ULONG blockIndex = 0;
4859 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4860 BOOL success = TRUE;
4861 ULONG smallBlocksPerBigBlock;
4863 offsetOfBlockInDepot.u.HighPart = 0;
4866 * Scan the small block depot for a free block
4868 while (nextBlockIndex != BLOCK_UNUSED)
4870 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4872 success = BlockChainStream_ReadAt(
4873 This->parentStorage->smallBlockDepotChain,
4874 offsetOfBlockInDepot,
4875 sizeof(DWORD),
4876 &buffer,
4877 &bytesRead);
4880 * If we run out of space for the small block depot, enlarge it
4882 if (success)
4884 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4886 if (nextBlockIndex != BLOCK_UNUSED)
4887 blockIndex++;
4889 else
4891 ULONG count =
4892 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4894 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4895 ULONG nextBlock, newsbdIndex;
4896 BYTE* smallBlockDepot;
4898 nextBlock = sbdIndex;
4899 while (nextBlock != BLOCK_END_OF_CHAIN)
4901 sbdIndex = nextBlock;
4902 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4905 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4906 if (sbdIndex != BLOCK_END_OF_CHAIN)
4907 StorageImpl_SetNextBlockInChain(
4908 This->parentStorage,
4909 sbdIndex,
4910 newsbdIndex);
4912 StorageImpl_SetNextBlockInChain(
4913 This->parentStorage,
4914 newsbdIndex,
4915 BLOCK_END_OF_CHAIN);
4918 * Initialize all the small blocks to free
4920 smallBlockDepot =
4921 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4923 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4924 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4926 if (count == 0)
4929 * We have just created the small block depot.
4931 StgProperty rootProp;
4932 ULONG sbStartIndex;
4935 * Save it in the header
4937 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4938 StorageImpl_SaveFileHeader(This->parentStorage);
4941 * And allocate the first big block that will contain small blocks
4943 sbStartIndex =
4944 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4946 StorageImpl_SetNextBlockInChain(
4947 This->parentStorage,
4948 sbStartIndex,
4949 BLOCK_END_OF_CHAIN);
4951 StorageImpl_ReadProperty(
4952 This->parentStorage,
4953 This->parentStorage->base.rootPropertySetIndex,
4954 &rootProp);
4956 rootProp.startingBlock = sbStartIndex;
4957 rootProp.size.u.HighPart = 0;
4958 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
4960 StorageImpl_WriteProperty(
4961 This->parentStorage,
4962 This->parentStorage->base.rootPropertySetIndex,
4963 &rootProp);
4968 smallBlocksPerBigBlock =
4969 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4972 * Verify if we have to allocate big blocks to contain small blocks
4974 if (blockIndex % smallBlocksPerBigBlock == 0)
4976 StgProperty rootProp;
4977 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4979 StorageImpl_ReadProperty(
4980 This->parentStorage,
4981 This->parentStorage->base.rootPropertySetIndex,
4982 &rootProp);
4984 if (rootProp.size.u.LowPart <
4985 (blocksRequired * This->parentStorage->bigBlockSize))
4987 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
4989 BlockChainStream_SetSize(
4990 This->parentStorage->smallBlockRootChain,
4991 rootProp.size);
4993 StorageImpl_WriteProperty(
4994 This->parentStorage,
4995 This->parentStorage->base.rootPropertySetIndex,
4996 &rootProp);
5000 return blockIndex;
5003 /******************************************************************************
5004 * SmallBlockChainStream_ReadAt
5006 * Reads a specified number of bytes from this chain at the specified offset.
5007 * bytesRead may be NULL.
5008 * Failure will be returned if the specified number of bytes has not been read.
5010 BOOL SmallBlockChainStream_ReadAt(
5011 SmallBlockChainStream* This,
5012 ULARGE_INTEGER offset,
5013 ULONG size,
5014 void* buffer,
5015 ULONG* bytesRead)
5017 ULARGE_INTEGER offsetInBigBlockFile;
5018 ULONG blockNoInSequence =
5019 offset.u.LowPart / This->parentStorage->smallBlockSize;
5021 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5022 ULONG bytesToReadInBuffer;
5023 ULONG blockIndex;
5024 ULONG bytesReadFromBigBlockFile;
5025 BYTE* bufferWalker;
5028 * This should never happen on a small block file.
5030 assert(offset.u.HighPart==0);
5033 * Find the first block in the stream that contains part of the buffer.
5035 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5037 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5039 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5040 &blockIndex)))
5041 return FALSE;
5042 blockNoInSequence--;
5046 * Start reading the buffer.
5048 *bytesRead = 0;
5049 bufferWalker = buffer;
5051 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5054 * Calculate how many bytes we can copy from this small block.
5056 bytesToReadInBuffer =
5057 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5060 * Calculate the offset of the small block in the small block file.
5062 offsetInBigBlockFile.u.HighPart = 0;
5063 offsetInBigBlockFile.u.LowPart =
5064 blockIndex * This->parentStorage->smallBlockSize;
5066 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5069 * Read those bytes in the buffer from the small block file.
5071 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5072 offsetInBigBlockFile,
5073 bytesToReadInBuffer,
5074 bufferWalker,
5075 &bytesReadFromBigBlockFile);
5077 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5080 * Step to the next big block.
5082 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5083 return FALSE;
5084 bufferWalker += bytesToReadInBuffer;
5085 size -= bytesToReadInBuffer;
5086 *bytesRead += bytesToReadInBuffer;
5087 offsetInBlock = 0; /* There is no offset on the next block */
5090 return (size == 0);
5093 /******************************************************************************
5094 * SmallBlockChainStream_WriteAt
5096 * Writes the specified number of bytes to this chain at the specified offset.
5097 * bytesWritten may be NULL.
5098 * Will fail if not all specified number of bytes have been written.
5100 BOOL SmallBlockChainStream_WriteAt(
5101 SmallBlockChainStream* This,
5102 ULARGE_INTEGER offset,
5103 ULONG size,
5104 const void* buffer,
5105 ULONG* bytesWritten)
5107 ULARGE_INTEGER offsetInBigBlockFile;
5108 ULONG blockNoInSequence =
5109 offset.u.LowPart / This->parentStorage->smallBlockSize;
5111 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5112 ULONG bytesToWriteInBuffer;
5113 ULONG blockIndex;
5114 ULONG bytesWrittenFromBigBlockFile;
5115 const BYTE* bufferWalker;
5118 * This should never happen on a small block file.
5120 assert(offset.u.HighPart==0);
5123 * Find the first block in the stream that contains part of the buffer.
5125 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5127 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5129 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5130 return FALSE;
5131 blockNoInSequence--;
5135 * Start writing the buffer.
5137 * Here, I'm casting away the constness on the buffer variable
5138 * This is OK since we don't intend to modify that buffer.
5140 *bytesWritten = 0;
5141 bufferWalker = (const BYTE*)buffer;
5142 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5145 * Calculate how many bytes we can copy to this small block.
5147 bytesToWriteInBuffer =
5148 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5151 * Calculate the offset of the small block in the small block file.
5153 offsetInBigBlockFile.u.HighPart = 0;
5154 offsetInBigBlockFile.u.LowPart =
5155 blockIndex * This->parentStorage->smallBlockSize;
5157 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5160 * Write those bytes in the buffer to the small block file.
5162 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5163 offsetInBigBlockFile,
5164 bytesToWriteInBuffer,
5165 bufferWalker,
5166 &bytesWrittenFromBigBlockFile);
5168 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5171 * Step to the next big block.
5173 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5174 &blockIndex)))
5175 return FALSE;
5176 bufferWalker += bytesToWriteInBuffer;
5177 size -= bytesToWriteInBuffer;
5178 *bytesWritten += bytesToWriteInBuffer;
5179 offsetInBlock = 0; /* There is no offset on the next block */
5182 return (size == 0);
5185 /******************************************************************************
5186 * SmallBlockChainStream_Shrink
5188 * Shrinks this chain in the small block depot.
5190 BOOL SmallBlockChainStream_Shrink(
5191 SmallBlockChainStream* This,
5192 ULARGE_INTEGER newSize)
5194 ULONG blockIndex, extraBlock;
5195 ULONG numBlocks;
5196 ULONG count = 0;
5198 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5200 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5201 numBlocks++;
5203 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5206 * Go to the new end of chain
5208 while (count < numBlocks)
5210 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5211 &blockIndex)))
5212 return FALSE;
5213 count++;
5217 * If the count is 0, we have a special case, the head of the chain was
5218 * just freed.
5220 if (count == 0)
5222 StgProperty chainProp;
5224 StorageImpl_ReadProperty(This->parentStorage,
5225 This->ownerPropertyIndex,
5226 &chainProp);
5228 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5230 StorageImpl_WriteProperty(This->parentStorage,
5231 This->ownerPropertyIndex,
5232 &chainProp);
5235 * We start freeing the chain at the head block.
5237 extraBlock = blockIndex;
5239 else
5241 /* Get the next block before marking the new end */
5242 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5243 &extraBlock)))
5244 return FALSE;
5246 /* Mark the new end of chain */
5247 SmallBlockChainStream_SetNextBlockInChain(
5248 This,
5249 blockIndex,
5250 BLOCK_END_OF_CHAIN);
5254 * Mark the extra blocks as free
5256 while (extraBlock != BLOCK_END_OF_CHAIN)
5258 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5259 &blockIndex)))
5260 return FALSE;
5261 SmallBlockChainStream_FreeBlock(This, extraBlock);
5262 extraBlock = blockIndex;
5265 return TRUE;
5268 /******************************************************************************
5269 * SmallBlockChainStream_Enlarge
5271 * Grows this chain in the small block depot.
5273 BOOL SmallBlockChainStream_Enlarge(
5274 SmallBlockChainStream* This,
5275 ULARGE_INTEGER newSize)
5277 ULONG blockIndex, currentBlock;
5278 ULONG newNumBlocks;
5279 ULONG oldNumBlocks = 0;
5281 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5284 * Empty chain
5286 if (blockIndex == BLOCK_END_OF_CHAIN)
5289 StgProperty chainProp;
5291 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5292 &chainProp);
5294 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5296 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5297 &chainProp);
5299 blockIndex = chainProp.startingBlock;
5300 SmallBlockChainStream_SetNextBlockInChain(
5301 This,
5302 blockIndex,
5303 BLOCK_END_OF_CHAIN);
5306 currentBlock = blockIndex;
5309 * Figure out how many blocks are needed to contain this stream
5311 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5313 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5314 newNumBlocks++;
5317 * Go to the current end of chain
5319 while (blockIndex != BLOCK_END_OF_CHAIN)
5321 oldNumBlocks++;
5322 currentBlock = blockIndex;
5323 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5324 return FALSE;
5328 * Add new blocks to the chain
5330 while (oldNumBlocks < newNumBlocks)
5332 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5333 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5335 SmallBlockChainStream_SetNextBlockInChain(
5336 This,
5337 blockIndex,
5338 BLOCK_END_OF_CHAIN);
5340 currentBlock = blockIndex;
5341 oldNumBlocks++;
5344 return TRUE;
5347 /******************************************************************************
5348 * SmallBlockChainStream_GetCount
5350 * Returns the number of blocks that comprises this chain.
5351 * This is not the size of this chain as the last block may not be full!
5353 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5355 ULONG blockIndex;
5356 ULONG count = 0;
5358 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5360 while (blockIndex != BLOCK_END_OF_CHAIN)
5362 count++;
5364 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5365 return 0;
5368 return count;
5371 /******************************************************************************
5372 * SmallBlockChainStream_SetSize
5374 * Sets the size of this stream.
5375 * The file will grow if we grow the chain.
5377 * TODO: Free the actual blocks in the file when we shrink the chain.
5378 * Currently, the blocks are still in the file. So the file size
5379 * doesn't shrink even if we shrink streams.
5381 BOOL SmallBlockChainStream_SetSize(
5382 SmallBlockChainStream* This,
5383 ULARGE_INTEGER newSize)
5385 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5387 if (newSize.u.LowPart == size.u.LowPart)
5388 return TRUE;
5390 if (newSize.u.LowPart < size.u.LowPart)
5392 SmallBlockChainStream_Shrink(This, newSize);
5394 else
5396 SmallBlockChainStream_Enlarge(This, newSize);
5399 return TRUE;
5402 /******************************************************************************
5403 * SmallBlockChainStream_GetSize
5405 * Returns the size of this chain.
5407 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5409 StgProperty chainProperty;
5411 StorageImpl_ReadProperty(
5412 This->parentStorage,
5413 This->ownerPropertyIndex,
5414 &chainProperty);
5416 return chainProperty.size;
5419 /******************************************************************************
5420 * StgCreateDocfile [OLE32.@]
5422 HRESULT WINAPI StgCreateDocfile(
5423 LPCOLESTR pwcsName,
5424 DWORD grfMode,
5425 DWORD reserved,
5426 IStorage **ppstgOpen)
5428 StorageImpl* newStorage = 0;
5429 HANDLE hFile = INVALID_HANDLE_VALUE;
5430 HRESULT hr = S_OK;
5431 DWORD shareMode;
5432 DWORD accessMode;
5433 DWORD creationMode;
5434 DWORD fileAttributes;
5435 WCHAR tempFileName[MAX_PATH];
5437 TRACE("(%s, %lx, %ld, %p)\n",
5438 debugstr_w(pwcsName), grfMode,
5439 reserved, ppstgOpen);
5442 * Validate the parameters
5444 if (ppstgOpen == 0)
5445 return STG_E_INVALIDPOINTER;
5446 if (reserved != 0)
5447 return STG_E_INVALIDPARAMETER;
5450 * Validate the STGM flags
5452 if ( FAILED( validateSTGM(grfMode) ))
5453 return STG_E_INVALIDFLAG;
5455 /* StgCreateDocFile always opens for write */
5456 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5457 return STG_E_INVALIDFLAG;
5459 /* always opens non-shared */
5460 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5461 return STG_E_INVALIDFLAG;
5464 * Generate a unique name.
5466 if (pwcsName == 0)
5468 WCHAR tempPath[MAX_PATH];
5469 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5471 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5472 return STG_E_INVALIDFLAG;
5473 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5474 return STG_E_INVALIDFLAG;
5476 memset(tempPath, 0, sizeof(tempPath));
5477 memset(tempFileName, 0, sizeof(tempFileName));
5479 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5480 tempPath[0] = '.';
5482 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5483 pwcsName = tempFileName;
5484 else
5485 return STG_E_INSUFFICIENTMEMORY;
5487 creationMode = TRUNCATE_EXISTING;
5489 else
5491 creationMode = GetCreationModeFromSTGM(grfMode);
5495 * Interpret the STGM value grfMode
5497 shareMode = GetShareModeFromSTGM(grfMode);
5498 accessMode = GetAccessModeFromSTGM(grfMode);
5500 if (grfMode & STGM_DELETEONRELEASE)
5501 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5502 else
5503 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5505 if (grfMode & STGM_TRANSACTED)
5506 FIXME("Transacted mode not implemented.\n");
5509 * Initialize the "out" parameter.
5511 *ppstgOpen = 0;
5513 hFile = CreateFileW(pwcsName,
5514 accessMode,
5515 shareMode,
5516 NULL,
5517 creationMode,
5518 fileAttributes,
5521 if (hFile == INVALID_HANDLE_VALUE)
5523 if(GetLastError() == ERROR_FILE_EXISTS)
5524 return STG_E_FILEALREADYEXISTS;
5525 return E_FAIL;
5529 * Allocate and initialize the new IStorage32object.
5531 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5533 if (newStorage == 0)
5534 return STG_E_INSUFFICIENTMEMORY;
5536 hr = StorageImpl_Construct(
5537 newStorage,
5538 hFile,
5539 pwcsName,
5540 NULL,
5541 grfMode,
5542 TRUE,
5543 TRUE);
5545 if (FAILED(hr))
5547 HeapFree(GetProcessHeap(), 0, newStorage);
5548 return hr;
5552 * Get an "out" pointer for the caller.
5554 hr = StorageBaseImpl_QueryInterface(
5555 (IStorage*)newStorage,
5556 (REFIID)&IID_IStorage,
5557 (void**)ppstgOpen);
5559 return hr;
5562 /******************************************************************************
5563 * StgCreateStorageEx [OLE32.@]
5565 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5567 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5568 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5569 return STG_E_UNIMPLEMENTEDFUNCTION;
5572 /******************************************************************************
5573 * StgOpenStorage [OLE32.@]
5575 HRESULT WINAPI StgOpenStorage(
5576 const OLECHAR *pwcsName,
5577 IStorage *pstgPriority,
5578 DWORD grfMode,
5579 SNB snbExclude,
5580 DWORD reserved,
5581 IStorage **ppstgOpen)
5583 StorageImpl* newStorage = 0;
5584 HRESULT hr = S_OK;
5585 HANDLE hFile = 0;
5586 DWORD shareMode;
5587 DWORD accessMode;
5588 WCHAR fullname[MAX_PATH];
5589 DWORD length;
5591 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5592 debugstr_w(pwcsName), pstgPriority, grfMode,
5593 snbExclude, reserved, ppstgOpen);
5596 * Perform sanity checks
5598 if (pwcsName == 0)
5600 hr = STG_E_INVALIDNAME;
5601 goto end;
5604 if (ppstgOpen == 0)
5606 hr = STG_E_INVALIDPOINTER;
5607 goto end;
5610 if (reserved)
5612 hr = STG_E_INVALIDPARAMETER;
5613 goto end;
5617 * Validate the sharing mode
5619 switch(STGM_SHARE_MODE(grfMode))
5621 case STGM_SHARE_EXCLUSIVE:
5622 case STGM_SHARE_DENY_WRITE:
5623 break;
5624 default:
5625 hr = STG_E_INVALIDFLAG;
5626 goto end;
5630 * Validate the STGM flags
5632 if ( FAILED( validateSTGM(grfMode) ) ||
5633 (grfMode&STGM_CREATE))
5635 hr = STG_E_INVALIDFLAG;
5636 goto end;
5640 * Interpret the STGM value grfMode
5642 shareMode = GetShareModeFromSTGM(grfMode);
5643 accessMode = GetAccessModeFromSTGM(grfMode);
5646 * Initialize the "out" parameter.
5648 *ppstgOpen = 0;
5650 hFile = CreateFileW( pwcsName,
5651 accessMode,
5652 shareMode,
5653 NULL,
5654 OPEN_EXISTING,
5655 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5658 if (hFile==INVALID_HANDLE_VALUE)
5660 DWORD last_error = GetLastError();
5662 hr = E_FAIL;
5664 switch (last_error)
5666 case ERROR_FILE_NOT_FOUND:
5667 hr = STG_E_FILENOTFOUND;
5668 break;
5670 case ERROR_PATH_NOT_FOUND:
5671 hr = STG_E_PATHNOTFOUND;
5672 break;
5674 case ERROR_ACCESS_DENIED:
5675 case ERROR_WRITE_PROTECT:
5676 hr = STG_E_ACCESSDENIED;
5677 break;
5679 case ERROR_SHARING_VIOLATION:
5680 hr = STG_E_SHAREVIOLATION;
5681 break;
5683 default:
5684 hr = E_FAIL;
5687 goto end;
5690 length = GetFileSize(hFile, NULL);
5693 * Allocate and initialize the new IStorage32object.
5695 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5697 if (newStorage == 0)
5699 hr = STG_E_INSUFFICIENTMEMORY;
5700 goto end;
5703 /* if the file's length was zero, initialize the storage */
5704 hr = StorageImpl_Construct(
5705 newStorage,
5706 hFile,
5707 pwcsName,
5708 NULL,
5709 grfMode,
5710 TRUE,
5711 !length );
5713 if (FAILED(hr))
5715 HeapFree(GetProcessHeap(), 0, newStorage);
5717 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5719 if(hr == STG_E_INVALIDHEADER)
5720 hr = STG_E_FILEALREADYEXISTS;
5721 goto end;
5724 /* prepare the file name string given in lieu of the root property name */
5725 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5726 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5727 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5730 * Get an "out" pointer for the caller.
5732 hr = StorageBaseImpl_QueryInterface(
5733 (IStorage*)newStorage,
5734 (REFIID)&IID_IStorage,
5735 (void**)ppstgOpen);
5737 end:
5738 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5739 return hr;
5742 /******************************************************************************
5743 * StgCreateDocfileOnILockBytes [OLE32.@]
5745 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5746 ILockBytes *plkbyt,
5747 DWORD grfMode,
5748 DWORD reserved,
5749 IStorage** ppstgOpen)
5751 StorageImpl* newStorage = 0;
5752 HRESULT hr = S_OK;
5755 * Validate the parameters
5757 if ((ppstgOpen == 0) || (plkbyt == 0))
5758 return STG_E_INVALIDPOINTER;
5761 * Allocate and initialize the new IStorage object.
5763 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5765 if (newStorage == 0)
5766 return STG_E_INSUFFICIENTMEMORY;
5768 hr = StorageImpl_Construct(
5769 newStorage,
5772 plkbyt,
5773 grfMode,
5774 FALSE,
5775 TRUE);
5777 if (FAILED(hr))
5779 HeapFree(GetProcessHeap(), 0, newStorage);
5780 return hr;
5784 * Get an "out" pointer for the caller.
5786 hr = StorageBaseImpl_QueryInterface(
5787 (IStorage*)newStorage,
5788 (REFIID)&IID_IStorage,
5789 (void**)ppstgOpen);
5791 return hr;
5794 /******************************************************************************
5795 * StgOpenStorageOnILockBytes [OLE32.@]
5797 HRESULT WINAPI StgOpenStorageOnILockBytes(
5798 ILockBytes *plkbyt,
5799 IStorage *pstgPriority,
5800 DWORD grfMode,
5801 SNB snbExclude,
5802 DWORD reserved,
5803 IStorage **ppstgOpen)
5805 StorageImpl* newStorage = 0;
5806 HRESULT hr = S_OK;
5809 * Perform a sanity check
5811 if ((plkbyt == 0) || (ppstgOpen == 0))
5812 return STG_E_INVALIDPOINTER;
5815 * Validate the STGM flags
5817 if ( FAILED( validateSTGM(grfMode) ))
5818 return STG_E_INVALIDFLAG;
5821 * Initialize the "out" parameter.
5823 *ppstgOpen = 0;
5826 * Allocate and initialize the new IStorage object.
5828 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5830 if (newStorage == 0)
5831 return STG_E_INSUFFICIENTMEMORY;
5833 hr = StorageImpl_Construct(
5834 newStorage,
5837 plkbyt,
5838 grfMode,
5839 FALSE,
5840 FALSE);
5842 if (FAILED(hr))
5844 HeapFree(GetProcessHeap(), 0, newStorage);
5845 return hr;
5849 * Get an "out" pointer for the caller.
5851 hr = StorageBaseImpl_QueryInterface(
5852 (IStorage*)newStorage,
5853 (REFIID)&IID_IStorage,
5854 (void**)ppstgOpen);
5856 return hr;
5859 /******************************************************************************
5860 * StgSetTimes [ole32.@]
5861 * StgSetTimes [OLE32.@]
5865 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *a,
5866 FILETIME const *b, FILETIME const *c )
5868 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5869 return S_OK;
5872 /******************************************************************************
5873 * StgIsStorageILockBytes [OLE32.@]
5875 * Determines if the ILockBytes contains a storage object.
5877 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5879 BYTE sig[8];
5880 ULARGE_INTEGER offset;
5882 offset.u.HighPart = 0;
5883 offset.u.LowPart = 0;
5885 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5887 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5888 return S_OK;
5890 return S_FALSE;
5893 /******************************************************************************
5894 * WriteClassStg [OLE32.@]
5896 * This method will store the specified CLSID in the specified storage object
5898 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5900 HRESULT hRes;
5902 assert(pStg != 0);
5904 hRes = IStorage_SetClass(pStg, rclsid);
5906 return hRes;
5909 /***********************************************************************
5910 * ReadClassStg (OLE32.@)
5912 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5914 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5916 STATSTG pstatstg;
5917 HRESULT hRes;
5919 TRACE("()\n");
5921 if(pclsid==NULL)
5922 return E_POINTER;
5924 * read a STATSTG structure (contains the clsid) from the storage
5926 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5928 if(SUCCEEDED(hRes))
5929 *pclsid=pstatstg.clsid;
5931 return hRes;
5934 /***********************************************************************
5935 * OleLoadFromStream (OLE32.@)
5937 * This function loads an object from stream
5939 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5941 CLSID clsid;
5942 HRESULT res;
5943 LPPERSISTSTREAM xstm;
5945 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5947 res=ReadClassStm(pStm,&clsid);
5948 if (!SUCCEEDED(res))
5949 return res;
5950 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5951 if (!SUCCEEDED(res))
5952 return res;
5953 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5954 if (!SUCCEEDED(res)) {
5955 IUnknown_Release((IUnknown*)*ppvObj);
5956 return res;
5958 res=IPersistStream_Load(xstm,pStm);
5959 IPersistStream_Release(xstm);
5960 /* FIXME: all refcounts ok at this point? I think they should be:
5961 * pStm : unchanged
5962 * ppvObj : 1
5963 * xstm : 0 (released)
5965 return res;
5968 /***********************************************************************
5969 * OleSaveToStream (OLE32.@)
5971 * This function saves an object with the IPersistStream interface on it
5972 * to the specified stream.
5974 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5977 CLSID clsid;
5978 HRESULT res;
5980 TRACE("(%p,%p)\n",pPStm,pStm);
5982 res=IPersistStream_GetClassID(pPStm,&clsid);
5984 if (SUCCEEDED(res)){
5986 res=WriteClassStm(pStm,&clsid);
5988 if (SUCCEEDED(res))
5990 res=IPersistStream_Save(pPStm,pStm,TRUE);
5993 TRACE("Finished Save\n");
5994 return res;
5997 /****************************************************************************
5998 * This method validate a STGM parameter that can contain the values below
6000 * STGM_DIRECT 0x00000000
6001 * STGM_TRANSACTED 0x00010000
6002 * STGM_SIMPLE 0x08000000
6004 * STGM_READ 0x00000000
6005 * STGM_WRITE 0x00000001
6006 * STGM_READWRITE 0x00000002
6008 * STGM_SHARE_DENY_NONE 0x00000040
6009 * STGM_SHARE_DENY_READ 0x00000030
6010 * STGM_SHARE_DENY_WRITE 0x00000020
6011 * STGM_SHARE_EXCLUSIVE 0x00000010
6013 * STGM_PRIORITY 0x00040000
6014 * STGM_DELETEONRELEASE 0x04000000
6016 * STGM_CREATE 0x00001000
6017 * STGM_CONVERT 0x00020000
6018 * STGM_FAILIFTHERE 0x00000000
6020 * STGM_NOSCRATCH 0x00100000
6021 * STGM_NOSNAPSHOT 0x00200000
6023 static HRESULT validateSTGM(DWORD stgm)
6025 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
6026 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
6027 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
6029 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6030 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6031 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6033 BOOL bSTGM_SHARE_DENY_NONE =
6034 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6036 BOOL bSTGM_SHARE_DENY_READ =
6037 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6039 BOOL bSTGM_SHARE_DENY_WRITE =
6040 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6042 BOOL bSTGM_SHARE_EXCLUSIVE =
6043 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6045 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
6046 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
6048 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
6049 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
6052 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6054 if ( ! bSTGM_DIRECT )
6055 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
6056 return E_FAIL;
6059 * STGM_WRITE | STGM_READWRITE | STGM_READ
6061 if ( ! bSTGM_READ )
6062 if( bSTGM_WRITE && bSTGM_READWRITE )
6063 return E_FAIL;
6066 * STGM_SHARE_DENY_NONE | others
6067 * (I assume here that DENY_READ implies DENY_WRITE)
6069 if ( bSTGM_SHARE_DENY_NONE )
6070 if ( bSTGM_SHARE_DENY_READ ||
6071 bSTGM_SHARE_DENY_WRITE ||
6072 bSTGM_SHARE_EXCLUSIVE)
6073 return E_FAIL;
6076 * STGM_CREATE | STGM_CONVERT
6077 * if both are false, STGM_FAILIFTHERE is set to TRUE
6079 if ( bSTGM_CREATE && bSTGM_CONVERT )
6080 return E_FAIL;
6083 * STGM_NOSCRATCH requires STGM_TRANSACTED
6085 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6086 return E_FAIL;
6089 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6090 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6092 if (bSTGM_NOSNAPSHOT)
6094 if ( ! ( bSTGM_TRANSACTED &&
6095 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6096 return E_FAIL;
6099 return S_OK;
6102 /****************************************************************************
6103 * GetShareModeFromSTGM
6105 * This method will return a share mode flag from a STGM value.
6106 * The STGM value is assumed valid.
6108 static DWORD GetShareModeFromSTGM(DWORD stgm)
6110 DWORD dwShareMode = 0;
6111 BOOL bSTGM_SHARE_DENY_NONE =
6112 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6114 BOOL bSTGM_SHARE_DENY_READ =
6115 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6117 BOOL bSTGM_SHARE_DENY_WRITE =
6118 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6120 BOOL bSTGM_SHARE_EXCLUSIVE =
6121 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6123 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6124 dwShareMode = 0;
6126 if (bSTGM_SHARE_DENY_NONE)
6127 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6129 if (bSTGM_SHARE_DENY_WRITE)
6130 dwShareMode = FILE_SHARE_READ;
6132 return dwShareMode;
6135 /****************************************************************************
6136 * GetAccessModeFromSTGM
6138 * This method will return an access mode flag from a STGM value.
6139 * The STGM value is assumed valid.
6141 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6143 DWORD dwDesiredAccess = GENERIC_READ;
6144 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6145 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6146 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6148 if (bSTGM_READ)
6149 dwDesiredAccess = GENERIC_READ;
6151 if (bSTGM_WRITE)
6152 dwDesiredAccess |= GENERIC_WRITE;
6154 if (bSTGM_READWRITE)
6155 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6157 return dwDesiredAccess;
6160 /****************************************************************************
6161 * GetCreationModeFromSTGM
6163 * This method will return a creation mode flag from a STGM value.
6164 * The STGM value is assumed valid.
6166 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6168 if ( stgm & STGM_CREATE)
6169 return CREATE_ALWAYS;
6170 if (stgm & STGM_CONVERT) {
6171 FIXME("STGM_CONVERT not implemented!\n");
6172 return CREATE_NEW;
6174 /* All other cases */
6175 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6176 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6177 return CREATE_NEW;
6181 /*************************************************************************
6182 * OLECONVERT_LoadOLE10 [Internal]
6184 * Loads the OLE10 STREAM to memory
6186 * PARAMS
6187 * pOleStream [I] The OLESTREAM
6188 * pData [I] Data Structure for the OLESTREAM Data
6190 * RETURNS
6191 * Success: S_OK
6192 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6193 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6195 * NOTES
6196 * This function is used by OleConvertOLESTREAMToIStorage only.
6198 * Memory allocated for pData must be freed by the caller
6200 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6202 DWORD dwSize;
6203 HRESULT hRes = S_OK;
6204 int nTryCnt=0;
6205 int max_try = 6;
6207 pData->pData = NULL;
6208 pData->pstrOleObjFileName = (CHAR *) NULL;
6210 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6212 /* Get the OleID */
6213 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6214 if(dwSize != sizeof(pData->dwOleID))
6216 hRes = CONVERT10_E_OLESTREAM_GET;
6218 else if(pData->dwOleID != OLESTREAM_ID)
6220 hRes = CONVERT10_E_OLESTREAM_FMT;
6222 else
6224 hRes = S_OK;
6225 break;
6229 if(hRes == S_OK)
6231 /* Get the TypeID...more info needed for this field */
6232 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6233 if(dwSize != sizeof(pData->dwTypeID))
6235 hRes = CONVERT10_E_OLESTREAM_GET;
6238 if(hRes == S_OK)
6240 if(pData->dwTypeID != 0)
6242 /* Get the length of the OleTypeName */
6243 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6244 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6246 hRes = CONVERT10_E_OLESTREAM_GET;
6249 if(hRes == S_OK)
6251 if(pData->dwOleTypeNameLength > 0)
6253 /* Get the OleTypeName */
6254 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6255 if(dwSize != pData->dwOleTypeNameLength)
6257 hRes = CONVERT10_E_OLESTREAM_GET;
6261 if(bStrem1)
6263 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6264 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6266 hRes = CONVERT10_E_OLESTREAM_GET;
6268 if(hRes == S_OK)
6270 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6271 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6272 pData->pstrOleObjFileName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6273 if(pData->pstrOleObjFileName)
6275 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6276 if(dwSize != pData->dwOleObjFileNameLength)
6278 hRes = CONVERT10_E_OLESTREAM_GET;
6281 else
6282 hRes = CONVERT10_E_OLESTREAM_GET;
6285 else
6287 /* Get the Width of the Metafile */
6288 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6289 if(dwSize != sizeof(pData->dwMetaFileWidth))
6291 hRes = CONVERT10_E_OLESTREAM_GET;
6293 if(hRes == S_OK)
6295 /* Get the Height of the Metafile */
6296 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6297 if(dwSize != sizeof(pData->dwMetaFileHeight))
6299 hRes = CONVERT10_E_OLESTREAM_GET;
6303 if(hRes == S_OK)
6305 /* Get the Length of the Data */
6306 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6307 if(dwSize != sizeof(pData->dwDataLength))
6309 hRes = CONVERT10_E_OLESTREAM_GET;
6313 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6315 if(!bStrem1) /* if it is a second OLE stream data */
6317 pData->dwDataLength -= 8;
6318 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6319 if(dwSize != sizeof(pData->strUnknown))
6321 hRes = CONVERT10_E_OLESTREAM_GET;
6325 if(hRes == S_OK)
6327 if(pData->dwDataLength > 0)
6329 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6331 /* Get Data (ex. IStorage, Metafile, or BMP) */
6332 if(pData->pData)
6334 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6335 if(dwSize != pData->dwDataLength)
6337 hRes = CONVERT10_E_OLESTREAM_GET;
6340 else
6342 hRes = CONVERT10_E_OLESTREAM_GET;
6348 return hRes;
6351 /*************************************************************************
6352 * OLECONVERT_SaveOLE10 [Internal]
6354 * Saves the OLE10 STREAM From memory
6356 * PARAMS
6357 * pData [I] Data Structure for the OLESTREAM Data
6358 * pOleStream [I] The OLESTREAM to save
6360 * RETURNS
6361 * Success: S_OK
6362 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6364 * NOTES
6365 * This function is used by OleConvertIStorageToOLESTREAM only.
6368 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6370 DWORD dwSize;
6371 HRESULT hRes = S_OK;
6374 /* Set the OleID */
6375 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6376 if(dwSize != sizeof(pData->dwOleID))
6378 hRes = CONVERT10_E_OLESTREAM_PUT;
6381 if(hRes == S_OK)
6383 /* Set the TypeID */
6384 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6385 if(dwSize != sizeof(pData->dwTypeID))
6387 hRes = CONVERT10_E_OLESTREAM_PUT;
6391 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6393 /* Set the Length of the OleTypeName */
6394 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6395 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6397 hRes = CONVERT10_E_OLESTREAM_PUT;
6400 if(hRes == S_OK)
6402 if(pData->dwOleTypeNameLength > 0)
6404 /* Set the OleTypeName */
6405 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6406 if(dwSize != pData->dwOleTypeNameLength)
6408 hRes = CONVERT10_E_OLESTREAM_PUT;
6413 if(hRes == S_OK)
6415 /* Set the width of the Metafile */
6416 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6417 if(dwSize != sizeof(pData->dwMetaFileWidth))
6419 hRes = CONVERT10_E_OLESTREAM_PUT;
6423 if(hRes == S_OK)
6425 /* Set the height of the Metafile */
6426 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6427 if(dwSize != sizeof(pData->dwMetaFileHeight))
6429 hRes = CONVERT10_E_OLESTREAM_PUT;
6433 if(hRes == S_OK)
6435 /* Set the length of the Data */
6436 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6437 if(dwSize != sizeof(pData->dwDataLength))
6439 hRes = CONVERT10_E_OLESTREAM_PUT;
6443 if(hRes == S_OK)
6445 if(pData->dwDataLength > 0)
6447 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6448 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6449 if(dwSize != pData->dwDataLength)
6451 hRes = CONVERT10_E_OLESTREAM_PUT;
6456 return hRes;
6459 /*************************************************************************
6460 * OLECONVERT_GetOLE20FromOLE10[Internal]
6462 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6463 * opens it, and copies the content to the dest IStorage for
6464 * OleConvertOLESTREAMToIStorage
6467 * PARAMS
6468 * pDestStorage [I] The IStorage to copy the data to
6469 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6470 * nBufferLength [I] The size of the buffer
6472 * RETURNS
6473 * Nothing
6475 * NOTES
6479 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6481 HRESULT hRes;
6482 HANDLE hFile;
6483 IStorage *pTempStorage;
6484 DWORD dwNumOfBytesWritten;
6485 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6486 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6488 /* Create a temp File */
6489 GetTempPathW(MAX_PATH, wstrTempDir);
6490 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6491 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6493 if(hFile != INVALID_HANDLE_VALUE)
6495 /* Write IStorage Data to File */
6496 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6497 CloseHandle(hFile);
6499 /* Open and copy temp storage to the Dest Storage */
6500 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6501 if(hRes == S_OK)
6503 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6504 StorageBaseImpl_Release(pTempStorage);
6506 DeleteFileW(wstrTempFile);
6511 /*************************************************************************
6512 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6514 * Saves the OLE10 STREAM From memory
6516 * PARAMS
6517 * pStorage [I] The Src IStorage to copy
6518 * pData [I] The Dest Memory to write to.
6520 * RETURNS
6521 * The size in bytes allocated for pData
6523 * NOTES
6524 * Memory allocated for pData must be freed by the caller
6526 * Used by OleConvertIStorageToOLESTREAM only.
6529 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6531 HANDLE hFile;
6532 HRESULT hRes;
6533 DWORD nDataLength = 0;
6534 IStorage *pTempStorage;
6535 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6536 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6538 *pData = NULL;
6540 /* Create temp Storage */
6541 GetTempPathW(MAX_PATH, wstrTempDir);
6542 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6543 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6545 if(hRes == S_OK)
6547 /* Copy Src Storage to the Temp Storage */
6548 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6549 StorageBaseImpl_Release(pTempStorage);
6551 /* Open Temp Storage as a file and copy to memory */
6552 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6553 if(hFile != INVALID_HANDLE_VALUE)
6555 nDataLength = GetFileSize(hFile, NULL);
6556 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6557 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6558 CloseHandle(hFile);
6560 DeleteFileW(wstrTempFile);
6562 return nDataLength;
6565 /*************************************************************************
6566 * OLECONVERT_CreateOleStream [Internal]
6568 * Creates the "\001OLE" stream in the IStorage if necessary.
6570 * PARAMS
6571 * pStorage [I] Dest storage to create the stream in
6573 * RETURNS
6574 * Nothing
6576 * NOTES
6577 * This function is used by OleConvertOLESTREAMToIStorage only.
6579 * This stream is still unknown, MS Word seems to have extra data
6580 * but since the data is stored in the OLESTREAM there should be
6581 * no need to recreate the stream. If the stream is manually
6582 * deleted it will create it with this default data.
6585 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6587 HRESULT hRes;
6588 IStream *pStream;
6589 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6590 BYTE pOleStreamHeader [] =
6592 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6593 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6594 0x00, 0x00, 0x00, 0x00
6597 /* Create stream if not present */
6598 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6599 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6601 if(hRes == S_OK)
6603 /* Write default Data */
6604 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6605 IStream_Release(pStream);
6609 /* write a string to a stream, preceded by its length */
6610 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6612 HRESULT r;
6613 LPSTR str;
6614 DWORD len = 0;
6616 if( string )
6617 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6618 r = IStream_Write( stm, &len, sizeof(len), NULL);
6619 if( FAILED( r ) )
6620 return r;
6621 if(len == 0)
6622 return r;
6623 str = CoTaskMemAlloc( len );
6624 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6625 r = IStream_Write( stm, str, len, NULL);
6626 CoTaskMemFree( str );
6627 return r;
6630 /* read a string preceded by its length from a stream */
6631 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6633 HRESULT r;
6634 DWORD len, count = 0;
6635 LPSTR str;
6636 LPWSTR wstr;
6638 r = IStream_Read( stm, &len, sizeof(len), &count );
6639 if( FAILED( r ) )
6640 return r;
6641 if( count != sizeof(len) )
6642 return E_OUTOFMEMORY;
6644 TRACE("%ld bytes\n",len);
6646 str = CoTaskMemAlloc( len );
6647 if( !str )
6648 return E_OUTOFMEMORY;
6649 count = 0;
6650 r = IStream_Read( stm, str, len, &count );
6651 if( FAILED( r ) )
6652 return r;
6653 if( count != len )
6655 CoTaskMemFree( str );
6656 return E_OUTOFMEMORY;
6659 TRACE("Read string %s\n",debugstr_an(str,len));
6661 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6662 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6663 if( wstr )
6664 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6665 CoTaskMemFree( str );
6667 *string = wstr;
6669 return r;
6673 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6674 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6676 IStream *pstm;
6677 HRESULT r = S_OK;
6678 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6680 static const BYTE unknown1[12] =
6681 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6682 0xFF, 0xFF, 0xFF, 0xFF};
6683 static const BYTE unknown2[16] =
6684 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6685 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6687 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6688 debugstr_w(lpszUserType), debugstr_w(szClipName),
6689 debugstr_w(szProgIDName));
6691 /* Create a CompObj stream if it doesn't exist */
6692 r = IStorage_CreateStream(pstg, szwStreamName,
6693 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6694 if( FAILED (r) )
6695 return r;
6697 /* Write CompObj Structure to stream */
6698 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6700 if( SUCCEEDED( r ) )
6701 r = WriteClassStm( pstm, clsid );
6703 if( SUCCEEDED( r ) )
6704 r = STREAM_WriteString( pstm, lpszUserType );
6705 if( SUCCEEDED( r ) )
6706 r = STREAM_WriteString( pstm, szClipName );
6707 if( SUCCEEDED( r ) )
6708 r = STREAM_WriteString( pstm, szProgIDName );
6709 if( SUCCEEDED( r ) )
6710 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6712 IStream_Release( pstm );
6714 return r;
6717 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6718 static HRESULT CLSIDFromUserType(LPCWSTR lpszUserType, CLSID *clsid)
6720 LONG r, i, len;
6721 ULONG count;
6722 WCHAR szKey[0x40];
6723 HKEY hkey, hkeyclsid;
6724 LPWSTR buffer = NULL;
6725 BOOL found = FALSE;
6726 static const WCHAR szclsid[] = { 'C','L','S','I','D',0 };
6728 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType));
6730 r = RegOpenKeyW( HKEY_CLASSES_ROOT, szclsid, &hkeyclsid );
6731 if( r )
6732 return E_INVALIDARG;
6734 len = lstrlenW( lpszUserType ) + 1;
6735 buffer = CoTaskMemAlloc( len * sizeof (WCHAR) );
6736 if( !buffer )
6737 goto end;
6739 for(i=0; !found; i++ )
6741 r = RegEnumKeyW( hkeyclsid, i, szKey, sizeof(szKey)/sizeof(WCHAR));
6742 if( r != ERROR_SUCCESS )
6743 break;
6744 hkey = 0;
6745 r = RegOpenKeyW( hkeyclsid, szKey, &hkey );
6746 if( r != ERROR_SUCCESS )
6747 break;
6748 count = len * sizeof (WCHAR);
6749 r = RegQueryValueW( hkey, NULL, buffer, &count );
6750 found = ( r == ERROR_SUCCESS ) &&
6751 ( count == len*sizeof(WCHAR) ) &&
6752 !lstrcmpW( buffer, lpszUserType ) ;
6753 RegCloseKey( hkey );
6756 end:
6757 if( buffer )
6758 CoTaskMemFree( buffer );
6759 RegCloseKey( hkeyclsid );
6761 if ( !found )
6762 return E_INVALIDARG;
6764 TRACE("clsid is %s\n", debugstr_w( szKey ) );
6766 r = CLSIDFromString( szKey, clsid );
6768 return r;
6772 /***********************************************************************
6773 * WriteFmtUserTypeStg (OLE32.@)
6775 HRESULT WINAPI WriteFmtUserTypeStg(
6776 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6778 HRESULT r;
6779 WCHAR szwClipName[0x40];
6780 WCHAR szCLSIDName[OLESTREAM_MAX_STR_LEN];
6781 CLSID clsid;
6782 LPWSTR wstrProgID;
6783 DWORD n;
6784 LPMALLOC allocator = NULL;
6786 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6788 r = CoGetMalloc(0, &allocator);
6789 if( FAILED( r) )
6790 return E_OUTOFMEMORY;
6792 /* get the clipboard format name */
6793 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6794 szwClipName[n]=0;
6796 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6798 /* Get the CLSID */
6799 szCLSIDName[0]=0;
6800 r = CLSIDFromUserType(lpszUserType, &clsid);
6801 if( FAILED( r ) )
6802 return r;
6804 TRACE("CLSID is %s\n",debugstr_guid(&clsid));
6806 /* get the real program ID */
6807 r = ProgIDFromCLSID( &clsid, &wstrProgID);
6808 if( FAILED( r ) )
6809 return r;
6811 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6813 /* if we have a good string, write the stream */
6814 if( wstrProgID )
6815 r = STORAGE_WriteCompObj( pstg, &clsid,
6816 lpszUserType, szwClipName, wstrProgID );
6817 else
6818 r = E_OUTOFMEMORY;
6820 IMalloc_Free( allocator, wstrProgID);
6822 return r;
6826 /******************************************************************************
6827 * ReadFmtUserTypeStg [OLE32.@]
6829 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6831 HRESULT r;
6832 IStream *stm = 0;
6833 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6834 unsigned char unknown1[12];
6835 unsigned char unknown2[16];
6836 DWORD count;
6837 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6838 CLSID clsid;
6840 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6842 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6843 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6844 if( FAILED ( r ) )
6846 ERR("Failed to open stream\n");
6847 return r;
6850 /* read the various parts of the structure */
6851 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6852 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6853 goto end;
6854 r = ReadClassStm( stm, &clsid );
6855 if( FAILED( r ) )
6856 goto end;
6858 r = STREAM_ReadString( stm, &szCLSIDName );
6859 if( FAILED( r ) )
6860 goto end;
6862 r = STREAM_ReadString( stm, &szOleTypeName );
6863 if( FAILED( r ) )
6864 goto end;
6866 r = STREAM_ReadString( stm, &szProgIDName );
6867 if( FAILED( r ) )
6868 goto end;
6870 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6871 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6872 goto end;
6874 /* ok, success... now we just need to store what we found */
6875 if( pcf )
6876 *pcf = RegisterClipboardFormatW( szOleTypeName );
6877 CoTaskMemFree( szOleTypeName );
6879 if( lplpszUserType )
6880 *lplpszUserType = szCLSIDName;
6881 CoTaskMemFree( szProgIDName );
6883 end:
6884 IStream_Release( stm );
6886 return r;
6890 /*************************************************************************
6891 * OLECONVERT_CreateCompObjStream [Internal]
6893 * Creates a "\001CompObj" is the destination IStorage if necessary.
6895 * PARAMS
6896 * pStorage [I] The dest IStorage to create the CompObj Stream
6897 * if necessary.
6898 * strOleTypeName [I] The ProgID
6900 * RETURNS
6901 * Success: S_OK
6902 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6904 * NOTES
6905 * This function is used by OleConvertOLESTREAMToIStorage only.
6907 * The stream data is stored in the OLESTREAM and there should be
6908 * no need to recreate the stream. If the stream is manually
6909 * deleted it will attempt to create it by querying the registry.
6913 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6915 IStream *pStream;
6916 HRESULT hStorageRes, hRes = S_OK;
6917 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6918 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6919 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
6921 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6922 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6924 /* Initialize the CompObj structure */
6925 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6926 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6927 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6930 /* Create a CompObj stream if it doesn't exist */
6931 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6932 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6933 if(hStorageRes == S_OK)
6935 /* copy the OleTypeName to the compobj struct */
6936 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6937 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6939 /* copy the OleTypeName to the compobj struct */
6940 /* Note: in the test made, these were Identical */
6941 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6942 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6944 /* Get the CLSID */
6945 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
6946 bufferW, OLESTREAM_MAX_STR_LEN );
6947 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
6949 if(hRes == S_OK)
6951 HKEY hKey;
6952 LONG hErr;
6953 /* Get the CLSID Default Name from the Registry */
6954 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6955 if(hErr == ERROR_SUCCESS)
6957 char strTemp[OLESTREAM_MAX_STR_LEN];
6958 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6959 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6960 if(hErr == ERROR_SUCCESS)
6962 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6964 RegCloseKey(hKey);
6968 /* Write CompObj Structure to stream */
6969 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6971 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6973 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6974 if(IStorageCompObj.dwCLSIDNameLength > 0)
6976 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6978 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6979 if(IStorageCompObj.dwOleTypeNameLength > 0)
6981 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6983 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6984 if(IStorageCompObj.dwProgIDNameLength > 0)
6986 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6988 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6989 IStream_Release(pStream);
6991 return hRes;
6995 /*************************************************************************
6996 * OLECONVERT_CreateOlePresStream[Internal]
6998 * Creates the "\002OlePres000" Stream with the Metafile data
7000 * PARAMS
7001 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7002 * dwExtentX [I] Width of the Metafile
7003 * dwExtentY [I] Height of the Metafile
7004 * pData [I] Metafile data
7005 * dwDataLength [I] Size of the Metafile data
7007 * RETURNS
7008 * Success: S_OK
7009 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7011 * NOTES
7012 * This function is used by OleConvertOLESTREAMToIStorage only.
7015 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7017 HRESULT hRes;
7018 IStream *pStream;
7019 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7020 BYTE pOlePresStreamHeader [] =
7022 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7023 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7024 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7025 0x00, 0x00, 0x00, 0x00
7028 BYTE pOlePresStreamHeaderEmpty [] =
7030 0x00, 0x00, 0x00, 0x00,
7031 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7032 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7033 0x00, 0x00, 0x00, 0x00
7036 /* Create the OlePres000 Stream */
7037 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7038 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7040 if(hRes == S_OK)
7042 DWORD nHeaderSize;
7043 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7045 memset(&OlePres, 0, sizeof(OlePres));
7046 /* Do we have any metafile data to save */
7047 if(dwDataLength > 0)
7049 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7050 nHeaderSize = sizeof(pOlePresStreamHeader);
7052 else
7054 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7055 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7057 /* Set width and height of the metafile */
7058 OlePres.dwExtentX = dwExtentX;
7059 OlePres.dwExtentY = -dwExtentY;
7061 /* Set Data and Length */
7062 if(dwDataLength > sizeof(METAFILEPICT16))
7064 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7065 OlePres.pData = &(pData[8]);
7067 /* Save OlePres000 Data to Stream */
7068 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7069 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7070 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7071 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7072 if(OlePres.dwSize > 0)
7074 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7076 IStream_Release(pStream);
7080 /*************************************************************************
7081 * OLECONVERT_CreateOle10NativeStream [Internal]
7083 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7085 * PARAMS
7086 * pStorage [I] Dest storage to create the stream in
7087 * pData [I] Ole10 Native Data (ex. bmp)
7088 * dwDataLength [I] Size of the Ole10 Native Data
7090 * RETURNS
7091 * Nothing
7093 * NOTES
7094 * This function is used by OleConvertOLESTREAMToIStorage only.
7096 * Might need to verify the data and return appropriate error message
7099 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7101 HRESULT hRes;
7102 IStream *pStream;
7103 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7105 /* Create the Ole10Native Stream */
7106 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7107 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7109 if(hRes == S_OK)
7111 /* Write info to stream */
7112 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7113 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7114 IStream_Release(pStream);
7119 /*************************************************************************
7120 * OLECONVERT_GetOLE10ProgID [Internal]
7122 * Finds the ProgID (or OleTypeID) from the IStorage
7124 * PARAMS
7125 * pStorage [I] The Src IStorage to get the ProgID
7126 * strProgID [I] the ProgID string to get
7127 * dwSize [I] the size of the string
7129 * RETURNS
7130 * Success: S_OK
7131 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7133 * NOTES
7134 * This function is used by OleConvertIStorageToOLESTREAM only.
7138 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7140 HRESULT hRes;
7141 IStream *pStream;
7142 LARGE_INTEGER iSeekPos;
7143 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7144 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7146 /* Open the CompObj Stream */
7147 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7148 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7149 if(hRes == S_OK)
7152 /*Get the OleType from the CompObj Stream */
7153 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7154 iSeekPos.u.HighPart = 0;
7156 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7157 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7158 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7159 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7160 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7161 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7162 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7164 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7165 if(*dwSize > 0)
7167 IStream_Read(pStream, strProgID, *dwSize, NULL);
7169 IStream_Release(pStream);
7171 else
7173 STATSTG stat;
7174 LPOLESTR wstrProgID;
7176 /* Get the OleType from the registry */
7177 REFCLSID clsid = &(stat.clsid);
7178 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7179 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7180 if(hRes == S_OK)
7182 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7186 return hRes;
7189 /*************************************************************************
7190 * OLECONVERT_GetOle10PresData [Internal]
7192 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7194 * PARAMS
7195 * pStorage [I] Src IStroage
7196 * pOleStream [I] Dest OleStream Mem Struct
7198 * RETURNS
7199 * Nothing
7201 * NOTES
7202 * This function is used by OleConvertIStorageToOLESTREAM only.
7204 * Memory allocated for pData must be freed by the caller
7208 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7211 HRESULT hRes;
7212 IStream *pStream;
7213 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7215 /* Initialize Default data for OLESTREAM */
7216 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7217 pOleStreamData[0].dwTypeID = 2;
7218 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7219 pOleStreamData[1].dwTypeID = 0;
7220 pOleStreamData[0].dwMetaFileWidth = 0;
7221 pOleStreamData[0].dwMetaFileHeight = 0;
7222 pOleStreamData[0].pData = NULL;
7223 pOleStreamData[1].pData = NULL;
7225 /* Open Ole10Native Stream */
7226 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7227 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7228 if(hRes == S_OK)
7231 /* Read Size and Data */
7232 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7233 if(pOleStreamData->dwDataLength > 0)
7235 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7236 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7238 IStream_Release(pStream);
7244 /*************************************************************************
7245 * OLECONVERT_GetOle20PresData[Internal]
7247 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7249 * PARAMS
7250 * pStorage [I] Src IStroage
7251 * pOleStreamData [I] Dest OleStream Mem Struct
7253 * RETURNS
7254 * Nothing
7256 * NOTES
7257 * This function is used by OleConvertIStorageToOLESTREAM only.
7259 * Memory allocated for pData must be freed by the caller
7261 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7263 HRESULT hRes;
7264 IStream *pStream;
7265 OLECONVERT_ISTORAGE_OLEPRES olePress;
7266 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7268 /* Initialize Default data for OLESTREAM */
7269 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7270 pOleStreamData[0].dwTypeID = 2;
7271 pOleStreamData[0].dwMetaFileWidth = 0;
7272 pOleStreamData[0].dwMetaFileHeight = 0;
7273 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7274 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7275 pOleStreamData[1].dwTypeID = 0;
7276 pOleStreamData[1].dwOleTypeNameLength = 0;
7277 pOleStreamData[1].strOleTypeName[0] = 0;
7278 pOleStreamData[1].dwMetaFileWidth = 0;
7279 pOleStreamData[1].dwMetaFileHeight = 0;
7280 pOleStreamData[1].pData = NULL;
7281 pOleStreamData[1].dwDataLength = 0;
7284 /* Open OlePress000 stream */
7285 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7286 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7287 if(hRes == S_OK)
7289 LARGE_INTEGER iSeekPos;
7290 METAFILEPICT16 MetaFilePict;
7291 static const char strMetafilePictName[] = "METAFILEPICT";
7293 /* Set the TypeID for a Metafile */
7294 pOleStreamData[1].dwTypeID = 5;
7296 /* Set the OleTypeName to Metafile */
7297 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7298 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7300 iSeekPos.u.HighPart = 0;
7301 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7303 /* Get Presentation Data */
7304 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7305 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7306 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7307 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7309 /*Set width and Height */
7310 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7311 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7312 if(olePress.dwSize > 0)
7314 /* Set Length */
7315 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7317 /* Set MetaFilePict struct */
7318 MetaFilePict.mm = 8;
7319 MetaFilePict.xExt = olePress.dwExtentX;
7320 MetaFilePict.yExt = olePress.dwExtentY;
7321 MetaFilePict.hMF = 0;
7323 /* Get Metafile Data */
7324 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7325 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7326 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7328 IStream_Release(pStream);
7332 /*************************************************************************
7333 * OleConvertOLESTREAMToIStorage [OLE32.@]
7335 * Read info on MSDN
7337 * TODO
7338 * DVTARGETDEVICE paramenter is not handled
7339 * Still unsure of some mem fields for OLE 10 Stream
7340 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7341 * and "\001OLE" streams
7344 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7345 LPOLESTREAM pOleStream,
7346 LPSTORAGE pstg,
7347 const DVTARGETDEVICE* ptd)
7349 int i;
7350 HRESULT hRes=S_OK;
7351 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7353 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7355 if(ptd != NULL)
7357 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7360 if(pstg == NULL || pOleStream == NULL)
7362 hRes = E_INVALIDARG;
7365 if(hRes == S_OK)
7367 /* Load the OLESTREAM to Memory */
7368 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7371 if(hRes == S_OK)
7373 /* Load the OLESTREAM to Memory (part 2)*/
7374 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7377 if(hRes == S_OK)
7380 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7382 /* Do we have the IStorage Data in the OLESTREAM */
7383 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7385 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7386 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7388 else
7390 /* It must be an original OLE 1.0 source */
7391 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7394 else
7396 /* It must be an original OLE 1.0 source */
7397 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7400 /* Create CompObj Stream if necessary */
7401 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7402 if(hRes == S_OK)
7404 /*Create the Ole Stream if necessary */
7405 OLECONVERT_CreateOleStream(pstg);
7410 /* Free allocated memory */
7411 for(i=0; i < 2; i++)
7413 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7414 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7415 pOleStreamData[i].pstrOleObjFileName = NULL;
7417 return hRes;
7420 /*************************************************************************
7421 * OleConvertIStorageToOLESTREAM [OLE32.@]
7423 * Read info on MSDN
7425 * Read info on MSDN
7427 * TODO
7428 * Still unsure of some mem fields for OLE 10 Stream
7429 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7430 * and "\001OLE" streams.
7433 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7434 LPSTORAGE pstg,
7435 LPOLESTREAM pOleStream)
7437 int i;
7438 HRESULT hRes = S_OK;
7439 IStream *pStream;
7440 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7441 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7444 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7446 if(pstg == NULL || pOleStream == NULL)
7448 hRes = E_INVALIDARG;
7450 if(hRes == S_OK)
7452 /* Get the ProgID */
7453 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7454 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7456 if(hRes == S_OK)
7458 /* Was it originally Ole10 */
7459 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7460 if(hRes == S_OK)
7462 IStream_Release(pStream);
7463 /* Get Presentation Data for Ole10Native */
7464 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7466 else
7468 /* Get Presentation Data (OLE20) */
7469 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7472 /* Save OLESTREAM */
7473 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7474 if(hRes == S_OK)
7476 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7481 /* Free allocated memory */
7482 for(i=0; i < 2; i++)
7484 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7487 return hRes;
7490 /***********************************************************************
7491 * GetConvertStg (OLE32.@)
7493 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7494 FIXME("unimplemented stub!\n");
7495 return E_FAIL;
7498 /******************************************************************************
7499 * StgIsStorageFile [OLE32.@]
7501 HRESULT WINAPI
7502 StgIsStorageFile(LPCOLESTR fn)
7504 HANDLE hf;
7505 BYTE magic[8];
7506 DWORD bytes_read;
7508 TRACE("(\'%s\')\n", debugstr_w(fn));
7509 hf = CreateFileW(fn, GENERIC_READ,
7510 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7511 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7513 if (hf == INVALID_HANDLE_VALUE)
7514 return STG_E_FILENOTFOUND;
7516 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7518 WARN(" unable to read file\n");
7519 CloseHandle(hf);
7520 return S_FALSE;
7523 CloseHandle(hf);
7525 if (bytes_read != 8) {
7526 WARN(" too short\n");
7527 return S_FALSE;
7530 if (!memcmp(magic,STORAGE_magic,8)) {
7531 WARN(" -> YES\n");
7532 return S_OK;
7535 WARN(" -> Invalid header.\n");
7536 return S_FALSE;