Don't test i386 types on non i386.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blobc182b8aae06b6c5178b2fff6f6bb0b61fcdf9337
1 /*
2 * Compound Storage (32 bit version)
3 * Storage implementation
5 * This file contains the compound file implementation
6 * of the storage interface.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Sylvain St-Germain
10 * Copyright 1999 Thuy Nguyen
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <assert.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winnls.h"
38 #include "winuser.h"
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
42 #include "storage32.h"
43 #include "ole2.h" /* For Write/ReadClassStm */
45 #include "winreg.h"
46 #include "wine/wingdi16.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(storage);
50 #define FILE_BEGIN 0
53 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
54 #define OLESTREAM_ID 0x501
55 #define OLESTREAM_MAX_STR_LEN 255
57 static const char rootPropertyName[] = "Root Entry";
60 /* OLESTREAM memory structure to use for Get and Put Routines */
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 typedef struct
64 DWORD dwOleID;
65 DWORD dwTypeID;
66 DWORD dwOleTypeNameLength;
67 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
68 CHAR *pstrOleObjFileName;
69 DWORD dwOleObjFileNameLength;
70 DWORD dwMetaFileWidth;
71 DWORD dwMetaFileHeight;
72 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
73 DWORD dwDataLength;
74 BYTE *pData;
75 }OLECONVERT_OLESTREAM_DATA;
77 /* CompObj Stream structure */
78 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
79 typedef struct
81 BYTE byUnknown1[12];
82 CLSID clsid;
83 DWORD dwCLSIDNameLength;
84 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
85 DWORD dwOleTypeNameLength;
86 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
87 DWORD dwProgIDNameLength;
88 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
89 BYTE byUnknown2[16];
90 }OLECONVERT_ISTORAGE_COMPOBJ;
93 /* Ole Presention Stream structure */
94 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
95 typedef struct
97 BYTE byUnknown1[28];
98 DWORD dwExtentX;
99 DWORD dwExtentY;
100 DWORD dwSize;
101 BYTE *pData;
102 }OLECONVERT_ISTORAGE_OLEPRES;
106 /***********************************************************************
107 * Forward declaration of internal functions used by the method DestroyElement
109 static HRESULT deleteStorageProperty(
110 StorageImpl *parentStorage,
111 ULONG foundPropertyIndexToDelete,
112 StgProperty propertyToDelete);
114 static HRESULT deleteStreamProperty(
115 StorageImpl *parentStorage,
116 ULONG foundPropertyIndexToDelete,
117 StgProperty propertyToDelete);
119 static HRESULT findPlaceholder(
120 StorageImpl *storage,
121 ULONG propertyIndexToStore,
122 ULONG storagePropertyIndex,
123 INT typeOfRelation);
125 static HRESULT adjustPropertyChain(
126 StorageImpl *This,
127 StgProperty propertyToDelete,
128 StgProperty parentProperty,
129 ULONG parentPropertyId,
130 INT typeOfRelation);
132 /***********************************************************************
133 * Declaration of the functions used to manipulate StgProperty
136 static ULONG getFreeProperty(
137 StorageImpl *storage);
139 static void updatePropertyChain(
140 StorageImpl *storage,
141 ULONG newPropertyIndex,
142 StgProperty newProperty);
144 static LONG propertyNameCmp(
145 OLECHAR *newProperty,
146 OLECHAR *currentProperty);
149 /***********************************************************************
150 * Declaration of miscellaneous functions...
152 static HRESULT validateSTGM(DWORD stgmValue);
154 static DWORD GetShareModeFromSTGM(DWORD stgm);
155 static DWORD GetAccessModeFromSTGM(DWORD stgm);
156 static DWORD GetCreationModeFromSTGM(DWORD stgm);
159 * Virtual function table for the IStorage32Impl class.
161 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
163 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
164 StorageBaseImpl_QueryInterface,
165 StorageBaseImpl_AddRef,
166 StorageBaseImpl_Release,
167 StorageBaseImpl_CreateStream,
168 StorageBaseImpl_OpenStream,
169 StorageImpl_CreateStorage,
170 StorageBaseImpl_OpenStorage,
171 StorageImpl_CopyTo,
172 StorageImpl_MoveElementTo,
173 StorageImpl_Commit,
174 StorageImpl_Revert,
175 StorageBaseImpl_EnumElements,
176 StorageImpl_DestroyElement,
177 StorageBaseImpl_RenameElement,
178 StorageImpl_SetElementTimes,
179 StorageBaseImpl_SetClass,
180 StorageImpl_SetStateBits,
181 StorageImpl_Stat
185 * Virtual function table for the Storage32InternalImpl class.
187 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
189 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
190 StorageBaseImpl_QueryInterface,
191 StorageBaseImpl_AddRef,
192 StorageBaseImpl_Release,
193 StorageBaseImpl_CreateStream,
194 StorageBaseImpl_OpenStream,
195 StorageImpl_CreateStorage,
196 StorageBaseImpl_OpenStorage,
197 StorageImpl_CopyTo,
198 StorageImpl_MoveElementTo,
199 StorageInternalImpl_Commit,
200 StorageInternalImpl_Revert,
201 StorageBaseImpl_EnumElements,
202 StorageImpl_DestroyElement,
203 StorageBaseImpl_RenameElement,
204 StorageImpl_SetElementTimes,
205 StorageBaseImpl_SetClass,
206 StorageImpl_SetStateBits,
207 StorageBaseImpl_Stat
211 * Virtual function table for the IEnumSTATSTGImpl class.
213 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
215 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
216 IEnumSTATSTGImpl_QueryInterface,
217 IEnumSTATSTGImpl_AddRef,
218 IEnumSTATSTGImpl_Release,
219 IEnumSTATSTGImpl_Next,
220 IEnumSTATSTGImpl_Skip,
221 IEnumSTATSTGImpl_Reset,
222 IEnumSTATSTGImpl_Clone
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 ICOM_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;
271 * Check that we obtained an interface.
273 if ((*ppvObject)==0)
274 return E_NOINTERFACE;
277 * Query Interface always increases the reference count by one when it is
278 * successful
280 StorageBaseImpl_AddRef(iface);
282 return S_OK;
285 /************************************************************************
286 * Storage32BaseImpl_AddRef (IUnknown)
288 * This method implements the common AddRef for all IStorage32
289 * implementations contained in this file.
291 * See Windows documentation for more details on IUnknown methods.
293 ULONG WINAPI StorageBaseImpl_AddRef(
294 IStorage* iface)
296 ICOM_THIS(StorageBaseImpl,iface);
297 This->ref++;
299 return This->ref;
302 /************************************************************************
303 * Storage32BaseImpl_Release (IUnknown)
305 * This method implements the common Release for all IStorage32
306 * implementations contained in this file.
308 * See Windows documentation for more details on IUnknown methods.
310 ULONG WINAPI StorageBaseImpl_Release(
311 IStorage* iface)
313 ICOM_THIS(StorageBaseImpl,iface);
315 * Decrease the reference count on this object.
317 This->ref--;
320 * If the reference count goes down to 0, perform suicide.
322 if (This->ref==0)
325 * Since we are using a system of base-classes, we want to call the
326 * destructor of the appropriate derived class. To do this, we are
327 * using virtual functions to implement the destructor.
329 This->v_destructor(This);
331 return 0;
334 return This->ref;
337 /************************************************************************
338 * Storage32BaseImpl_OpenStream (IStorage)
340 * This method will open the specified stream object from the current storage.
342 * See Windows documentation for more details on IStorage methods.
344 HRESULT WINAPI StorageBaseImpl_OpenStream(
345 IStorage* iface,
346 const OLECHAR* pwcsName, /* [string][in] */
347 void* reserved1, /* [unique][in] */
348 DWORD grfMode, /* [in] */
349 DWORD reserved2, /* [in] */
350 IStream** ppstm) /* [out] */
352 ICOM_THIS(StorageBaseImpl,iface);
353 IEnumSTATSTGImpl* propertyEnumeration;
354 StgStreamImpl* newStream;
355 StgProperty currentProperty;
356 ULONG foundPropertyIndex;
357 HRESULT res = STG_E_UNKNOWN;
359 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
360 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
363 * Perform a sanity check on the parameters.
365 if ( (pwcsName==NULL) || (ppstm==0) )
367 res = E_INVALIDARG;
368 goto end;
372 * Initialize the out parameter
374 *ppstm = NULL;
377 * Validate the STGM flags
379 if ( FAILED( validateSTGM(grfMode) ))
381 res = STG_E_INVALIDFLAG;
382 goto end;
386 * As documented.
388 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
389 (grfMode & STGM_DELETEONRELEASE) ||
390 (grfMode & STGM_TRANSACTED) )
392 res = STG_E_INVALIDFUNCTION;
393 goto end;
397 * Create a property enumeration to search the properties
399 propertyEnumeration = IEnumSTATSTGImpl_Construct(
400 This->ancestorStorage,
401 This->rootPropertySetIndex);
404 * Search the enumeration for the property with the given name
406 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
407 propertyEnumeration,
408 pwcsName,
409 &currentProperty);
412 * Delete the property enumeration since we don't need it anymore
414 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
417 * If it was found, construct the stream object and return a pointer to it.
419 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
420 (currentProperty.propertyType==PROPTYPE_STREAM) )
422 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
424 if (newStream!=0)
426 newStream->grfMode = grfMode;
427 *ppstm = (IStream*)newStream;
430 * Since we are returning a pointer to the interface, we have to
431 * nail down the reference.
433 StgStreamImpl_AddRef(*ppstm);
435 res = S_OK;
436 goto end;
439 res = E_OUTOFMEMORY;
440 goto end;
443 res = STG_E_FILENOTFOUND;
445 end:
446 if (res == S_OK)
447 TRACE("<-- IStream %p\n", *ppstm);
448 TRACE("<-- %08lx\n", res);
449 return res;
452 /************************************************************************
453 * Storage32BaseImpl_OpenStorage (IStorage)
455 * This method will open a new storage object from the current storage.
457 * See Windows documentation for more details on IStorage methods.
459 HRESULT WINAPI StorageBaseImpl_OpenStorage(
460 IStorage* iface,
461 const OLECHAR* pwcsName, /* [string][unique][in] */
462 IStorage* pstgPriority, /* [unique][in] */
463 DWORD grfMode, /* [in] */
464 SNB snbExclude, /* [unique][in] */
465 DWORD reserved, /* [in] */
466 IStorage** ppstg) /* [out] */
468 ICOM_THIS(StorageBaseImpl,iface);
469 StorageInternalImpl* newStorage;
470 IEnumSTATSTGImpl* propertyEnumeration;
471 StgProperty currentProperty;
472 ULONG foundPropertyIndex;
473 HRESULT res = STG_E_UNKNOWN;
475 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
476 iface, debugstr_w(pwcsName), pstgPriority,
477 grfMode, snbExclude, reserved, ppstg);
480 * Perform a sanity check on the parameters.
482 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
484 res = E_INVALIDARG;
485 goto end;
488 /* as documented */
489 if (snbExclude != NULL)
491 res = STG_E_INVALIDPARAMETER;
492 goto end;
496 * Validate the STGM flags
498 if ( FAILED( validateSTGM(grfMode) ))
500 res = STG_E_INVALIDFLAG;
501 goto end;
505 * As documented.
507 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
508 (grfMode & STGM_DELETEONRELEASE) ||
509 (grfMode & STGM_PRIORITY) )
511 res = STG_E_INVALIDFUNCTION;
512 goto end;
516 * Initialize the out parameter
518 *ppstg = NULL;
521 * Create a property enumeration to search the properties
523 propertyEnumeration = IEnumSTATSTGImpl_Construct(
524 This->ancestorStorage,
525 This->rootPropertySetIndex);
528 * Search the enumeration for the property with the given name
530 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
531 propertyEnumeration,
532 pwcsName,
533 &currentProperty);
536 * Delete the property enumeration since we don't need it anymore
538 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
541 * If it was found, construct the stream object and return a pointer to it.
543 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
544 (currentProperty.propertyType==PROPTYPE_STORAGE) )
547 * Construct a new Storage object
549 newStorage = StorageInternalImpl_Construct(
550 This->ancestorStorage,
551 foundPropertyIndex);
553 if (newStorage != 0)
555 *ppstg = (IStorage*)newStorage;
558 * Since we are returning a pointer to the interface,
559 * we have to nail down the reference.
561 StorageBaseImpl_AddRef(*ppstg);
563 res = S_OK;
564 goto end;
567 res = STG_E_INSUFFICIENTMEMORY;
568 goto end;
571 res = STG_E_FILENOTFOUND;
573 end:
574 TRACE("<-- %08lx\n", res);
575 return res;
578 /************************************************************************
579 * Storage32BaseImpl_EnumElements (IStorage)
581 * This method will create an enumerator object that can be used to
582 * retrieve informatino about all the properties in the storage object.
584 * See Windows documentation for more details on IStorage methods.
586 HRESULT WINAPI StorageBaseImpl_EnumElements(
587 IStorage* iface,
588 DWORD reserved1, /* [in] */
589 void* reserved2, /* [size_is][unique][in] */
590 DWORD reserved3, /* [in] */
591 IEnumSTATSTG** ppenum) /* [out] */
593 ICOM_THIS(StorageBaseImpl,iface);
594 IEnumSTATSTGImpl* newEnum;
596 TRACE("(%p, %ld, %p, %ld, %p)\n",
597 iface, reserved1, reserved2, reserved3, ppenum);
600 * Perform a sanity check on the parameters.
602 if ( (This==0) || (ppenum==0))
603 return E_INVALIDARG;
606 * Construct the enumerator.
608 newEnum = IEnumSTATSTGImpl_Construct(
609 This->ancestorStorage,
610 This->rootPropertySetIndex);
612 if (newEnum!=0)
614 *ppenum = (IEnumSTATSTG*)newEnum;
617 * Don't forget to nail down a reference to the new object before
618 * returning it.
620 IEnumSTATSTGImpl_AddRef(*ppenum);
622 return S_OK;
625 return E_OUTOFMEMORY;
628 /************************************************************************
629 * Storage32BaseImpl_Stat (IStorage)
631 * This method will retrieve information about this storage object.
633 * See Windows documentation for more details on IStorage methods.
635 HRESULT WINAPI StorageBaseImpl_Stat(
636 IStorage* iface,
637 STATSTG* pstatstg, /* [out] */
638 DWORD grfStatFlag) /* [in] */
640 ICOM_THIS(StorageBaseImpl,iface);
641 StgProperty curProperty;
642 BOOL readSuccessful;
643 HRESULT res = STG_E_UNKNOWN;
645 TRACE("(%p, %p, %lx)\n",
646 iface, pstatstg, grfStatFlag);
649 * Perform a sanity check on the parameters.
651 if ( (This==0) || (pstatstg==0))
653 res = E_INVALIDARG;
654 goto end;
658 * Read the information from the property.
660 readSuccessful = StorageImpl_ReadProperty(
661 This->ancestorStorage,
662 This->rootPropertySetIndex,
663 &curProperty);
665 if (readSuccessful)
667 StorageUtl_CopyPropertyToSTATSTG(
668 pstatstg,
669 &curProperty,
670 grfStatFlag);
672 res = S_OK;
673 goto end;
676 res = E_FAIL;
678 end:
679 if (res == S_OK)
681 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);
683 TRACE("<-- %08lx\n", res);
684 return res;
687 /************************************************************************
688 * Storage32BaseImpl_RenameElement (IStorage)
690 * This method will rename the specified element.
692 * See Windows documentation for more details on IStorage methods.
694 * Implementation notes: The method used to rename consists of creating a clone
695 * of the deleted StgProperty object setting it with the new name and to
696 * perform a DestroyElement of the old StgProperty.
698 HRESULT WINAPI StorageBaseImpl_RenameElement(
699 IStorage* iface,
700 const OLECHAR* pwcsOldName, /* [in] */
701 const OLECHAR* pwcsNewName) /* [in] */
703 ICOM_THIS(StorageBaseImpl,iface);
704 IEnumSTATSTGImpl* propertyEnumeration;
705 StgProperty currentProperty;
706 ULONG foundPropertyIndex;
708 TRACE("(%p, %s, %s)\n",
709 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
712 * Create a property enumeration to search the properties
714 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
715 This->rootPropertySetIndex);
718 * Search the enumeration for the new property name
720 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
721 pwcsNewName,
722 &currentProperty);
724 if (foundPropertyIndex != PROPERTY_NULL)
727 * There is already a property with the new name
729 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
730 return STG_E_FILEALREADYEXISTS;
733 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
736 * Search the enumeration for the old property name
738 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
739 pwcsOldName,
740 &currentProperty);
743 * Delete the property enumeration since we don't need it anymore
745 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
747 if (foundPropertyIndex != PROPERTY_NULL)
749 StgProperty renamedProperty;
750 ULONG renamedPropertyIndex;
753 * Setup a new property for the renamed property
755 renamedProperty.sizeOfNameString =
756 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
758 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
759 return STG_E_INVALIDNAME;
761 strcpyW(renamedProperty.name, pwcsNewName);
763 renamedProperty.propertyType = currentProperty.propertyType;
764 renamedProperty.startingBlock = currentProperty.startingBlock;
765 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
766 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
768 renamedProperty.previousProperty = PROPERTY_NULL;
769 renamedProperty.nextProperty = PROPERTY_NULL;
772 * Bring the dirProperty link in case it is a storage and in which
773 * case the renamed storage elements don't require to be reorganized.
775 renamedProperty.dirProperty = currentProperty.dirProperty;
777 /* call CoFileTime to get the current time
778 renamedProperty.timeStampS1
779 renamedProperty.timeStampD1
780 renamedProperty.timeStampS2
781 renamedProperty.timeStampD2
782 renamedProperty.propertyUniqueID
786 * Obtain a free property in the property chain
788 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
791 * Save the new property into the new property spot
793 StorageImpl_WriteProperty(
794 This->ancestorStorage,
795 renamedPropertyIndex,
796 &renamedProperty);
799 * Find a spot in the property chain for our newly created property.
801 updatePropertyChain(
802 (StorageImpl*)This,
803 renamedPropertyIndex,
804 renamedProperty);
807 * At this point the renamed property has been inserted in the tree,
808 * now, before to Destroy the old property we must zeroed it's dirProperty
809 * otherwise the DestroyProperty below will zap it all and we do not want
810 * this to happen.
811 * Also, we fake that the old property is a storage so the DestroyProperty
812 * will not do a SetSize(0) on the stream data.
814 * This means that we need to tweek the StgProperty if it is a stream or a
815 * non empty storage.
817 StorageImpl_ReadProperty(This->ancestorStorage,
818 foundPropertyIndex,
819 &currentProperty);
821 currentProperty.dirProperty = PROPERTY_NULL;
822 currentProperty.propertyType = PROPTYPE_STORAGE;
823 StorageImpl_WriteProperty(
824 This->ancestorStorage,
825 foundPropertyIndex,
826 &currentProperty);
829 * Invoke Destroy to get rid of the ole property and automatically redo
830 * the linking of it's previous and next members...
832 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
835 else
838 * There is no property with the old name
840 return STG_E_FILENOTFOUND;
843 return S_OK;
846 /************************************************************************
847 * Storage32BaseImpl_CreateStream (IStorage)
849 * This method will create a stream object within this storage
851 * See Windows documentation for more details on IStorage methods.
853 HRESULT WINAPI StorageBaseImpl_CreateStream(
854 IStorage* iface,
855 const OLECHAR* pwcsName, /* [string][in] */
856 DWORD grfMode, /* [in] */
857 DWORD reserved1, /* [in] */
858 DWORD reserved2, /* [in] */
859 IStream** ppstm) /* [out] */
861 ICOM_THIS(StorageBaseImpl,iface);
862 IEnumSTATSTGImpl* propertyEnumeration;
863 StgStreamImpl* newStream;
864 StgProperty currentProperty, newStreamProperty;
865 ULONG foundPropertyIndex, newPropertyIndex;
867 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
868 iface, debugstr_w(pwcsName), grfMode,
869 reserved1, reserved2, ppstm);
872 * Validate parameters
874 if (ppstm == 0)
875 return STG_E_INVALIDPOINTER;
877 if (pwcsName == 0)
878 return STG_E_INVALIDNAME;
881 * Validate the STGM flags
883 if ( FAILED( validateSTGM(grfMode) ))
884 return STG_E_INVALIDFLAG;
887 * As documented.
889 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
890 (grfMode & STGM_DELETEONRELEASE) ||
891 (grfMode & STGM_TRANSACTED) )
892 return STG_E_INVALIDFUNCTION;
895 * Initialize the out parameter
897 *ppstm = 0;
900 * Create a property enumeration to search the properties
902 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
903 This->rootPropertySetIndex);
905 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
906 pwcsName,
907 &currentProperty);
909 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
911 if (foundPropertyIndex != PROPERTY_NULL)
914 * An element with this name already exists
916 if (grfMode & STGM_CREATE)
918 IStorage_DestroyElement(iface, pwcsName);
920 else
921 return STG_E_FILEALREADYEXISTS;
925 * memset the empty property
927 memset(&newStreamProperty, 0, sizeof(StgProperty));
929 newStreamProperty.sizeOfNameString =
930 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
932 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
933 return STG_E_INVALIDNAME;
935 strcpyW(newStreamProperty.name, pwcsName);
937 newStreamProperty.propertyType = PROPTYPE_STREAM;
938 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
939 newStreamProperty.size.u.LowPart = 0;
940 newStreamProperty.size.u.HighPart = 0;
942 newStreamProperty.previousProperty = PROPERTY_NULL;
943 newStreamProperty.nextProperty = PROPERTY_NULL;
944 newStreamProperty.dirProperty = PROPERTY_NULL;
946 /* call CoFileTime to get the current time
947 newStreamProperty.timeStampS1
948 newStreamProperty.timeStampD1
949 newStreamProperty.timeStampS2
950 newStreamProperty.timeStampD2
953 /* newStreamProperty.propertyUniqueID */
956 * Get a free property or create a new one
958 newPropertyIndex = getFreeProperty(This->ancestorStorage);
961 * Save the new property into the new property spot
963 StorageImpl_WriteProperty(
964 This->ancestorStorage,
965 newPropertyIndex,
966 &newStreamProperty);
969 * Find a spot in the property chain for our newly created property.
971 updatePropertyChain(
972 (StorageImpl*)This,
973 newPropertyIndex,
974 newStreamProperty);
977 * Open the stream to return it.
979 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
981 if (newStream != 0)
983 *ppstm = (IStream*)newStream;
986 * Since we are returning a pointer to the interface, we have to nail down
987 * the reference.
989 StgStreamImpl_AddRef(*ppstm);
991 else
993 return STG_E_INSUFFICIENTMEMORY;
996 return S_OK;
999 /************************************************************************
1000 * Storage32BaseImpl_SetClass (IStorage)
1002 * This method will write the specified CLSID in the property of this
1003 * storage.
1005 * See Windows documentation for more details on IStorage methods.
1007 HRESULT WINAPI StorageBaseImpl_SetClass(
1008 IStorage* iface,
1009 REFCLSID clsid) /* [in] */
1011 ICOM_THIS(StorageBaseImpl,iface);
1012 HRESULT hRes = E_FAIL;
1013 StgProperty curProperty;
1014 BOOL success;
1016 TRACE("(%p, %p)\n", iface, clsid);
1018 success = StorageImpl_ReadProperty(This->ancestorStorage,
1019 This->rootPropertySetIndex,
1020 &curProperty);
1021 if (success)
1023 curProperty.propertyUniqueID = *clsid;
1025 success = StorageImpl_WriteProperty(This->ancestorStorage,
1026 This->rootPropertySetIndex,
1027 &curProperty);
1028 if (success)
1029 hRes = S_OK;
1032 return hRes;
1035 /************************************************************************
1036 ** Storage32Impl implementation
1039 /************************************************************************
1040 * Storage32Impl_CreateStorage (IStorage)
1042 * This method will create the storage object within the provided storage.
1044 * See Windows documentation for more details on IStorage methods.
1046 HRESULT WINAPI StorageImpl_CreateStorage(
1047 IStorage* iface,
1048 const OLECHAR *pwcsName, /* [string][in] */
1049 DWORD grfMode, /* [in] */
1050 DWORD reserved1, /* [in] */
1051 DWORD reserved2, /* [in] */
1052 IStorage **ppstg) /* [out] */
1054 StorageImpl* const This=(StorageImpl*)iface;
1056 IEnumSTATSTGImpl *propertyEnumeration;
1057 StgProperty currentProperty;
1058 StgProperty newProperty;
1059 ULONG foundPropertyIndex;
1060 ULONG newPropertyIndex;
1061 HRESULT hr;
1063 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1064 iface, debugstr_w(pwcsName), grfMode,
1065 reserved1, reserved2, ppstg);
1068 * Validate parameters
1070 if (ppstg == 0)
1071 return STG_E_INVALIDPOINTER;
1073 if (pwcsName == 0)
1074 return STG_E_INVALIDNAME;
1077 * Validate the STGM flags
1079 if ( FAILED( validateSTGM(grfMode) ) ||
1080 (grfMode & STGM_DELETEONRELEASE) )
1081 return STG_E_INVALIDFLAG;
1084 * Initialize the out parameter
1086 *ppstg = 0;
1089 * Create a property enumeration and search the properties
1091 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1092 This->rootPropertySetIndex);
1094 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1095 pwcsName,
1096 &currentProperty);
1097 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1099 if (foundPropertyIndex != PROPERTY_NULL)
1102 * An element with this name already exists
1104 if (grfMode & STGM_CREATE)
1105 IStorage_DestroyElement(iface, pwcsName);
1106 else
1107 return STG_E_FILEALREADYEXISTS;
1111 * memset the empty property
1113 memset(&newProperty, 0, sizeof(StgProperty));
1115 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1117 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1118 return STG_E_INVALIDNAME;
1120 strcpyW(newProperty.name, pwcsName);
1122 newProperty.propertyType = PROPTYPE_STORAGE;
1123 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1124 newProperty.size.u.LowPart = 0;
1125 newProperty.size.u.HighPart = 0;
1127 newProperty.previousProperty = PROPERTY_NULL;
1128 newProperty.nextProperty = PROPERTY_NULL;
1129 newProperty.dirProperty = PROPERTY_NULL;
1131 /* call CoFileTime to get the current time
1132 newProperty.timeStampS1
1133 newProperty.timeStampD1
1134 newProperty.timeStampS2
1135 newProperty.timeStampD2
1138 /* newStorageProperty.propertyUniqueID */
1141 * Obtain a free property in the property chain
1143 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1146 * Save the new property into the new property spot
1148 StorageImpl_WriteProperty(
1149 This->ancestorStorage,
1150 newPropertyIndex,
1151 &newProperty);
1154 * Find a spot in the property chain for our newly created property.
1156 updatePropertyChain(
1157 This,
1158 newPropertyIndex,
1159 newProperty);
1162 * Open it to get a pointer to return.
1164 hr = IStorage_OpenStorage(
1165 iface,
1166 (OLECHAR*)pwcsName,
1168 grfMode,
1171 ppstg);
1173 if( (hr != S_OK) || (*ppstg == NULL))
1175 return hr;
1179 return S_OK;
1183 /***************************************************************************
1185 * Internal Method
1187 * Get a free property or create a new one.
1189 static ULONG getFreeProperty(
1190 StorageImpl *storage)
1192 ULONG currentPropertyIndex = 0;
1193 ULONG newPropertyIndex = PROPERTY_NULL;
1194 BOOL readSuccessful = TRUE;
1195 StgProperty currentProperty;
1200 * Start by reading the root property
1202 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1203 currentPropertyIndex,
1204 &currentProperty);
1205 if (readSuccessful)
1207 if (currentProperty.sizeOfNameString == 0)
1210 * The property existis and is available, we found it.
1212 newPropertyIndex = currentPropertyIndex;
1215 else
1218 * We exhausted the property list, we will create more space below
1220 newPropertyIndex = currentPropertyIndex;
1222 currentPropertyIndex++;
1224 } while (newPropertyIndex == PROPERTY_NULL);
1227 * grow the property chain
1229 if (! readSuccessful)
1231 StgProperty emptyProperty;
1232 ULARGE_INTEGER newSize;
1233 ULONG propertyIndex;
1234 ULONG lastProperty = 0;
1235 ULONG blockCount = 0;
1238 * obtain the new count of property blocks
1240 blockCount = BlockChainStream_GetCount(
1241 storage->ancestorStorage->rootBlockChain)+1;
1244 * initialize the size used by the property stream
1246 newSize.u.HighPart = 0;
1247 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1250 * add a property block to the property chain
1252 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1255 * memset the empty property in order to initialize the unused newly
1256 * created property
1258 memset(&emptyProperty, 0, sizeof(StgProperty));
1261 * initialize them
1263 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1265 for(
1266 propertyIndex = newPropertyIndex;
1267 propertyIndex < lastProperty;
1268 propertyIndex++)
1270 StorageImpl_WriteProperty(
1271 storage->ancestorStorage,
1272 propertyIndex,
1273 &emptyProperty);
1277 return newPropertyIndex;
1280 /****************************************************************************
1282 * Internal Method
1284 * Case insensitive comparaison of StgProperty.name by first considering
1285 * their size.
1287 * Returns <0 when newPrpoerty < currentProperty
1288 * >0 when newPrpoerty > currentProperty
1289 * 0 when newPrpoerty == currentProperty
1291 static LONG propertyNameCmp(
1292 OLECHAR *newProperty,
1293 OLECHAR *currentProperty)
1295 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1297 if (diff == 0)
1300 * We compare the string themselves only when they are of the same length
1302 diff = lstrcmpiW( newProperty, currentProperty);
1305 return diff;
1308 /****************************************************************************
1310 * Internal Method
1312 * Properly link this new element in the property chain.
1314 static void updatePropertyChain(
1315 StorageImpl *storage,
1316 ULONG newPropertyIndex,
1317 StgProperty newProperty)
1319 StgProperty currentProperty;
1322 * Read the root property
1324 StorageImpl_ReadProperty(storage->ancestorStorage,
1325 storage->rootPropertySetIndex,
1326 &currentProperty);
1328 if (currentProperty.dirProperty != PROPERTY_NULL)
1331 * The root storage contains some element, therefore, start the research
1332 * for the appropriate location.
1334 BOOL found = 0;
1335 ULONG current, next, previous, currentPropertyId;
1338 * Keep the StgProperty sequence number of the storage first property
1340 currentPropertyId = currentProperty.dirProperty;
1343 * Read
1345 StorageImpl_ReadProperty(storage->ancestorStorage,
1346 currentProperty.dirProperty,
1347 &currentProperty);
1349 previous = currentProperty.previousProperty;
1350 next = currentProperty.nextProperty;
1351 current = currentPropertyId;
1353 while (found == 0)
1355 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1357 if (diff < 0)
1359 if (previous != PROPERTY_NULL)
1361 StorageImpl_ReadProperty(storage->ancestorStorage,
1362 previous,
1363 &currentProperty);
1364 current = previous;
1366 else
1368 currentProperty.previousProperty = newPropertyIndex;
1369 StorageImpl_WriteProperty(storage->ancestorStorage,
1370 current,
1371 &currentProperty);
1372 found = 1;
1375 else if (diff > 0)
1377 if (next != PROPERTY_NULL)
1379 StorageImpl_ReadProperty(storage->ancestorStorage,
1380 next,
1381 &currentProperty);
1382 current = next;
1384 else
1386 currentProperty.nextProperty = newPropertyIndex;
1387 StorageImpl_WriteProperty(storage->ancestorStorage,
1388 current,
1389 &currentProperty);
1390 found = 1;
1393 else
1396 * Trying to insert an item with the same name in the
1397 * subtree structure.
1399 assert(FALSE);
1402 previous = currentProperty.previousProperty;
1403 next = currentProperty.nextProperty;
1406 else
1409 * The root storage is empty, link the new property to it's dir property
1411 currentProperty.dirProperty = newPropertyIndex;
1412 StorageImpl_WriteProperty(storage->ancestorStorage,
1413 storage->rootPropertySetIndex,
1414 &currentProperty);
1419 /*************************************************************************
1420 * CopyTo (IStorage)
1422 HRESULT WINAPI StorageImpl_CopyTo(
1423 IStorage* iface,
1424 DWORD ciidExclude, /* [in] */
1425 const IID* rgiidExclude, /* [size_is][unique][in] */
1426 SNB snbExclude, /* [unique][in] */
1427 IStorage* pstgDest) /* [unique][in] */
1429 IEnumSTATSTG *elements = 0;
1430 STATSTG curElement, strStat;
1431 HRESULT hr;
1432 IStorage *pstgTmp, *pstgChild;
1433 IStream *pstrTmp, *pstrChild;
1435 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1436 FIXME("Exclude option not implemented\n");
1438 TRACE("(%p, %ld, %p, %p, %p)\n",
1439 iface, ciidExclude, rgiidExclude,
1440 snbExclude, pstgDest);
1443 * Perform a sanity check
1445 if ( pstgDest == 0 )
1446 return STG_E_INVALIDPOINTER;
1449 * Enumerate the elements
1451 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1453 if ( hr != S_OK )
1454 return hr;
1457 * set the class ID
1459 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1460 IStorage_SetClass( pstgDest, &curElement.clsid );
1465 * Obtain the next element
1467 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1469 if ( hr == S_FALSE )
1471 hr = S_OK; /* done, every element has been copied */
1472 break;
1475 if (curElement.type == STGTY_STORAGE)
1478 * open child source storage
1480 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1481 STGM_READ|STGM_SHARE_EXCLUSIVE,
1482 NULL, 0, &pstgChild );
1484 if (hr != S_OK)
1485 break;
1488 * Check if destination storage is not a child of the source
1489 * storage, which will cause an infinite loop
1491 if (pstgChild == pstgDest)
1493 IEnumSTATSTG_Release(elements);
1495 return STG_E_ACCESSDENIED;
1499 * create a new storage in destination storage
1501 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1502 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1503 0, 0,
1504 &pstgTmp );
1506 * if it already exist, don't create a new one use this one
1508 if (hr == STG_E_FILEALREADYEXISTS)
1510 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1511 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1512 NULL, 0, &pstgTmp );
1515 if (hr != S_OK)
1516 break;
1520 * do the copy recursively
1522 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1523 snbExclude, pstgTmp );
1525 IStorage_Release( pstgTmp );
1526 IStorage_Release( pstgChild );
1528 else if (curElement.type == STGTY_STREAM)
1531 * create a new stream in destination storage. If the stream already
1532 * exist, it will be deleted and a new one will be created.
1534 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1535 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1536 0, 0, &pstrTmp );
1538 if (hr != S_OK)
1539 break;
1542 * open child stream storage
1544 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1545 STGM_READ|STGM_SHARE_EXCLUSIVE,
1546 0, &pstrChild );
1548 if (hr != S_OK)
1549 break;
1552 * Get the size of the source stream
1554 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1557 * Set the size of the destination stream.
1559 IStream_SetSize(pstrTmp, strStat.cbSize);
1562 * do the copy
1564 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1565 NULL, NULL );
1567 IStream_Release( pstrTmp );
1568 IStream_Release( pstrChild );
1570 else
1572 WARN("unknown element type: %ld\n", curElement.type);
1575 } while (hr == S_OK);
1578 * Clean-up
1580 IEnumSTATSTG_Release(elements);
1582 return hr;
1585 /*************************************************************************
1586 * MoveElementTo (IStorage)
1588 HRESULT WINAPI StorageImpl_MoveElementTo(
1589 IStorage* iface,
1590 const OLECHAR *pwcsName, /* [string][in] */
1591 IStorage *pstgDest, /* [unique][in] */
1592 const OLECHAR *pwcsNewName,/* [string][in] */
1593 DWORD grfFlags) /* [in] */
1595 FIXME("not implemented!\n");
1596 return E_NOTIMPL;
1599 /*************************************************************************
1600 * Commit (IStorage)
1602 HRESULT WINAPI StorageImpl_Commit(
1603 IStorage* iface,
1604 DWORD grfCommitFlags)/* [in] */
1606 FIXME("(%ld): stub!\n", grfCommitFlags);
1607 return S_OK;
1610 /*************************************************************************
1611 * Revert (IStorage)
1613 HRESULT WINAPI StorageImpl_Revert(
1614 IStorage* iface)
1616 FIXME("not implemented!\n");
1617 return E_NOTIMPL;
1620 /*************************************************************************
1621 * DestroyElement (IStorage)
1623 * Stategy: This implementation is build this way for simplicity not for speed.
1624 * I always delete the top most element of the enumeration and adjust
1625 * the deleted element pointer all the time. This takes longer to
1626 * do but allow to reinvoke DestroyElement whenever we encounter a
1627 * storage object. The optimisation reside in the usage of another
1628 * enumeration stategy that would give all the leaves of a storage
1629 * first. (postfix order)
1631 HRESULT WINAPI StorageImpl_DestroyElement(
1632 IStorage* iface,
1633 const OLECHAR *pwcsName)/* [string][in] */
1635 StorageImpl* const This=(StorageImpl*)iface;
1637 IEnumSTATSTGImpl* propertyEnumeration;
1638 HRESULT hr = S_OK;
1639 BOOL res;
1640 StgProperty propertyToDelete;
1641 StgProperty parentProperty;
1642 ULONG foundPropertyIndexToDelete;
1643 ULONG typeOfRelation;
1644 ULONG parentPropertyId;
1646 TRACE("(%p, %s)\n",
1647 iface, debugstr_w(pwcsName));
1650 * Perform a sanity check on the parameters.
1652 if (pwcsName==NULL)
1653 return STG_E_INVALIDPOINTER;
1656 * Create a property enumeration to search the property with the given name
1658 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1659 This->ancestorStorage,
1660 This->rootPropertySetIndex);
1662 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1663 propertyEnumeration,
1664 pwcsName,
1665 &propertyToDelete);
1667 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1669 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1671 return STG_E_FILENOTFOUND;
1675 * Find the parent property of the property to delete (the one that
1676 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1677 * the parent is This. Otherwise, the parent is one of it's sibling...
1681 * First, read This's StgProperty..
1683 res = StorageImpl_ReadProperty(
1684 This->ancestorStorage,
1685 This->rootPropertySetIndex,
1686 &parentProperty);
1688 assert(res==TRUE);
1691 * Second, check to see if by any chance the actual storage (This) is not
1692 * the parent of the property to delete... We never know...
1694 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1697 * Set data as it would have been done in the else part...
1699 typeOfRelation = PROPERTY_RELATION_DIR;
1700 parentPropertyId = This->rootPropertySetIndex;
1702 else
1705 * Create a property enumeration to search the parent properties, and
1706 * delete it once done.
1708 IEnumSTATSTGImpl* propertyEnumeration2;
1710 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1711 This->ancestorStorage,
1712 This->rootPropertySetIndex);
1714 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1715 propertyEnumeration2,
1716 foundPropertyIndexToDelete,
1717 &parentProperty,
1718 &parentPropertyId);
1720 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1723 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1725 hr = deleteStorageProperty(
1726 This,
1727 foundPropertyIndexToDelete,
1728 propertyToDelete);
1730 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1732 hr = deleteStreamProperty(
1733 This,
1734 foundPropertyIndexToDelete,
1735 propertyToDelete);
1738 if (hr!=S_OK)
1739 return hr;
1742 * Adjust the property chain
1744 hr = adjustPropertyChain(
1745 This,
1746 propertyToDelete,
1747 parentProperty,
1748 parentPropertyId,
1749 typeOfRelation);
1751 return hr;
1755 /************************************************************************
1756 * StorageImpl_Stat (IStorage)
1758 * This method will retrieve information about this storage object.
1760 * See Windows documentation for more details on IStorage methods.
1762 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1763 STATSTG* pstatstg, /* [out] */
1764 DWORD grfStatFlag) /* [in] */
1766 StorageImpl* const This = (StorageImpl*)iface;
1767 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1769 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1771 CoTaskMemFree(pstatstg->pwcsName);
1772 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1773 strcpyW(pstatstg->pwcsName, This->pwcsName);
1776 return result;
1781 /*********************************************************************
1783 * Internal Method
1785 * Perform the deletion of a complete storage node
1788 static HRESULT deleteStorageProperty(
1789 StorageImpl *parentStorage,
1790 ULONG indexOfPropertyToDelete,
1791 StgProperty propertyToDelete)
1793 IEnumSTATSTG *elements = 0;
1794 IStorage *childStorage = 0;
1795 STATSTG currentElement;
1796 HRESULT hr;
1797 HRESULT destroyHr = S_OK;
1800 * Open the storage and enumerate it
1802 hr = StorageBaseImpl_OpenStorage(
1803 (IStorage*)parentStorage,
1804 propertyToDelete.name,
1806 STGM_SHARE_EXCLUSIVE,
1809 &childStorage);
1811 if (hr != S_OK)
1813 return hr;
1817 * Enumerate the elements
1819 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1824 * Obtain the next element
1826 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1827 if (hr==S_OK)
1829 destroyHr = StorageImpl_DestroyElement(
1830 (IStorage*)childStorage,
1831 (OLECHAR*)currentElement.pwcsName);
1833 CoTaskMemFree(currentElement.pwcsName);
1837 * We need to Reset the enumeration every time because we delete elements
1838 * and the enumeration could be invalid
1840 IEnumSTATSTG_Reset(elements);
1842 } while ((hr == S_OK) && (destroyHr == S_OK));
1845 * Invalidate the property by zeroing it's name member.
1847 propertyToDelete.sizeOfNameString = 0;
1849 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1850 indexOfPropertyToDelete,
1851 &propertyToDelete);
1853 IStorage_Release(childStorage);
1854 IEnumSTATSTG_Release(elements);
1856 return destroyHr;
1859 /*********************************************************************
1861 * Internal Method
1863 * Perform the deletion of a stream node
1866 static HRESULT deleteStreamProperty(
1867 StorageImpl *parentStorage,
1868 ULONG indexOfPropertyToDelete,
1869 StgProperty propertyToDelete)
1871 IStream *pis;
1872 HRESULT hr;
1873 ULARGE_INTEGER size;
1875 size.u.HighPart = 0;
1876 size.u.LowPart = 0;
1878 hr = StorageBaseImpl_OpenStream(
1879 (IStorage*)parentStorage,
1880 (OLECHAR*)propertyToDelete.name,
1881 NULL,
1882 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1884 &pis);
1886 if (hr!=S_OK)
1888 return(hr);
1892 * Zap the stream
1894 hr = IStream_SetSize(pis, size);
1896 if(hr != S_OK)
1898 return hr;
1902 * Release the stream object.
1904 IStream_Release(pis);
1907 * Invalidate the property by zeroing it's name member.
1909 propertyToDelete.sizeOfNameString = 0;
1912 * Here we should re-read the property so we get the updated pointer
1913 * but since we are here to zap it, I don't do it...
1915 StorageImpl_WriteProperty(
1916 parentStorage->ancestorStorage,
1917 indexOfPropertyToDelete,
1918 &propertyToDelete);
1920 return S_OK;
1923 /*********************************************************************
1925 * Internal Method
1927 * Finds a placeholder for the StgProperty within the Storage
1930 static HRESULT findPlaceholder(
1931 StorageImpl *storage,
1932 ULONG propertyIndexToStore,
1933 ULONG storePropertyIndex,
1934 INT typeOfRelation)
1936 StgProperty storeProperty;
1937 HRESULT hr = S_OK;
1938 BOOL res = TRUE;
1941 * Read the storage property
1943 res = StorageImpl_ReadProperty(
1944 storage->ancestorStorage,
1945 storePropertyIndex,
1946 &storeProperty);
1948 if(! res)
1950 return E_FAIL;
1953 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1955 if (storeProperty.previousProperty != PROPERTY_NULL)
1957 return findPlaceholder(
1958 storage,
1959 propertyIndexToStore,
1960 storeProperty.previousProperty,
1961 typeOfRelation);
1963 else
1965 storeProperty.previousProperty = propertyIndexToStore;
1968 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1970 if (storeProperty.nextProperty != PROPERTY_NULL)
1972 return findPlaceholder(
1973 storage,
1974 propertyIndexToStore,
1975 storeProperty.nextProperty,
1976 typeOfRelation);
1978 else
1980 storeProperty.nextProperty = propertyIndexToStore;
1983 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1985 if (storeProperty.dirProperty != PROPERTY_NULL)
1987 return findPlaceholder(
1988 storage,
1989 propertyIndexToStore,
1990 storeProperty.dirProperty,
1991 typeOfRelation);
1993 else
1995 storeProperty.dirProperty = propertyIndexToStore;
1999 hr = StorageImpl_WriteProperty(
2000 storage->ancestorStorage,
2001 storePropertyIndex,
2002 &storeProperty);
2004 if(! hr)
2006 return E_FAIL;
2009 return S_OK;
2012 /*************************************************************************
2014 * Internal Method
2016 * This method takes the previous and the next property link of a property
2017 * to be deleted and find them a place in the Storage.
2019 static HRESULT adjustPropertyChain(
2020 StorageImpl *This,
2021 StgProperty propertyToDelete,
2022 StgProperty parentProperty,
2023 ULONG parentPropertyId,
2024 INT typeOfRelation)
2026 ULONG newLinkProperty = PROPERTY_NULL;
2027 BOOL needToFindAPlaceholder = FALSE;
2028 ULONG storeNode = PROPERTY_NULL;
2029 ULONG toStoreNode = PROPERTY_NULL;
2030 INT relationType = 0;
2031 HRESULT hr = S_OK;
2032 BOOL res = TRUE;
2034 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2036 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2039 * Set the parent previous to the property to delete previous
2041 newLinkProperty = propertyToDelete.previousProperty;
2043 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2046 * We also need to find a storage for the other link, setup variables
2047 * to do this at the end...
2049 needToFindAPlaceholder = TRUE;
2050 storeNode = propertyToDelete.previousProperty;
2051 toStoreNode = propertyToDelete.nextProperty;
2052 relationType = PROPERTY_RELATION_NEXT;
2055 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2058 * Set the parent previous to the property to delete next
2060 newLinkProperty = propertyToDelete.nextProperty;
2064 * Link it for real...
2066 parentProperty.previousProperty = newLinkProperty;
2069 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2071 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2074 * Set the parent next to the property to delete next previous
2076 newLinkProperty = propertyToDelete.previousProperty;
2078 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2081 * We also need to find a storage for the other link, setup variables
2082 * to do this at the end...
2084 needToFindAPlaceholder = TRUE;
2085 storeNode = propertyToDelete.previousProperty;
2086 toStoreNode = propertyToDelete.nextProperty;
2087 relationType = PROPERTY_RELATION_NEXT;
2090 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2093 * Set the parent next to the property to delete next
2095 newLinkProperty = propertyToDelete.nextProperty;
2099 * Link it for real...
2101 parentProperty.nextProperty = newLinkProperty;
2103 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2105 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2108 * Set the parent dir to the property to delete previous
2110 newLinkProperty = propertyToDelete.previousProperty;
2112 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2115 * We also need to find a storage for the other link, setup variables
2116 * to do this at the end...
2118 needToFindAPlaceholder = TRUE;
2119 storeNode = propertyToDelete.previousProperty;
2120 toStoreNode = propertyToDelete.nextProperty;
2121 relationType = PROPERTY_RELATION_NEXT;
2124 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2127 * Set the parent dir to the property to delete next
2129 newLinkProperty = propertyToDelete.nextProperty;
2133 * Link it for real...
2135 parentProperty.dirProperty = newLinkProperty;
2139 * Write back the parent property
2141 res = StorageImpl_WriteProperty(
2142 This->ancestorStorage,
2143 parentPropertyId,
2144 &parentProperty);
2145 if(! res)
2147 return E_FAIL;
2151 * If a placeholder is required for the other link, then, find one and
2152 * get out of here...
2154 if (needToFindAPlaceholder)
2156 hr = findPlaceholder(
2157 This,
2158 toStoreNode,
2159 storeNode,
2160 relationType);
2163 return hr;
2167 /******************************************************************************
2168 * SetElementTimes (IStorage)
2170 HRESULT WINAPI StorageImpl_SetElementTimes(
2171 IStorage* iface,
2172 const OLECHAR *pwcsName,/* [string][in] */
2173 const FILETIME *pctime, /* [in] */
2174 const FILETIME *patime, /* [in] */
2175 const FILETIME *pmtime) /* [in] */
2177 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2178 return S_OK;
2181 /******************************************************************************
2182 * SetStateBits (IStorage)
2184 HRESULT WINAPI StorageImpl_SetStateBits(
2185 IStorage* iface,
2186 DWORD grfStateBits,/* [in] */
2187 DWORD grfMask) /* [in] */
2189 FIXME("not implemented!\n");
2190 return E_NOTIMPL;
2193 HRESULT StorageImpl_Construct(
2194 StorageImpl* This,
2195 HANDLE hFile,
2196 LPCOLESTR pwcsName,
2197 ILockBytes* pLkbyt,
2198 DWORD openFlags,
2199 BOOL fileBased,
2200 BOOL fileCreate)
2202 HRESULT hr = S_OK;
2203 StgProperty currentProperty;
2204 BOOL readSuccessful;
2205 ULONG currentPropertyIndex;
2207 if ( FAILED( validateSTGM(openFlags) ))
2208 return STG_E_INVALIDFLAG;
2210 memset(This, 0, sizeof(StorageImpl));
2213 * Initialize the virtual function table.
2215 This->lpVtbl = &Storage32Impl_Vtbl;
2216 This->v_destructor = &StorageImpl_Destroy;
2219 * This is the top-level storage so initialize the ancestor pointer
2220 * to this.
2222 This->ancestorStorage = This;
2225 * Initialize the physical support of the storage.
2227 This->hFile = hFile;
2230 * Store copy of file path.
2232 if(pwcsName) {
2233 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2234 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2235 if (!This->pwcsName)
2236 return STG_E_INSUFFICIENTMEMORY;
2237 strcpyW(This->pwcsName, pwcsName);
2241 * Initialize the big block cache.
2243 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2244 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2245 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2246 pLkbyt,
2247 openFlags,
2248 This->bigBlockSize,
2249 fileBased);
2251 if (This->bigBlockFile == 0)
2252 return E_FAIL;
2254 if (fileCreate)
2256 ULARGE_INTEGER size;
2257 BYTE* bigBlockBuffer;
2260 * Initialize all header variables:
2261 * - The big block depot consists of one block and it is at block 0
2262 * - The properties start at block 1
2263 * - There is no small block depot
2265 memset( This->bigBlockDepotStart,
2266 BLOCK_UNUSED,
2267 sizeof(This->bigBlockDepotStart));
2269 This->bigBlockDepotCount = 1;
2270 This->bigBlockDepotStart[0] = 0;
2271 This->rootStartBlock = 1;
2272 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2273 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2274 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2275 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2276 This->extBigBlockDepotCount = 0;
2278 StorageImpl_SaveFileHeader(This);
2281 * Add one block for the big block depot and one block for the properties
2283 size.u.HighPart = 0;
2284 size.u.LowPart = This->bigBlockSize * 3;
2285 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2288 * Initialize the big block depot
2290 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2291 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2292 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2293 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2294 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2296 else
2299 * Load the header for the file.
2301 hr = StorageImpl_LoadFileHeader(This);
2303 if (FAILED(hr))
2305 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2307 return hr;
2312 * There is no block depot cached yet.
2314 This->indexBlockDepotCached = 0xFFFFFFFF;
2317 * Start searching for free blocks with block 0.
2319 This->prevFreeBlock = 0;
2322 * Create the block chain abstractions.
2324 if(!(This->rootBlockChain =
2325 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2326 return STG_E_READFAULT;
2328 if(!(This->smallBlockDepotChain =
2329 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2330 PROPERTY_NULL)))
2331 return STG_E_READFAULT;
2334 * Write the root property
2336 if (fileCreate)
2338 StgProperty rootProp;
2340 * Initialize the property chain
2342 memset(&rootProp, 0, sizeof(rootProp));
2343 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2344 sizeof(rootProp.name)/sizeof(WCHAR) );
2345 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2346 rootProp.propertyType = PROPTYPE_ROOT;
2347 rootProp.previousProperty = PROPERTY_NULL;
2348 rootProp.nextProperty = PROPERTY_NULL;
2349 rootProp.dirProperty = PROPERTY_NULL;
2350 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2351 rootProp.size.u.HighPart = 0;
2352 rootProp.size.u.LowPart = 0;
2354 StorageImpl_WriteProperty(This, 0, &rootProp);
2358 * Find the ID of the root in the property sets.
2360 currentPropertyIndex = 0;
2364 readSuccessful = StorageImpl_ReadProperty(
2365 This,
2366 currentPropertyIndex,
2367 &currentProperty);
2369 if (readSuccessful)
2371 if ( (currentProperty.sizeOfNameString != 0 ) &&
2372 (currentProperty.propertyType == PROPTYPE_ROOT) )
2374 This->rootPropertySetIndex = currentPropertyIndex;
2378 currentPropertyIndex++;
2380 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2382 if (!readSuccessful)
2384 /* TODO CLEANUP */
2385 return STG_E_READFAULT;
2389 * Create the block chain abstraction for the small block root chain.
2391 if(!(This->smallBlockRootChain =
2392 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2393 return STG_E_READFAULT;
2395 return hr;
2398 void StorageImpl_Destroy(
2399 StorageImpl* This)
2401 TRACE("(%p)\n", This);
2403 if(This->pwcsName)
2404 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2406 BlockChainStream_Destroy(This->smallBlockRootChain);
2407 BlockChainStream_Destroy(This->rootBlockChain);
2408 BlockChainStream_Destroy(This->smallBlockDepotChain);
2410 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2411 return;
2414 /******************************************************************************
2415 * Storage32Impl_GetNextFreeBigBlock
2417 * Returns the index of the next free big block.
2418 * If the big block depot is filled, this method will enlarge it.
2421 ULONG StorageImpl_GetNextFreeBigBlock(
2422 StorageImpl* This)
2424 ULONG depotBlockIndexPos;
2425 void *depotBuffer;
2426 ULONG depotBlockOffset;
2427 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2428 ULONG nextBlockIndex = BLOCK_SPECIAL;
2429 int depotIndex = 0;
2430 ULONG freeBlock = BLOCK_UNUSED;
2432 depotIndex = This->prevFreeBlock / blocksPerDepot;
2433 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2436 * Scan the entire big block depot until we find a block marked free
2438 while (nextBlockIndex != BLOCK_UNUSED)
2440 if (depotIndex < COUNT_BBDEPOTINHEADER)
2442 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2445 * Grow the primary depot.
2447 if (depotBlockIndexPos == BLOCK_UNUSED)
2449 depotBlockIndexPos = depotIndex*blocksPerDepot;
2452 * Add a block depot.
2454 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2455 This->bigBlockDepotCount++;
2456 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2459 * Flag it as a block depot.
2461 StorageImpl_SetNextBlockInChain(This,
2462 depotBlockIndexPos,
2463 BLOCK_SPECIAL);
2465 /* Save new header information.
2467 StorageImpl_SaveFileHeader(This);
2470 else
2472 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2474 if (depotBlockIndexPos == BLOCK_UNUSED)
2477 * Grow the extended depot.
2479 ULONG extIndex = BLOCK_UNUSED;
2480 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2481 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2483 if (extBlockOffset == 0)
2485 /* We need an extended block.
2487 extIndex = Storage32Impl_AddExtBlockDepot(This);
2488 This->extBigBlockDepotCount++;
2489 depotBlockIndexPos = extIndex + 1;
2491 else
2492 depotBlockIndexPos = depotIndex * blocksPerDepot;
2495 * Add a block depot and mark it in the extended block.
2497 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2498 This->bigBlockDepotCount++;
2499 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2501 /* Flag the block depot.
2503 StorageImpl_SetNextBlockInChain(This,
2504 depotBlockIndexPos,
2505 BLOCK_SPECIAL);
2507 /* If necessary, flag the extended depot block.
2509 if (extIndex != BLOCK_UNUSED)
2510 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2512 /* Save header information.
2514 StorageImpl_SaveFileHeader(This);
2518 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2520 if (depotBuffer != 0)
2522 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2523 ( nextBlockIndex != BLOCK_UNUSED))
2525 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2527 if (nextBlockIndex == BLOCK_UNUSED)
2529 freeBlock = (depotIndex * blocksPerDepot) +
2530 (depotBlockOffset/sizeof(ULONG));
2533 depotBlockOffset += sizeof(ULONG);
2536 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2539 depotIndex++;
2540 depotBlockOffset = 0;
2543 This->prevFreeBlock = freeBlock;
2545 return freeBlock;
2548 /******************************************************************************
2549 * Storage32Impl_AddBlockDepot
2551 * This will create a depot block, essentially it is a block initialized
2552 * to BLOCK_UNUSEDs.
2554 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2556 BYTE* blockBuffer;
2558 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2561 * Initialize blocks as free
2563 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2565 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2568 /******************************************************************************
2569 * Storage32Impl_GetExtDepotBlock
2571 * Returns the index of the block that corresponds to the specified depot
2572 * index. This method is only for depot indexes equal or greater than
2573 * COUNT_BBDEPOTINHEADER.
2575 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2577 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2578 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2579 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2580 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2581 ULONG blockIndex = BLOCK_UNUSED;
2582 ULONG extBlockIndex = This->extBigBlockDepotStart;
2584 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2586 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2587 return BLOCK_UNUSED;
2589 while (extBlockCount > 0)
2591 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2592 extBlockCount--;
2595 if (extBlockIndex != BLOCK_UNUSED)
2597 BYTE* depotBuffer;
2599 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2601 if (depotBuffer != 0)
2603 StorageUtl_ReadDWord(depotBuffer,
2604 extBlockOffset * sizeof(ULONG),
2605 &blockIndex);
2607 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2611 return blockIndex;
2614 /******************************************************************************
2615 * Storage32Impl_SetExtDepotBlock
2617 * Associates the specified block index to the specified depot index.
2618 * This method is only for depot indexes equal or greater than
2619 * COUNT_BBDEPOTINHEADER.
2621 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2622 ULONG depotIndex,
2623 ULONG blockIndex)
2625 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2626 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2627 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2628 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2629 ULONG extBlockIndex = This->extBigBlockDepotStart;
2631 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2633 while (extBlockCount > 0)
2635 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2636 extBlockCount--;
2639 if (extBlockIndex != BLOCK_UNUSED)
2641 BYTE* depotBuffer;
2643 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2645 if (depotBuffer != 0)
2647 StorageUtl_WriteDWord(depotBuffer,
2648 extBlockOffset * sizeof(ULONG),
2649 blockIndex);
2651 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2656 /******************************************************************************
2657 * Storage32Impl_AddExtBlockDepot
2659 * Creates an extended depot block.
2661 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2663 ULONG numExtBlocks = This->extBigBlockDepotCount;
2664 ULONG nextExtBlock = This->extBigBlockDepotStart;
2665 BYTE* depotBuffer = NULL;
2666 ULONG index = BLOCK_UNUSED;
2667 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2668 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2669 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2671 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2672 blocksPerDepotBlock;
2674 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2677 * The first extended block.
2679 This->extBigBlockDepotStart = index;
2681 else
2683 int i;
2685 * Follow the chain to the last one.
2687 for (i = 0; i < (numExtBlocks - 1); i++)
2689 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2693 * Add the new extended block to the chain.
2695 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2696 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2697 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2701 * Initialize this block.
2703 depotBuffer = StorageImpl_GetBigBlock(This, index);
2704 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2705 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2707 return index;
2710 /******************************************************************************
2711 * Storage32Impl_FreeBigBlock
2713 * This method will flag the specified block as free in the big block depot.
2715 void StorageImpl_FreeBigBlock(
2716 StorageImpl* This,
2717 ULONG blockIndex)
2719 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2721 if (blockIndex < This->prevFreeBlock)
2722 This->prevFreeBlock = blockIndex;
2725 /************************************************************************
2726 * Storage32Impl_GetNextBlockInChain
2728 * This method will retrieve the block index of the next big block in
2729 * in the chain.
2731 * Params: This - Pointer to the Storage object.
2732 * blockIndex - Index of the block to retrieve the chain
2733 * for.
2734 * nextBlockIndex - receives the return value.
2736 * Returns: This method returns the index of the next block in the chain.
2737 * It will return the constants:
2738 * BLOCK_SPECIAL - If the block given was not part of a
2739 * chain.
2740 * BLOCK_END_OF_CHAIN - If the block given was the last in
2741 * a chain.
2742 * BLOCK_UNUSED - If the block given was not past of a chain
2743 * and is available.
2744 * BLOCK_EXTBBDEPOT - This block is part of the extended
2745 * big block depot.
2747 * See Windows documentation for more details on IStorage methods.
2749 HRESULT StorageImpl_GetNextBlockInChain(
2750 StorageImpl* This,
2751 ULONG blockIndex,
2752 ULONG* nextBlockIndex)
2754 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2755 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2756 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2757 void* depotBuffer;
2758 ULONG depotBlockIndexPos;
2759 int index;
2761 *nextBlockIndex = BLOCK_SPECIAL;
2763 if(depotBlockCount >= This->bigBlockDepotCount)
2765 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2766 This->bigBlockDepotCount);
2767 return STG_E_READFAULT;
2771 * Cache the currently accessed depot block.
2773 if (depotBlockCount != This->indexBlockDepotCached)
2775 This->indexBlockDepotCached = depotBlockCount;
2777 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2779 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2781 else
2784 * We have to look in the extended depot.
2786 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2789 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2791 if (!depotBuffer)
2792 return STG_E_READFAULT;
2794 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2796 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2797 This->blockDepotCached[index] = *nextBlockIndex;
2799 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2802 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2804 return S_OK;
2807 /******************************************************************************
2808 * Storage32Impl_GetNextExtendedBlock
2810 * Given an extended block this method will return the next extended block.
2812 * NOTES:
2813 * The last ULONG of an extended block is the block index of the next
2814 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2815 * depot.
2817 * Return values:
2818 * - The index of the next extended block
2819 * - BLOCK_UNUSED: there is no next extended block.
2820 * - Any other return values denotes failure.
2822 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2824 ULONG nextBlockIndex = BLOCK_SPECIAL;
2825 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2826 void* depotBuffer;
2828 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2830 if (depotBuffer!=0)
2832 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2834 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2837 return nextBlockIndex;
2840 /******************************************************************************
2841 * Storage32Impl_SetNextBlockInChain
2843 * This method will write the index of the specified block's next block
2844 * in the big block depot.
2846 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2847 * do the following
2849 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2850 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2851 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2854 void StorageImpl_SetNextBlockInChain(
2855 StorageImpl* This,
2856 ULONG blockIndex,
2857 ULONG nextBlock)
2859 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2860 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2861 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2862 ULONG depotBlockIndexPos;
2863 void* depotBuffer;
2865 assert(depotBlockCount < This->bigBlockDepotCount);
2866 assert(blockIndex != nextBlock);
2868 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2870 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2872 else
2875 * We have to look in the extended depot.
2877 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2880 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2882 if (depotBuffer!=0)
2884 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2885 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2889 * Update the cached block depot, if necessary.
2891 if (depotBlockCount == This->indexBlockDepotCached)
2893 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2897 /******************************************************************************
2898 * Storage32Impl_LoadFileHeader
2900 * This method will read in the file header, i.e. big block index -1.
2902 HRESULT StorageImpl_LoadFileHeader(
2903 StorageImpl* This)
2905 HRESULT hr = STG_E_FILENOTFOUND;
2906 void* headerBigBlock = NULL;
2907 int index;
2910 * Get a pointer to the big block of data containing the header.
2912 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2915 * Extract the information from the header.
2917 if (headerBigBlock!=0)
2920 * Check for the "magic number" signature and return an error if it is not
2921 * found.
2923 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2925 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2926 return STG_E_OLDFORMAT;
2929 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2931 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2932 return STG_E_INVALIDHEADER;
2935 StorageUtl_ReadWord(
2936 headerBigBlock,
2937 OFFSET_BIGBLOCKSIZEBITS,
2938 &This->bigBlockSizeBits);
2940 StorageUtl_ReadWord(
2941 headerBigBlock,
2942 OFFSET_SMALLBLOCKSIZEBITS,
2943 &This->smallBlockSizeBits);
2945 StorageUtl_ReadDWord(
2946 headerBigBlock,
2947 OFFSET_BBDEPOTCOUNT,
2948 &This->bigBlockDepotCount);
2950 StorageUtl_ReadDWord(
2951 headerBigBlock,
2952 OFFSET_ROOTSTARTBLOCK,
2953 &This->rootStartBlock);
2955 StorageUtl_ReadDWord(
2956 headerBigBlock,
2957 OFFSET_SBDEPOTSTART,
2958 &This->smallBlockDepotStart);
2960 StorageUtl_ReadDWord(
2961 headerBigBlock,
2962 OFFSET_EXTBBDEPOTSTART,
2963 &This->extBigBlockDepotStart);
2965 StorageUtl_ReadDWord(
2966 headerBigBlock,
2967 OFFSET_EXTBBDEPOTCOUNT,
2968 &This->extBigBlockDepotCount);
2970 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2972 StorageUtl_ReadDWord(
2973 headerBigBlock,
2974 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2975 &(This->bigBlockDepotStart[index]));
2979 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2981 if ((1 << 2) == 4)
2983 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2984 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2986 else
2988 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2989 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2993 * Right now, the code is making some assumptions about the size of the
2994 * blocks, just make sure they are what we're expecting.
2996 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2997 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2999 WARN("Broken OLE storage file\n");
3000 hr = STG_E_INVALIDHEADER;
3002 else
3003 hr = S_OK;
3006 * Release the block.
3008 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3011 return hr;
3014 /******************************************************************************
3015 * Storage32Impl_SaveFileHeader
3017 * This method will save to the file the header, i.e. big block -1.
3019 void StorageImpl_SaveFileHeader(
3020 StorageImpl* This)
3022 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3023 int index;
3024 BOOL success;
3027 * Get a pointer to the big block of data containing the header.
3029 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3032 * If the block read failed, the file is probably new.
3034 if (!success)
3037 * Initialize for all unknown fields.
3039 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3042 * Initialize the magic number.
3044 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3047 * And a bunch of things we don't know what they mean
3049 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3050 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3051 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3052 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3056 * Write the information to the header.
3058 StorageUtl_WriteWord(
3059 headerBigBlock,
3060 OFFSET_BIGBLOCKSIZEBITS,
3061 This->bigBlockSizeBits);
3063 StorageUtl_WriteWord(
3064 headerBigBlock,
3065 OFFSET_SMALLBLOCKSIZEBITS,
3066 This->smallBlockSizeBits);
3068 StorageUtl_WriteDWord(
3069 headerBigBlock,
3070 OFFSET_BBDEPOTCOUNT,
3071 This->bigBlockDepotCount);
3073 StorageUtl_WriteDWord(
3074 headerBigBlock,
3075 OFFSET_ROOTSTARTBLOCK,
3076 This->rootStartBlock);
3078 StorageUtl_WriteDWord(
3079 headerBigBlock,
3080 OFFSET_SBDEPOTSTART,
3081 This->smallBlockDepotStart);
3083 StorageUtl_WriteDWord(
3084 headerBigBlock,
3085 OFFSET_SBDEPOTCOUNT,
3086 This->smallBlockDepotChain ?
3087 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3089 StorageUtl_WriteDWord(
3090 headerBigBlock,
3091 OFFSET_EXTBBDEPOTSTART,
3092 This->extBigBlockDepotStart);
3094 StorageUtl_WriteDWord(
3095 headerBigBlock,
3096 OFFSET_EXTBBDEPOTCOUNT,
3097 This->extBigBlockDepotCount);
3099 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3101 StorageUtl_WriteDWord(
3102 headerBigBlock,
3103 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3104 (This->bigBlockDepotStart[index]));
3108 * Write the big block back to the file.
3110 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3113 /******************************************************************************
3114 * Storage32Impl_ReadProperty
3116 * This method will read the specified property from the property chain.
3118 BOOL StorageImpl_ReadProperty(
3119 StorageImpl* This,
3120 ULONG index,
3121 StgProperty* buffer)
3123 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3124 ULARGE_INTEGER offsetInPropSet;
3125 BOOL readSuccessful;
3126 ULONG bytesRead;
3128 offsetInPropSet.u.HighPart = 0;
3129 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3131 readSuccessful = BlockChainStream_ReadAt(
3132 This->rootBlockChain,
3133 offsetInPropSet,
3134 PROPSET_BLOCK_SIZE,
3135 currentProperty,
3136 &bytesRead);
3138 if (readSuccessful)
3140 /* replace the name of root entry (often "Root Entry") by the file name */
3141 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3142 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3144 memset(buffer->name, 0, sizeof(buffer->name));
3145 memcpy(
3146 buffer->name,
3147 propName,
3148 PROPERTY_NAME_BUFFER_LEN );
3149 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3151 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3153 StorageUtl_ReadWord(
3154 currentProperty,
3155 OFFSET_PS_NAMELENGTH,
3156 &buffer->sizeOfNameString);
3158 StorageUtl_ReadDWord(
3159 currentProperty,
3160 OFFSET_PS_PREVIOUSPROP,
3161 &buffer->previousProperty);
3163 StorageUtl_ReadDWord(
3164 currentProperty,
3165 OFFSET_PS_NEXTPROP,
3166 &buffer->nextProperty);
3168 StorageUtl_ReadDWord(
3169 currentProperty,
3170 OFFSET_PS_DIRPROP,
3171 &buffer->dirProperty);
3173 StorageUtl_ReadGUID(
3174 currentProperty,
3175 OFFSET_PS_GUID,
3176 &buffer->propertyUniqueID);
3178 StorageUtl_ReadDWord(
3179 currentProperty,
3180 OFFSET_PS_TSS1,
3181 &buffer->timeStampS1);
3183 StorageUtl_ReadDWord(
3184 currentProperty,
3185 OFFSET_PS_TSD1,
3186 &buffer->timeStampD1);
3188 StorageUtl_ReadDWord(
3189 currentProperty,
3190 OFFSET_PS_TSS2,
3191 &buffer->timeStampS2);
3193 StorageUtl_ReadDWord(
3194 currentProperty,
3195 OFFSET_PS_TSD2,
3196 &buffer->timeStampD2);
3198 StorageUtl_ReadDWord(
3199 currentProperty,
3200 OFFSET_PS_STARTBLOCK,
3201 &buffer->startingBlock);
3203 StorageUtl_ReadDWord(
3204 currentProperty,
3205 OFFSET_PS_SIZE,
3206 &buffer->size.u.LowPart);
3208 buffer->size.u.HighPart = 0;
3211 return readSuccessful;
3214 /*********************************************************************
3215 * Write the specified property into the property chain
3217 BOOL StorageImpl_WriteProperty(
3218 StorageImpl* This,
3219 ULONG index,
3220 StgProperty* buffer)
3222 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3223 ULARGE_INTEGER offsetInPropSet;
3224 BOOL writeSuccessful;
3225 ULONG bytesWritten;
3227 offsetInPropSet.u.HighPart = 0;
3228 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3230 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3232 memcpy(
3233 currentProperty + OFFSET_PS_NAME,
3234 buffer->name,
3235 PROPERTY_NAME_BUFFER_LEN );
3237 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3239 StorageUtl_WriteWord(
3240 currentProperty,
3241 OFFSET_PS_NAMELENGTH,
3242 buffer->sizeOfNameString);
3244 StorageUtl_WriteDWord(
3245 currentProperty,
3246 OFFSET_PS_PREVIOUSPROP,
3247 buffer->previousProperty);
3249 StorageUtl_WriteDWord(
3250 currentProperty,
3251 OFFSET_PS_NEXTPROP,
3252 buffer->nextProperty);
3254 StorageUtl_WriteDWord(
3255 currentProperty,
3256 OFFSET_PS_DIRPROP,
3257 buffer->dirProperty);
3259 StorageUtl_WriteGUID(
3260 currentProperty,
3261 OFFSET_PS_GUID,
3262 &buffer->propertyUniqueID);
3264 StorageUtl_WriteDWord(
3265 currentProperty,
3266 OFFSET_PS_TSS1,
3267 buffer->timeStampS1);
3269 StorageUtl_WriteDWord(
3270 currentProperty,
3271 OFFSET_PS_TSD1,
3272 buffer->timeStampD1);
3274 StorageUtl_WriteDWord(
3275 currentProperty,
3276 OFFSET_PS_TSS2,
3277 buffer->timeStampS2);
3279 StorageUtl_WriteDWord(
3280 currentProperty,
3281 OFFSET_PS_TSD2,
3282 buffer->timeStampD2);
3284 StorageUtl_WriteDWord(
3285 currentProperty,
3286 OFFSET_PS_STARTBLOCK,
3287 buffer->startingBlock);
3289 StorageUtl_WriteDWord(
3290 currentProperty,
3291 OFFSET_PS_SIZE,
3292 buffer->size.u.LowPart);
3294 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3295 offsetInPropSet,
3296 PROPSET_BLOCK_SIZE,
3297 currentProperty,
3298 &bytesWritten);
3299 return writeSuccessful;
3302 BOOL StorageImpl_ReadBigBlock(
3303 StorageImpl* This,
3304 ULONG blockIndex,
3305 void* buffer)
3307 void* bigBlockBuffer;
3309 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3311 if (bigBlockBuffer!=0)
3313 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3315 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3317 return TRUE;
3320 return FALSE;
3323 BOOL StorageImpl_WriteBigBlock(
3324 StorageImpl* This,
3325 ULONG blockIndex,
3326 void* buffer)
3328 void* bigBlockBuffer;
3330 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3332 if (bigBlockBuffer!=0)
3334 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3336 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3338 return TRUE;
3341 return FALSE;
3344 void* StorageImpl_GetROBigBlock(
3345 StorageImpl* This,
3346 ULONG blockIndex)
3348 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3351 void* StorageImpl_GetBigBlock(
3352 StorageImpl* This,
3353 ULONG blockIndex)
3355 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3358 void StorageImpl_ReleaseBigBlock(
3359 StorageImpl* This,
3360 void* pBigBlock)
3362 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3365 /******************************************************************************
3366 * Storage32Impl_SmallBlocksToBigBlocks
3368 * This method will convert a small block chain to a big block chain.
3369 * The small block chain will be destroyed.
3371 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3372 StorageImpl* This,
3373 SmallBlockChainStream** ppsbChain)
3375 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3376 ULARGE_INTEGER size, offset;
3377 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3378 ULONG propertyIndex;
3379 BOOL successRead, successWrite;
3380 StgProperty chainProperty;
3381 BYTE *buffer;
3382 BlockChainStream *bbTempChain = NULL;
3383 BlockChainStream *bigBlockChain = NULL;
3386 * Create a temporary big block chain that doesn't have
3387 * an associated property. This temporary chain will be
3388 * used to copy data from small blocks to big blocks.
3390 bbTempChain = BlockChainStream_Construct(This,
3391 &bbHeadOfChain,
3392 PROPERTY_NULL);
3393 if(!bbTempChain) return NULL;
3395 * Grow the big block chain.
3397 size = SmallBlockChainStream_GetSize(*ppsbChain);
3398 BlockChainStream_SetSize(bbTempChain, size);
3401 * Copy the contents of the small block chain to the big block chain
3402 * by small block size increments.
3404 offset.u.LowPart = 0;
3405 offset.u.HighPart = 0;
3406 cbTotalRead = 0;
3407 cbTotalWritten = 0;
3409 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3412 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3413 offset,
3414 DEF_SMALL_BLOCK_SIZE,
3415 buffer,
3416 &cbRead);
3417 cbTotalRead += cbRead;
3419 successWrite = BlockChainStream_WriteAt(bbTempChain,
3420 offset,
3421 cbRead,
3422 buffer,
3423 &cbWritten);
3424 cbTotalWritten += cbWritten;
3426 offset.u.LowPart += This->smallBlockSize;
3428 } while (successRead && successWrite);
3429 HeapFree(GetProcessHeap(),0,buffer);
3431 assert(cbTotalRead == cbTotalWritten);
3434 * Destroy the small block chain.
3436 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3437 size.u.HighPart = 0;
3438 size.u.LowPart = 0;
3439 SmallBlockChainStream_SetSize(*ppsbChain, size);
3440 SmallBlockChainStream_Destroy(*ppsbChain);
3441 *ppsbChain = 0;
3444 * Change the property information. This chain is now a big block chain
3445 * and it doesn't reside in the small blocks chain anymore.
3447 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3449 chainProperty.startingBlock = bbHeadOfChain;
3451 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3454 * Destroy the temporary propertyless big block chain.
3455 * Create a new big block chain associated with this property.
3457 BlockChainStream_Destroy(bbTempChain);
3458 bigBlockChain = BlockChainStream_Construct(This,
3459 NULL,
3460 propertyIndex);
3462 return bigBlockChain;
3465 /******************************************************************************
3466 ** Storage32InternalImpl implementation
3469 StorageInternalImpl* StorageInternalImpl_Construct(
3470 StorageImpl* ancestorStorage,
3471 ULONG rootPropertyIndex)
3473 StorageInternalImpl* newStorage;
3476 * Allocate space for the new storage object
3478 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3480 if (newStorage!=0)
3482 memset(newStorage, 0, sizeof(StorageInternalImpl));
3485 * Initialize the virtual function table.
3487 newStorage->lpVtbl = &Storage32InternalImpl_Vtbl;
3488 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3491 * Keep the ancestor storage pointer and nail a reference to it.
3493 newStorage->ancestorStorage = ancestorStorage;
3494 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3497 * Keep the index of the root property set for this storage,
3499 newStorage->rootPropertySetIndex = rootPropertyIndex;
3501 return newStorage;
3504 return 0;
3507 void StorageInternalImpl_Destroy(
3508 StorageInternalImpl* This)
3510 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3511 HeapFree(GetProcessHeap(), 0, This);
3514 /******************************************************************************
3516 ** Storage32InternalImpl_Commit
3518 ** The non-root storages cannot be opened in transacted mode thus this function
3519 ** does nothing.
3521 HRESULT WINAPI StorageInternalImpl_Commit(
3522 IStorage* iface,
3523 DWORD grfCommitFlags) /* [in] */
3525 return S_OK;
3528 /******************************************************************************
3530 ** Storage32InternalImpl_Revert
3532 ** The non-root storages cannot be opened in transacted mode thus this function
3533 ** does nothing.
3535 HRESULT WINAPI StorageInternalImpl_Revert(
3536 IStorage* iface)
3538 return S_OK;
3541 /******************************************************************************
3542 ** IEnumSTATSTGImpl implementation
3545 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3546 StorageImpl* parentStorage,
3547 ULONG firstPropertyNode)
3549 IEnumSTATSTGImpl* newEnumeration;
3551 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3553 if (newEnumeration!=0)
3556 * Set-up the virtual function table and reference count.
3558 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3559 newEnumeration->ref = 0;
3562 * We want to nail-down the reference to the storage in case the
3563 * enumeration out-lives the storage in the client application.
3565 newEnumeration->parentStorage = parentStorage;
3566 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3568 newEnumeration->firstPropertyNode = firstPropertyNode;
3571 * Initialize the search stack
3573 newEnumeration->stackSize = 0;
3574 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3575 newEnumeration->stackToVisit =
3576 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3579 * Make sure the current node of the iterator is the first one.
3581 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3584 return newEnumeration;
3587 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3589 IStorage_Release((IStorage*)This->parentStorage);
3590 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3591 HeapFree(GetProcessHeap(), 0, This);
3594 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3595 IEnumSTATSTG* iface,
3596 REFIID riid,
3597 void** ppvObject)
3599 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3602 * Perform a sanity check on the parameters.
3604 if (ppvObject==0)
3605 return E_INVALIDARG;
3608 * Initialize the return parameter.
3610 *ppvObject = 0;
3613 * Compare the riid with the interface IDs implemented by this object.
3615 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3617 *ppvObject = (IEnumSTATSTG*)This;
3619 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3621 *ppvObject = (IEnumSTATSTG*)This;
3625 * Check that we obtained an interface.
3627 if ((*ppvObject)==0)
3628 return E_NOINTERFACE;
3631 * Query Interface always increases the reference count by one when it is
3632 * successful
3634 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3636 return S_OK;
3639 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3640 IEnumSTATSTG* iface)
3642 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3644 This->ref++;
3645 return This->ref;
3648 ULONG WINAPI IEnumSTATSTGImpl_Release(
3649 IEnumSTATSTG* iface)
3651 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3653 ULONG newRef;
3655 This->ref--;
3656 newRef = This->ref;
3659 * If the reference count goes down to 0, perform suicide.
3661 if (newRef==0)
3663 IEnumSTATSTGImpl_Destroy(This);
3666 return newRef;
3669 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3670 IEnumSTATSTG* iface,
3671 ULONG celt,
3672 STATSTG* rgelt,
3673 ULONG* pceltFetched)
3675 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3677 StgProperty currentProperty;
3678 STATSTG* currentReturnStruct = rgelt;
3679 ULONG objectFetched = 0;
3680 ULONG currentSearchNode;
3683 * Perform a sanity check on the parameters.
3685 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3686 return E_INVALIDARG;
3689 * To avoid the special case, get another pointer to a ULONG value if
3690 * the caller didn't supply one.
3692 if (pceltFetched==0)
3693 pceltFetched = &objectFetched;
3696 * Start the iteration, we will iterate until we hit the end of the
3697 * linked list or until we hit the number of items to iterate through
3699 *pceltFetched = 0;
3702 * Start with the node at the top of the stack.
3704 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3706 while ( ( *pceltFetched < celt) &&
3707 ( currentSearchNode!=PROPERTY_NULL) )
3710 * Remove the top node from the stack
3712 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3715 * Read the property from the storage.
3717 StorageImpl_ReadProperty(This->parentStorage,
3718 currentSearchNode,
3719 &currentProperty);
3722 * Copy the information to the return buffer.
3724 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3725 &currentProperty,
3726 STATFLAG_DEFAULT);
3729 * Step to the next item in the iteration
3731 (*pceltFetched)++;
3732 currentReturnStruct++;
3735 * Push the next search node in the search stack.
3737 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3740 * continue the iteration.
3742 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3745 if (*pceltFetched == celt)
3746 return S_OK;
3748 return S_FALSE;
3752 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3753 IEnumSTATSTG* iface,
3754 ULONG celt)
3756 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3758 StgProperty currentProperty;
3759 ULONG objectFetched = 0;
3760 ULONG currentSearchNode;
3763 * Start with the node at the top of the stack.
3765 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3767 while ( (objectFetched < celt) &&
3768 (currentSearchNode!=PROPERTY_NULL) )
3771 * Remove the top node from the stack
3773 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3776 * Read the property from the storage.
3778 StorageImpl_ReadProperty(This->parentStorage,
3779 currentSearchNode,
3780 &currentProperty);
3783 * Step to the next item in the iteration
3785 objectFetched++;
3788 * Push the next search node in the search stack.
3790 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3793 * continue the iteration.
3795 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3798 if (objectFetched == celt)
3799 return S_OK;
3801 return S_FALSE;
3804 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3805 IEnumSTATSTG* iface)
3807 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3809 StgProperty rootProperty;
3810 BOOL readSuccessful;
3813 * Re-initialize the search stack to an empty stack
3815 This->stackSize = 0;
3818 * Read the root property from the storage.
3820 readSuccessful = StorageImpl_ReadProperty(
3821 This->parentStorage,
3822 This->firstPropertyNode,
3823 &rootProperty);
3825 if (readSuccessful)
3827 assert(rootProperty.sizeOfNameString!=0);
3830 * Push the search node in the search stack.
3832 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3835 return S_OK;
3838 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3839 IEnumSTATSTG* iface,
3840 IEnumSTATSTG** ppenum)
3842 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3844 IEnumSTATSTGImpl* newClone;
3847 * Perform a sanity check on the parameters.
3849 if (ppenum==0)
3850 return E_INVALIDARG;
3852 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3853 This->firstPropertyNode);
3857 * The new clone enumeration must point to the same current node as
3858 * the ole one.
3860 newClone->stackSize = This->stackSize ;
3861 newClone->stackMaxSize = This->stackMaxSize ;
3862 newClone->stackToVisit =
3863 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3865 memcpy(
3866 newClone->stackToVisit,
3867 This->stackToVisit,
3868 sizeof(ULONG) * newClone->stackSize);
3870 *ppenum = (IEnumSTATSTG*)newClone;
3873 * Don't forget to nail down a reference to the clone before
3874 * returning it.
3876 IEnumSTATSTGImpl_AddRef(*ppenum);
3878 return S_OK;
3881 INT IEnumSTATSTGImpl_FindParentProperty(
3882 IEnumSTATSTGImpl *This,
3883 ULONG childProperty,
3884 StgProperty *currentProperty,
3885 ULONG *thisNodeId)
3887 ULONG currentSearchNode;
3888 ULONG foundNode;
3891 * To avoid the special case, get another pointer to a ULONG value if
3892 * the caller didn't supply one.
3894 if (thisNodeId==0)
3895 thisNodeId = &foundNode;
3898 * Start with the node at the top of the stack.
3900 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3903 while (currentSearchNode!=PROPERTY_NULL)
3906 * Store the current node in the returned parameters
3908 *thisNodeId = currentSearchNode;
3911 * Remove the top node from the stack
3913 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3916 * Read the property from the storage.
3918 StorageImpl_ReadProperty(
3919 This->parentStorage,
3920 currentSearchNode,
3921 currentProperty);
3923 if (currentProperty->previousProperty == childProperty)
3924 return PROPERTY_RELATION_PREVIOUS;
3926 else if (currentProperty->nextProperty == childProperty)
3927 return PROPERTY_RELATION_NEXT;
3929 else if (currentProperty->dirProperty == childProperty)
3930 return PROPERTY_RELATION_DIR;
3933 * Push the next search node in the search stack.
3935 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3938 * continue the iteration.
3940 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3943 return PROPERTY_NULL;
3946 ULONG IEnumSTATSTGImpl_FindProperty(
3947 IEnumSTATSTGImpl* This,
3948 const OLECHAR* lpszPropName,
3949 StgProperty* currentProperty)
3951 ULONG currentSearchNode;
3954 * Start with the node at the top of the stack.
3956 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3958 while (currentSearchNode!=PROPERTY_NULL)
3961 * Remove the top node from the stack
3963 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3966 * Read the property from the storage.
3968 StorageImpl_ReadProperty(This->parentStorage,
3969 currentSearchNode,
3970 currentProperty);
3972 if ( propertyNameCmp(
3973 (OLECHAR*)currentProperty->name,
3974 (OLECHAR*)lpszPropName) == 0)
3975 return currentSearchNode;
3978 * Push the next search node in the search stack.
3980 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3983 * continue the iteration.
3985 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3988 return PROPERTY_NULL;
3991 void IEnumSTATSTGImpl_PushSearchNode(
3992 IEnumSTATSTGImpl* This,
3993 ULONG nodeToPush)
3995 StgProperty rootProperty;
3996 BOOL readSuccessful;
3999 * First, make sure we're not trying to push an unexisting node.
4001 if (nodeToPush==PROPERTY_NULL)
4002 return;
4005 * First push the node to the stack
4007 if (This->stackSize == This->stackMaxSize)
4009 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4011 This->stackToVisit = HeapReAlloc(
4012 GetProcessHeap(),
4014 This->stackToVisit,
4015 sizeof(ULONG) * This->stackMaxSize);
4018 This->stackToVisit[This->stackSize] = nodeToPush;
4019 This->stackSize++;
4022 * Read the root property from the storage.
4024 readSuccessful = StorageImpl_ReadProperty(
4025 This->parentStorage,
4026 nodeToPush,
4027 &rootProperty);
4029 if (readSuccessful)
4031 assert(rootProperty.sizeOfNameString!=0);
4034 * Push the previous search node in the search stack.
4036 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4040 ULONG IEnumSTATSTGImpl_PopSearchNode(
4041 IEnumSTATSTGImpl* This,
4042 BOOL remove)
4044 ULONG topNode;
4046 if (This->stackSize == 0)
4047 return PROPERTY_NULL;
4049 topNode = This->stackToVisit[This->stackSize-1];
4051 if (remove)
4052 This->stackSize--;
4054 return topNode;
4057 /******************************************************************************
4058 ** StorageUtl implementation
4061 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4063 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4066 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4068 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4071 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4073 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4076 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4078 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4081 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4083 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4084 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4085 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4087 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4090 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4092 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4093 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4094 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4096 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4099 void StorageUtl_CopyPropertyToSTATSTG(
4100 STATSTG* destination,
4101 StgProperty* source,
4102 int statFlags)
4105 * The copy of the string occurs only when the flag is not set
4107 if ((statFlags & STATFLAG_NONAME) != 0)
4109 destination->pwcsName = 0;
4111 else
4113 destination->pwcsName =
4114 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4116 strcpyW((LPWSTR)destination->pwcsName, source->name);
4119 switch (source->propertyType)
4121 case PROPTYPE_STORAGE:
4122 case PROPTYPE_ROOT:
4123 destination->type = STGTY_STORAGE;
4124 break;
4125 case PROPTYPE_STREAM:
4126 destination->type = STGTY_STREAM;
4127 break;
4128 default:
4129 destination->type = STGTY_STREAM;
4130 break;
4133 destination->cbSize = source->size;
4135 currentReturnStruct->mtime = {0}; TODO
4136 currentReturnStruct->ctime = {0};
4137 currentReturnStruct->atime = {0};
4139 destination->grfMode = 0;
4140 destination->grfLocksSupported = 0;
4141 destination->clsid = source->propertyUniqueID;
4142 destination->grfStateBits = 0;
4143 destination->reserved = 0;
4146 /******************************************************************************
4147 ** BlockChainStream implementation
4150 BlockChainStream* BlockChainStream_Construct(
4151 StorageImpl* parentStorage,
4152 ULONG* headOfStreamPlaceHolder,
4153 ULONG propertyIndex)
4155 BlockChainStream* newStream;
4156 ULONG blockIndex;
4158 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4160 newStream->parentStorage = parentStorage;
4161 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4162 newStream->ownerPropertyIndex = propertyIndex;
4163 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4164 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4165 newStream->numBlocks = 0;
4167 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4169 while (blockIndex != BLOCK_END_OF_CHAIN)
4171 newStream->numBlocks++;
4172 newStream->tailIndex = blockIndex;
4174 if(FAILED(StorageImpl_GetNextBlockInChain(
4175 parentStorage,
4176 blockIndex,
4177 &blockIndex)))
4179 HeapFree(GetProcessHeap(), 0, newStream);
4180 return NULL;
4184 return newStream;
4187 void BlockChainStream_Destroy(BlockChainStream* This)
4189 HeapFree(GetProcessHeap(), 0, This);
4192 /******************************************************************************
4193 * BlockChainStream_GetHeadOfChain
4195 * Returns the head of this stream chain.
4196 * Some special chains don't have properties, their heads are kept in
4197 * This->headOfStreamPlaceHolder.
4200 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4202 StgProperty chainProperty;
4203 BOOL readSuccessful;
4205 if (This->headOfStreamPlaceHolder != 0)
4206 return *(This->headOfStreamPlaceHolder);
4208 if (This->ownerPropertyIndex != PROPERTY_NULL)
4210 readSuccessful = StorageImpl_ReadProperty(
4211 This->parentStorage,
4212 This->ownerPropertyIndex,
4213 &chainProperty);
4215 if (readSuccessful)
4217 return chainProperty.startingBlock;
4221 return BLOCK_END_OF_CHAIN;
4224 /******************************************************************************
4225 * BlockChainStream_GetCount
4227 * Returns the number of blocks that comprises this chain.
4228 * This is not the size of the stream as the last block may not be full!
4231 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4233 ULONG blockIndex;
4234 ULONG count = 0;
4236 blockIndex = BlockChainStream_GetHeadOfChain(This);
4238 while (blockIndex != BLOCK_END_OF_CHAIN)
4240 count++;
4242 if(FAILED(StorageImpl_GetNextBlockInChain(
4243 This->parentStorage,
4244 blockIndex,
4245 &blockIndex)))
4246 return 0;
4249 return count;
4252 /******************************************************************************
4253 * BlockChainStream_ReadAt
4255 * Reads a specified number of bytes from this chain at the specified offset.
4256 * bytesRead may be NULL.
4257 * Failure will be returned if the specified number of bytes has not been read.
4259 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4260 ULARGE_INTEGER offset,
4261 ULONG size,
4262 void* buffer,
4263 ULONG* bytesRead)
4265 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4266 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4267 ULONG bytesToReadInBuffer;
4268 ULONG blockIndex;
4269 BYTE* bufferWalker;
4270 BYTE* bigBlockBuffer;
4273 * Find the first block in the stream that contains part of the buffer.
4275 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4276 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4277 (blockNoInSequence < This->lastBlockNoInSequence) )
4279 blockIndex = BlockChainStream_GetHeadOfChain(This);
4280 This->lastBlockNoInSequence = blockNoInSequence;
4282 else
4284 ULONG temp = blockNoInSequence;
4286 blockIndex = This->lastBlockNoInSequenceIndex;
4287 blockNoInSequence -= This->lastBlockNoInSequence;
4288 This->lastBlockNoInSequence = temp;
4291 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4293 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4294 return FALSE;
4295 blockNoInSequence--;
4298 This->lastBlockNoInSequenceIndex = blockIndex;
4301 * Start reading the buffer.
4303 *bytesRead = 0;
4304 bufferWalker = buffer;
4306 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4309 * Calculate how many bytes we can copy from this big block.
4311 bytesToReadInBuffer =
4312 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4315 * Copy those bytes to the buffer
4317 bigBlockBuffer =
4318 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4320 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4322 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4325 * Step to the next big block.
4327 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4328 return FALSE;
4330 bufferWalker += bytesToReadInBuffer;
4331 size -= bytesToReadInBuffer;
4332 *bytesRead += bytesToReadInBuffer;
4333 offsetInBlock = 0; /* There is no offset on the next block */
4337 return (size == 0);
4340 /******************************************************************************
4341 * BlockChainStream_WriteAt
4343 * Writes the specified number of bytes to this chain at the specified offset.
4344 * bytesWritten may be NULL.
4345 * Will fail if not all specified number of bytes have been written.
4347 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4348 ULARGE_INTEGER offset,
4349 ULONG size,
4350 const void* buffer,
4351 ULONG* bytesWritten)
4353 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4354 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4355 ULONG bytesToWrite;
4356 ULONG blockIndex;
4357 BYTE* bufferWalker;
4358 BYTE* bigBlockBuffer;
4361 * Find the first block in the stream that contains part of the buffer.
4363 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4364 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4365 (blockNoInSequence < This->lastBlockNoInSequence) )
4367 blockIndex = BlockChainStream_GetHeadOfChain(This);
4368 This->lastBlockNoInSequence = blockNoInSequence;
4370 else
4372 ULONG temp = blockNoInSequence;
4374 blockIndex = This->lastBlockNoInSequenceIndex;
4375 blockNoInSequence -= This->lastBlockNoInSequence;
4376 This->lastBlockNoInSequence = temp;
4379 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4381 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4382 &blockIndex)))
4383 return FALSE;
4384 blockNoInSequence--;
4387 This->lastBlockNoInSequenceIndex = blockIndex;
4390 * Here, I'm casting away the constness on the buffer variable
4391 * This is OK since we don't intend to modify that buffer.
4393 *bytesWritten = 0;
4394 bufferWalker = (BYTE*)buffer;
4396 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4399 * Calculate how many bytes we can copy from this big block.
4401 bytesToWrite =
4402 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4405 * Copy those bytes to the buffer
4407 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4409 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4411 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4414 * Step to the next big block.
4416 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4417 &blockIndex)))
4418 return FALSE;
4419 bufferWalker += bytesToWrite;
4420 size -= bytesToWrite;
4421 *bytesWritten += bytesToWrite;
4422 offsetInBlock = 0; /* There is no offset on the next block */
4425 return (size == 0);
4428 /******************************************************************************
4429 * BlockChainStream_Shrink
4431 * Shrinks this chain in the big block depot.
4433 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4434 ULARGE_INTEGER newSize)
4436 ULONG blockIndex, extraBlock;
4437 ULONG numBlocks;
4438 ULONG count = 1;
4441 * Reset the last accessed block cache.
4443 This->lastBlockNoInSequence = 0xFFFFFFFF;
4444 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4447 * Figure out how many blocks are needed to contain the new size
4449 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4451 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4452 numBlocks++;
4454 blockIndex = BlockChainStream_GetHeadOfChain(This);
4457 * Go to the new end of chain
4459 while (count < numBlocks)
4461 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4462 &blockIndex)))
4463 return FALSE;
4464 count++;
4467 /* Get the next block before marking the new end */
4468 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4469 &extraBlock)))
4470 return FALSE;
4472 /* Mark the new end of chain */
4473 StorageImpl_SetNextBlockInChain(
4474 This->parentStorage,
4475 blockIndex,
4476 BLOCK_END_OF_CHAIN);
4478 This->tailIndex = blockIndex;
4479 This->numBlocks = numBlocks;
4482 * Mark the extra blocks as free
4484 while (extraBlock != BLOCK_END_OF_CHAIN)
4486 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4487 &blockIndex)))
4488 return FALSE;
4489 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4490 extraBlock = blockIndex;
4493 return TRUE;
4496 /******************************************************************************
4497 * BlockChainStream_Enlarge
4499 * Grows this chain in the big block depot.
4501 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4502 ULARGE_INTEGER newSize)
4504 ULONG blockIndex, currentBlock;
4505 ULONG newNumBlocks;
4506 ULONG oldNumBlocks = 0;
4508 blockIndex = BlockChainStream_GetHeadOfChain(This);
4511 * Empty chain. Create the head.
4513 if (blockIndex == BLOCK_END_OF_CHAIN)
4515 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4516 StorageImpl_SetNextBlockInChain(This->parentStorage,
4517 blockIndex,
4518 BLOCK_END_OF_CHAIN);
4520 if (This->headOfStreamPlaceHolder != 0)
4522 *(This->headOfStreamPlaceHolder) = blockIndex;
4524 else
4526 StgProperty chainProp;
4527 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4529 StorageImpl_ReadProperty(
4530 This->parentStorage,
4531 This->ownerPropertyIndex,
4532 &chainProp);
4534 chainProp.startingBlock = blockIndex;
4536 StorageImpl_WriteProperty(
4537 This->parentStorage,
4538 This->ownerPropertyIndex,
4539 &chainProp);
4542 This->tailIndex = blockIndex;
4543 This->numBlocks = 1;
4547 * Figure out how many blocks are needed to contain this stream
4549 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4551 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4552 newNumBlocks++;
4555 * Go to the current end of chain
4557 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4559 currentBlock = blockIndex;
4561 while (blockIndex != BLOCK_END_OF_CHAIN)
4563 This->numBlocks++;
4564 currentBlock = blockIndex;
4566 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4567 &blockIndex)))
4568 return FALSE;
4571 This->tailIndex = currentBlock;
4574 currentBlock = This->tailIndex;
4575 oldNumBlocks = This->numBlocks;
4578 * Add new blocks to the chain
4580 if (oldNumBlocks < newNumBlocks)
4582 while (oldNumBlocks < newNumBlocks)
4584 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4586 StorageImpl_SetNextBlockInChain(
4587 This->parentStorage,
4588 currentBlock,
4589 blockIndex);
4591 StorageImpl_SetNextBlockInChain(
4592 This->parentStorage,
4593 blockIndex,
4594 BLOCK_END_OF_CHAIN);
4596 currentBlock = blockIndex;
4597 oldNumBlocks++;
4600 This->tailIndex = blockIndex;
4601 This->numBlocks = newNumBlocks;
4604 return TRUE;
4607 /******************************************************************************
4608 * BlockChainStream_SetSize
4610 * Sets the size of this stream. The big block depot will be updated.
4611 * The file will grow if we grow the chain.
4613 * TODO: Free the actual blocks in the file when we shrink the chain.
4614 * Currently, the blocks are still in the file. So the file size
4615 * doesn't shrink even if we shrink streams.
4617 BOOL BlockChainStream_SetSize(
4618 BlockChainStream* This,
4619 ULARGE_INTEGER newSize)
4621 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4623 if (newSize.u.LowPart == size.u.LowPart)
4624 return TRUE;
4626 if (newSize.u.LowPart < size.u.LowPart)
4628 BlockChainStream_Shrink(This, newSize);
4630 else
4632 ULARGE_INTEGER fileSize =
4633 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4635 ULONG diff = newSize.u.LowPart - size.u.LowPart;
4638 * Make sure the file stays a multiple of blocksize
4640 if ((diff % This->parentStorage->bigBlockSize) != 0)
4641 diff += (This->parentStorage->bigBlockSize -
4642 (diff % This->parentStorage->bigBlockSize) );
4644 fileSize.u.LowPart += diff;
4645 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4647 BlockChainStream_Enlarge(This, newSize);
4650 return TRUE;
4653 /******************************************************************************
4654 * BlockChainStream_GetSize
4656 * Returns the size of this chain.
4657 * Will return the block count if this chain doesn't have a property.
4659 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4661 StgProperty chainProperty;
4663 if(This->headOfStreamPlaceHolder == NULL)
4666 * This chain is a data stream read the property and return
4667 * the appropriate size
4669 StorageImpl_ReadProperty(
4670 This->parentStorage,
4671 This->ownerPropertyIndex,
4672 &chainProperty);
4674 return chainProperty.size;
4676 else
4679 * this chain is a chain that does not have a property, figure out the
4680 * size by making the product number of used blocks times the
4681 * size of them
4683 ULARGE_INTEGER result;
4684 result.u.HighPart = 0;
4686 result.u.LowPart =
4687 BlockChainStream_GetCount(This) *
4688 This->parentStorage->bigBlockSize;
4690 return result;
4694 /******************************************************************************
4695 ** SmallBlockChainStream implementation
4698 SmallBlockChainStream* SmallBlockChainStream_Construct(
4699 StorageImpl* parentStorage,
4700 ULONG propertyIndex)
4702 SmallBlockChainStream* newStream;
4704 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4706 newStream->parentStorage = parentStorage;
4707 newStream->ownerPropertyIndex = propertyIndex;
4709 return newStream;
4712 void SmallBlockChainStream_Destroy(
4713 SmallBlockChainStream* This)
4715 HeapFree(GetProcessHeap(), 0, This);
4718 /******************************************************************************
4719 * SmallBlockChainStream_GetHeadOfChain
4721 * Returns the head of this chain of small blocks.
4723 ULONG SmallBlockChainStream_GetHeadOfChain(
4724 SmallBlockChainStream* This)
4726 StgProperty chainProperty;
4727 BOOL readSuccessful;
4729 if (This->ownerPropertyIndex)
4731 readSuccessful = StorageImpl_ReadProperty(
4732 This->parentStorage,
4733 This->ownerPropertyIndex,
4734 &chainProperty);
4736 if (readSuccessful)
4738 return chainProperty.startingBlock;
4743 return BLOCK_END_OF_CHAIN;
4746 /******************************************************************************
4747 * SmallBlockChainStream_GetNextBlockInChain
4749 * Returns the index of the next small block in this chain.
4751 * Return Values:
4752 * - BLOCK_END_OF_CHAIN: end of this chain
4753 * - BLOCK_UNUSED: small block 'blockIndex' is free
4755 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4756 SmallBlockChainStream* This,
4757 ULONG blockIndex,
4758 ULONG* nextBlockInChain)
4760 ULARGE_INTEGER offsetOfBlockInDepot;
4761 DWORD buffer;
4762 ULONG bytesRead;
4763 BOOL success;
4765 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4767 offsetOfBlockInDepot.u.HighPart = 0;
4768 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4771 * Read those bytes in the buffer from the small block file.
4773 success = BlockChainStream_ReadAt(
4774 This->parentStorage->smallBlockDepotChain,
4775 offsetOfBlockInDepot,
4776 sizeof(DWORD),
4777 &buffer,
4778 &bytesRead);
4780 if (success)
4782 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4783 return S_OK;
4786 return STG_E_READFAULT;
4789 /******************************************************************************
4790 * SmallBlockChainStream_SetNextBlockInChain
4792 * Writes the index of the next block of the specified block in the small
4793 * block depot.
4794 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4795 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4797 void SmallBlockChainStream_SetNextBlockInChain(
4798 SmallBlockChainStream* This,
4799 ULONG blockIndex,
4800 ULONG nextBlock)
4802 ULARGE_INTEGER offsetOfBlockInDepot;
4803 DWORD buffer;
4804 ULONG bytesWritten;
4806 offsetOfBlockInDepot.u.HighPart = 0;
4807 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4809 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4812 * Read those bytes in the buffer from the small block file.
4814 BlockChainStream_WriteAt(
4815 This->parentStorage->smallBlockDepotChain,
4816 offsetOfBlockInDepot,
4817 sizeof(DWORD),
4818 &buffer,
4819 &bytesWritten);
4822 /******************************************************************************
4823 * SmallBlockChainStream_FreeBlock
4825 * Flag small block 'blockIndex' as free in the small block depot.
4827 void SmallBlockChainStream_FreeBlock(
4828 SmallBlockChainStream* This,
4829 ULONG blockIndex)
4831 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4834 /******************************************************************************
4835 * SmallBlockChainStream_GetNextFreeBlock
4837 * Returns the index of a free small block. The small block depot will be
4838 * enlarged if necessary. The small block chain will also be enlarged if
4839 * necessary.
4841 ULONG SmallBlockChainStream_GetNextFreeBlock(
4842 SmallBlockChainStream* This)
4844 ULARGE_INTEGER offsetOfBlockInDepot;
4845 DWORD buffer;
4846 ULONG bytesRead;
4847 ULONG blockIndex = 0;
4848 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4849 BOOL success = TRUE;
4850 ULONG smallBlocksPerBigBlock;
4852 offsetOfBlockInDepot.u.HighPart = 0;
4855 * Scan the small block depot for a free block
4857 while (nextBlockIndex != BLOCK_UNUSED)
4859 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4861 success = BlockChainStream_ReadAt(
4862 This->parentStorage->smallBlockDepotChain,
4863 offsetOfBlockInDepot,
4864 sizeof(DWORD),
4865 &buffer,
4866 &bytesRead);
4869 * If we run out of space for the small block depot, enlarge it
4871 if (success)
4873 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4875 if (nextBlockIndex != BLOCK_UNUSED)
4876 blockIndex++;
4878 else
4880 ULONG count =
4881 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4883 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4884 ULONG nextBlock, newsbdIndex;
4885 BYTE* smallBlockDepot;
4887 nextBlock = sbdIndex;
4888 while (nextBlock != BLOCK_END_OF_CHAIN)
4890 sbdIndex = nextBlock;
4891 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4894 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4895 if (sbdIndex != BLOCK_END_OF_CHAIN)
4896 StorageImpl_SetNextBlockInChain(
4897 This->parentStorage,
4898 sbdIndex,
4899 newsbdIndex);
4901 StorageImpl_SetNextBlockInChain(
4902 This->parentStorage,
4903 newsbdIndex,
4904 BLOCK_END_OF_CHAIN);
4907 * Initialize all the small blocks to free
4909 smallBlockDepot =
4910 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4912 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4913 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4915 if (count == 0)
4918 * We have just created the small block depot.
4920 StgProperty rootProp;
4921 ULONG sbStartIndex;
4924 * Save it in the header
4926 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4927 StorageImpl_SaveFileHeader(This->parentStorage);
4930 * And allocate the first big block that will contain small blocks
4932 sbStartIndex =
4933 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4935 StorageImpl_SetNextBlockInChain(
4936 This->parentStorage,
4937 sbStartIndex,
4938 BLOCK_END_OF_CHAIN);
4940 StorageImpl_ReadProperty(
4941 This->parentStorage,
4942 This->parentStorage->rootPropertySetIndex,
4943 &rootProp);
4945 rootProp.startingBlock = sbStartIndex;
4946 rootProp.size.u.HighPart = 0;
4947 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
4949 StorageImpl_WriteProperty(
4950 This->parentStorage,
4951 This->parentStorage->rootPropertySetIndex,
4952 &rootProp);
4957 smallBlocksPerBigBlock =
4958 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4961 * Verify if we have to allocate big blocks to contain small blocks
4963 if (blockIndex % smallBlocksPerBigBlock == 0)
4965 StgProperty rootProp;
4966 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4968 StorageImpl_ReadProperty(
4969 This->parentStorage,
4970 This->parentStorage->rootPropertySetIndex,
4971 &rootProp);
4973 if (rootProp.size.u.LowPart <
4974 (blocksRequired * This->parentStorage->bigBlockSize))
4976 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
4978 BlockChainStream_SetSize(
4979 This->parentStorage->smallBlockRootChain,
4980 rootProp.size);
4982 StorageImpl_WriteProperty(
4983 This->parentStorage,
4984 This->parentStorage->rootPropertySetIndex,
4985 &rootProp);
4989 return blockIndex;
4992 /******************************************************************************
4993 * SmallBlockChainStream_ReadAt
4995 * Reads a specified number of bytes from this chain at the specified offset.
4996 * bytesRead may be NULL.
4997 * Failure will be returned if the specified number of bytes has not been read.
4999 BOOL SmallBlockChainStream_ReadAt(
5000 SmallBlockChainStream* This,
5001 ULARGE_INTEGER offset,
5002 ULONG size,
5003 void* buffer,
5004 ULONG* bytesRead)
5006 ULARGE_INTEGER offsetInBigBlockFile;
5007 ULONG blockNoInSequence =
5008 offset.u.LowPart / This->parentStorage->smallBlockSize;
5010 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5011 ULONG bytesToReadInBuffer;
5012 ULONG blockIndex;
5013 ULONG bytesReadFromBigBlockFile;
5014 BYTE* bufferWalker;
5017 * This should never happen on a small block file.
5019 assert(offset.u.HighPart==0);
5022 * Find the first block in the stream that contains part of the buffer.
5024 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5026 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5028 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5029 &blockIndex)))
5030 return FALSE;
5031 blockNoInSequence--;
5035 * Start reading the buffer.
5037 *bytesRead = 0;
5038 bufferWalker = buffer;
5040 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5043 * Calculate how many bytes we can copy from this small block.
5045 bytesToReadInBuffer =
5046 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5049 * Calculate the offset of the small block in the small block file.
5051 offsetInBigBlockFile.u.HighPart = 0;
5052 offsetInBigBlockFile.u.LowPart =
5053 blockIndex * This->parentStorage->smallBlockSize;
5055 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5058 * Read those bytes in the buffer from the small block file.
5060 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5061 offsetInBigBlockFile,
5062 bytesToReadInBuffer,
5063 bufferWalker,
5064 &bytesReadFromBigBlockFile);
5066 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5069 * Step to the next big block.
5071 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5072 return FALSE;
5073 bufferWalker += bytesToReadInBuffer;
5074 size -= bytesToReadInBuffer;
5075 *bytesRead += bytesToReadInBuffer;
5076 offsetInBlock = 0; /* There is no offset on the next block */
5079 return (size == 0);
5082 /******************************************************************************
5083 * SmallBlockChainStream_WriteAt
5085 * Writes the specified number of bytes to this chain at the specified offset.
5086 * bytesWritten may be NULL.
5087 * Will fail if not all specified number of bytes have been written.
5089 BOOL SmallBlockChainStream_WriteAt(
5090 SmallBlockChainStream* This,
5091 ULARGE_INTEGER offset,
5092 ULONG size,
5093 const void* buffer,
5094 ULONG* bytesWritten)
5096 ULARGE_INTEGER offsetInBigBlockFile;
5097 ULONG blockNoInSequence =
5098 offset.u.LowPart / This->parentStorage->smallBlockSize;
5100 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5101 ULONG bytesToWriteInBuffer;
5102 ULONG blockIndex;
5103 ULONG bytesWrittenFromBigBlockFile;
5104 BYTE* bufferWalker;
5107 * This should never happen on a small block file.
5109 assert(offset.u.HighPart==0);
5112 * Find the first block in the stream that contains part of the buffer.
5114 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5116 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5118 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5119 return FALSE;
5120 blockNoInSequence--;
5124 * Start writing the buffer.
5126 * Here, I'm casting away the constness on the buffer variable
5127 * This is OK since we don't intend to modify that buffer.
5129 *bytesWritten = 0;
5130 bufferWalker = (BYTE*)buffer;
5131 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5134 * Calculate how many bytes we can copy to this small block.
5136 bytesToWriteInBuffer =
5137 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5140 * Calculate the offset of the small block in the small block file.
5142 offsetInBigBlockFile.u.HighPart = 0;
5143 offsetInBigBlockFile.u.LowPart =
5144 blockIndex * This->parentStorage->smallBlockSize;
5146 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5149 * Write those bytes in the buffer to the small block file.
5151 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5152 offsetInBigBlockFile,
5153 bytesToWriteInBuffer,
5154 bufferWalker,
5155 &bytesWrittenFromBigBlockFile);
5157 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5160 * Step to the next big block.
5162 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5163 &blockIndex)))
5164 return FALSE;
5165 bufferWalker += bytesToWriteInBuffer;
5166 size -= bytesToWriteInBuffer;
5167 *bytesWritten += bytesToWriteInBuffer;
5168 offsetInBlock = 0; /* There is no offset on the next block */
5171 return (size == 0);
5174 /******************************************************************************
5175 * SmallBlockChainStream_Shrink
5177 * Shrinks this chain in the small block depot.
5179 BOOL SmallBlockChainStream_Shrink(
5180 SmallBlockChainStream* This,
5181 ULARGE_INTEGER newSize)
5183 ULONG blockIndex, extraBlock;
5184 ULONG numBlocks;
5185 ULONG count = 0;
5187 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5189 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5190 numBlocks++;
5192 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5195 * Go to the new end of chain
5197 while (count < numBlocks)
5199 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5200 &blockIndex)))
5201 return FALSE;
5202 count++;
5206 * If the count is 0, we have a special case, the head of the chain was
5207 * just freed.
5209 if (count == 0)
5211 StgProperty chainProp;
5213 StorageImpl_ReadProperty(This->parentStorage,
5214 This->ownerPropertyIndex,
5215 &chainProp);
5217 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5219 StorageImpl_WriteProperty(This->parentStorage,
5220 This->ownerPropertyIndex,
5221 &chainProp);
5224 * We start freeing the chain at the head block.
5226 extraBlock = blockIndex;
5228 else
5230 /* Get the next block before marking the new end */
5231 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5232 &extraBlock)))
5233 return FALSE;
5235 /* Mark the new end of chain */
5236 SmallBlockChainStream_SetNextBlockInChain(
5237 This,
5238 blockIndex,
5239 BLOCK_END_OF_CHAIN);
5243 * Mark the extra blocks as free
5245 while (extraBlock != BLOCK_END_OF_CHAIN)
5247 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5248 &blockIndex)))
5249 return FALSE;
5250 SmallBlockChainStream_FreeBlock(This, extraBlock);
5251 extraBlock = blockIndex;
5254 return TRUE;
5257 /******************************************************************************
5258 * SmallBlockChainStream_Enlarge
5260 * Grows this chain in the small block depot.
5262 BOOL SmallBlockChainStream_Enlarge(
5263 SmallBlockChainStream* This,
5264 ULARGE_INTEGER newSize)
5266 ULONG blockIndex, currentBlock;
5267 ULONG newNumBlocks;
5268 ULONG oldNumBlocks = 0;
5270 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5273 * Empty chain
5275 if (blockIndex == BLOCK_END_OF_CHAIN)
5278 StgProperty chainProp;
5280 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5281 &chainProp);
5283 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5285 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5286 &chainProp);
5288 blockIndex = chainProp.startingBlock;
5289 SmallBlockChainStream_SetNextBlockInChain(
5290 This,
5291 blockIndex,
5292 BLOCK_END_OF_CHAIN);
5295 currentBlock = blockIndex;
5298 * Figure out how many blocks are needed to contain this stream
5300 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5302 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5303 newNumBlocks++;
5306 * Go to the current end of chain
5308 while (blockIndex != BLOCK_END_OF_CHAIN)
5310 oldNumBlocks++;
5311 currentBlock = blockIndex;
5312 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5313 return FALSE;
5317 * Add new blocks to the chain
5319 while (oldNumBlocks < newNumBlocks)
5321 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5322 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5324 SmallBlockChainStream_SetNextBlockInChain(
5325 This,
5326 blockIndex,
5327 BLOCK_END_OF_CHAIN);
5329 currentBlock = blockIndex;
5330 oldNumBlocks++;
5333 return TRUE;
5336 /******************************************************************************
5337 * SmallBlockChainStream_GetCount
5339 * Returns the number of blocks that comprises this chain.
5340 * This is not the size of this chain as the last block may not be full!
5342 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5344 ULONG blockIndex;
5345 ULONG count = 0;
5347 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5349 while (blockIndex != BLOCK_END_OF_CHAIN)
5351 count++;
5353 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5354 return 0;
5357 return count;
5360 /******************************************************************************
5361 * SmallBlockChainStream_SetSize
5363 * Sets the size of this stream.
5364 * The file will grow if we grow the chain.
5366 * TODO: Free the actual blocks in the file when we shrink the chain.
5367 * Currently, the blocks are still in the file. So the file size
5368 * doesn't shrink even if we shrink streams.
5370 BOOL SmallBlockChainStream_SetSize(
5371 SmallBlockChainStream* This,
5372 ULARGE_INTEGER newSize)
5374 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5376 if (newSize.u.LowPart == size.u.LowPart)
5377 return TRUE;
5379 if (newSize.u.LowPart < size.u.LowPart)
5381 SmallBlockChainStream_Shrink(This, newSize);
5383 else
5385 SmallBlockChainStream_Enlarge(This, newSize);
5388 return TRUE;
5391 /******************************************************************************
5392 * SmallBlockChainStream_GetSize
5394 * Returns the size of this chain.
5396 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5398 StgProperty chainProperty;
5400 StorageImpl_ReadProperty(
5401 This->parentStorage,
5402 This->ownerPropertyIndex,
5403 &chainProperty);
5405 return chainProperty.size;
5408 /******************************************************************************
5409 * StgCreateDocfile [OLE32.@]
5411 HRESULT WINAPI StgCreateDocfile(
5412 LPCOLESTR pwcsName,
5413 DWORD grfMode,
5414 DWORD reserved,
5415 IStorage **ppstgOpen)
5417 StorageImpl* newStorage = 0;
5418 HANDLE hFile = INVALID_HANDLE_VALUE;
5419 HRESULT hr = S_OK;
5420 DWORD shareMode;
5421 DWORD accessMode;
5422 DWORD creationMode;
5423 DWORD fileAttributes;
5424 WCHAR tempFileName[MAX_PATH];
5426 TRACE("(%s, %lx, %ld, %p)\n",
5427 debugstr_w(pwcsName), grfMode,
5428 reserved, ppstgOpen);
5431 * Validate the parameters
5433 if (ppstgOpen == 0)
5434 return STG_E_INVALIDPOINTER;
5437 * Validate the STGM flags
5439 if ( FAILED( validateSTGM(grfMode) ))
5440 return STG_E_INVALIDFLAG;
5443 * Generate a unique name.
5445 if (pwcsName == 0)
5447 WCHAR tempPath[MAX_PATH];
5448 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5450 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5451 return STG_E_INVALIDFLAG;
5452 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5453 return STG_E_INVALIDFLAG;
5455 memset(tempPath, 0, sizeof(tempPath));
5456 memset(tempFileName, 0, sizeof(tempFileName));
5458 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5459 tempPath[0] = '.';
5461 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5462 pwcsName = tempFileName;
5463 else
5464 return STG_E_INSUFFICIENTMEMORY;
5466 creationMode = TRUNCATE_EXISTING;
5468 else
5470 creationMode = GetCreationModeFromSTGM(grfMode);
5474 * Interpret the STGM value grfMode
5476 shareMode = GetShareModeFromSTGM(grfMode);
5477 accessMode = GetAccessModeFromSTGM(grfMode);
5479 if (grfMode & STGM_DELETEONRELEASE)
5480 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5481 else
5482 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5484 if (grfMode & STGM_TRANSACTED)
5485 FIXME("Transacted mode not implemented.\n");
5488 * Initialize the "out" parameter.
5490 *ppstgOpen = 0;
5492 hFile = CreateFileW(pwcsName,
5493 accessMode,
5494 shareMode,
5495 NULL,
5496 creationMode,
5497 fileAttributes,
5500 if (hFile == INVALID_HANDLE_VALUE)
5502 return E_FAIL;
5506 * Allocate and initialize the new IStorage32object.
5508 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5510 if (newStorage == 0)
5511 return STG_E_INSUFFICIENTMEMORY;
5513 hr = StorageImpl_Construct(
5514 newStorage,
5515 hFile,
5516 pwcsName,
5517 NULL,
5518 grfMode,
5519 TRUE,
5520 TRUE);
5522 if (FAILED(hr))
5524 HeapFree(GetProcessHeap(), 0, newStorage);
5525 return hr;
5529 * Get an "out" pointer for the caller.
5531 hr = StorageBaseImpl_QueryInterface(
5532 (IStorage*)newStorage,
5533 (REFIID)&IID_IStorage,
5534 (void**)ppstgOpen);
5536 return hr;
5539 /******************************************************************************
5540 * StgCreateStorageEx [OLE32.@]
5542 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5544 TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5545 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5546 return STG_E_UNIMPLEMENTEDFUNCTION;
5549 /******************************************************************************
5550 * StgOpenStorage [OLE32.@]
5552 HRESULT WINAPI StgOpenStorage(
5553 const OLECHAR *pwcsName,
5554 IStorage *pstgPriority,
5555 DWORD grfMode,
5556 SNB snbExclude,
5557 DWORD reserved,
5558 IStorage **ppstgOpen)
5560 StorageImpl* newStorage = 0;
5561 HRESULT hr = S_OK;
5562 HANDLE hFile = 0;
5563 DWORD shareMode;
5564 DWORD accessMode;
5565 WCHAR fullname[MAX_PATH];
5566 DWORD length;
5568 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5569 debugstr_w(pwcsName), pstgPriority, grfMode,
5570 snbExclude, reserved, ppstgOpen);
5573 * Perform a sanity check
5575 if (( pwcsName == 0) || (ppstgOpen == 0) )
5577 hr = STG_E_INVALIDPOINTER;
5578 goto end;
5582 * Validate the STGM flags
5584 if ( FAILED( validateSTGM(grfMode) ))
5586 hr = STG_E_INVALIDFLAG;
5587 goto end;
5591 * Interpret the STGM value grfMode
5593 shareMode = GetShareModeFromSTGM(grfMode);
5594 accessMode = GetAccessModeFromSTGM(grfMode);
5597 * Initialize the "out" parameter.
5599 *ppstgOpen = 0;
5601 hFile = CreateFileW( pwcsName,
5602 accessMode,
5603 shareMode,
5604 NULL,
5605 OPEN_EXISTING,
5606 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5609 length = GetFileSize(hFile, NULL);
5611 if (hFile==INVALID_HANDLE_VALUE)
5613 DWORD last_error = GetLastError();
5615 hr = E_FAIL;
5617 switch (last_error)
5619 case ERROR_FILE_NOT_FOUND:
5620 hr = STG_E_FILENOTFOUND;
5621 break;
5623 case ERROR_PATH_NOT_FOUND:
5624 hr = STG_E_PATHNOTFOUND;
5625 break;
5627 case ERROR_ACCESS_DENIED:
5628 case ERROR_WRITE_PROTECT:
5629 hr = STG_E_ACCESSDENIED;
5630 break;
5632 case ERROR_SHARING_VIOLATION:
5633 hr = STG_E_SHAREVIOLATION;
5634 break;
5636 default:
5637 hr = E_FAIL;
5640 goto end;
5644 * Allocate and initialize the new IStorage32object.
5646 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5648 if (newStorage == 0)
5650 hr = STG_E_INSUFFICIENTMEMORY;
5651 goto end;
5654 /* if the file's length was zero, initialize the storage */
5655 hr = StorageImpl_Construct(
5656 newStorage,
5657 hFile,
5658 pwcsName,
5659 NULL,
5660 grfMode,
5661 TRUE,
5662 !length );
5664 if (FAILED(hr))
5666 HeapFree(GetProcessHeap(), 0, newStorage);
5668 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5670 if(hr == STG_E_INVALIDHEADER)
5671 hr = STG_E_FILEALREADYEXISTS;
5672 goto end;
5675 /* prepare the file name string given in lieu of the root property name */
5676 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5677 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5678 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5681 * Get an "out" pointer for the caller.
5683 hr = StorageBaseImpl_QueryInterface(
5684 (IStorage*)newStorage,
5685 (REFIID)&IID_IStorage,
5686 (void**)ppstgOpen);
5688 end:
5689 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5690 return hr;
5693 /******************************************************************************
5694 * StgCreateDocfileOnILockBytes [OLE32.@]
5696 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5697 ILockBytes *plkbyt,
5698 DWORD grfMode,
5699 DWORD reserved,
5700 IStorage** ppstgOpen)
5702 StorageImpl* newStorage = 0;
5703 HRESULT hr = S_OK;
5706 * Validate the parameters
5708 if ((ppstgOpen == 0) || (plkbyt == 0))
5709 return STG_E_INVALIDPOINTER;
5712 * Allocate and initialize the new IStorage object.
5714 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5716 if (newStorage == 0)
5717 return STG_E_INSUFFICIENTMEMORY;
5719 hr = StorageImpl_Construct(
5720 newStorage,
5723 plkbyt,
5724 grfMode,
5725 FALSE,
5726 TRUE);
5728 if (FAILED(hr))
5730 HeapFree(GetProcessHeap(), 0, newStorage);
5731 return hr;
5735 * Get an "out" pointer for the caller.
5737 hr = StorageBaseImpl_QueryInterface(
5738 (IStorage*)newStorage,
5739 (REFIID)&IID_IStorage,
5740 (void**)ppstgOpen);
5742 return hr;
5745 /******************************************************************************
5746 * StgOpenStorageOnILockBytes [OLE32.@]
5748 HRESULT WINAPI StgOpenStorageOnILockBytes(
5749 ILockBytes *plkbyt,
5750 IStorage *pstgPriority,
5751 DWORD grfMode,
5752 SNB snbExclude,
5753 DWORD reserved,
5754 IStorage **ppstgOpen)
5756 StorageImpl* newStorage = 0;
5757 HRESULT hr = S_OK;
5760 * Perform a sanity check
5762 if ((plkbyt == 0) || (ppstgOpen == 0))
5763 return STG_E_INVALIDPOINTER;
5766 * Validate the STGM flags
5768 if ( FAILED( validateSTGM(grfMode) ))
5769 return STG_E_INVALIDFLAG;
5772 * Initialize the "out" parameter.
5774 *ppstgOpen = 0;
5777 * Allocate and initialize the new IStorage object.
5779 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5781 if (newStorage == 0)
5782 return STG_E_INSUFFICIENTMEMORY;
5784 hr = StorageImpl_Construct(
5785 newStorage,
5788 plkbyt,
5789 grfMode,
5790 FALSE,
5791 FALSE);
5793 if (FAILED(hr))
5795 HeapFree(GetProcessHeap(), 0, newStorage);
5796 return hr;
5800 * Get an "out" pointer for the caller.
5802 hr = StorageBaseImpl_QueryInterface(
5803 (IStorage*)newStorage,
5804 (REFIID)&IID_IStorage,
5805 (void**)ppstgOpen);
5807 return hr;
5810 /******************************************************************************
5811 * StgSetTimes [ole32.@]
5812 * StgSetTimes [OLE32.@]
5816 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *a,
5817 FILETIME const *b, FILETIME const *c )
5819 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5820 return S_OK;
5823 /******************************************************************************
5824 * StgIsStorageILockBytes [OLE32.@]
5826 * Determines if the ILockBytes contains a storage object.
5828 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5830 BYTE sig[8];
5831 ULARGE_INTEGER offset;
5833 offset.u.HighPart = 0;
5834 offset.u.LowPart = 0;
5836 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5838 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5839 return S_OK;
5841 return S_FALSE;
5844 /******************************************************************************
5845 * WriteClassStg [OLE32.@]
5847 * This method will store the specified CLSID in the specified storage object
5849 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5851 HRESULT hRes;
5853 assert(pStg != 0);
5855 hRes = IStorage_SetClass(pStg, rclsid);
5857 return hRes;
5860 /***********************************************************************
5861 * ReadClassStg (OLE32.@)
5863 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5865 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5867 STATSTG pstatstg;
5868 HRESULT hRes;
5870 TRACE("()\n");
5872 if(pclsid==NULL)
5873 return E_POINTER;
5875 * read a STATSTG structure (contains the clsid) from the storage
5877 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5879 if(SUCCEEDED(hRes))
5880 *pclsid=pstatstg.clsid;
5882 return hRes;
5885 /***********************************************************************
5886 * OleLoadFromStream (OLE32.@)
5888 * This function loads an object from stream
5890 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5892 CLSID clsid;
5893 HRESULT res;
5894 LPPERSISTSTREAM xstm;
5896 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5898 res=ReadClassStm(pStm,&clsid);
5899 if (!SUCCEEDED(res))
5900 return res;
5901 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5902 if (!SUCCEEDED(res))
5903 return res;
5904 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5905 if (!SUCCEEDED(res)) {
5906 IUnknown_Release((IUnknown*)*ppvObj);
5907 return res;
5909 res=IPersistStream_Load(xstm,pStm);
5910 IPersistStream_Release(xstm);
5911 /* FIXME: all refcounts ok at this point? I think they should be:
5912 * pStm : unchanged
5913 * ppvObj : 1
5914 * xstm : 0 (released)
5916 return res;
5919 /***********************************************************************
5920 * OleSaveToStream (OLE32.@)
5922 * This function saves an object with the IPersistStream interface on it
5923 * to the specified stream.
5925 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5928 CLSID clsid;
5929 HRESULT res;
5931 TRACE("(%p,%p)\n",pPStm,pStm);
5933 res=IPersistStream_GetClassID(pPStm,&clsid);
5935 if (SUCCEEDED(res)){
5937 res=WriteClassStm(pStm,&clsid);
5939 if (SUCCEEDED(res))
5941 res=IPersistStream_Save(pPStm,pStm,TRUE);
5944 TRACE("Finished Save\n");
5945 return res;
5948 /****************************************************************************
5949 * This method validate a STGM parameter that can contain the values below
5951 * STGM_DIRECT 0x00000000
5952 * STGM_TRANSACTED 0x00010000
5953 * STGM_SIMPLE 0x08000000
5955 * STGM_READ 0x00000000
5956 * STGM_WRITE 0x00000001
5957 * STGM_READWRITE 0x00000002
5959 * STGM_SHARE_DENY_NONE 0x00000040
5960 * STGM_SHARE_DENY_READ 0x00000030
5961 * STGM_SHARE_DENY_WRITE 0x00000020
5962 * STGM_SHARE_EXCLUSIVE 0x00000010
5964 * STGM_PRIORITY 0x00040000
5965 * STGM_DELETEONRELEASE 0x04000000
5967 * STGM_CREATE 0x00001000
5968 * STGM_CONVERT 0x00020000
5969 * STGM_FAILIFTHERE 0x00000000
5971 * STGM_NOSCRATCH 0x00100000
5972 * STGM_NOSNAPSHOT 0x00200000
5974 static HRESULT validateSTGM(DWORD stgm)
5976 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5977 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5978 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5980 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5981 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5982 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5984 BOOL bSTGM_SHARE_DENY_NONE =
5985 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5987 BOOL bSTGM_SHARE_DENY_READ =
5988 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5990 BOOL bSTGM_SHARE_DENY_WRITE =
5991 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5993 BOOL bSTGM_SHARE_EXCLUSIVE =
5994 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5996 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5997 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5999 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
6000 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
6003 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6005 if ( ! bSTGM_DIRECT )
6006 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
6007 return E_FAIL;
6010 * STGM_WRITE | STGM_READWRITE | STGM_READ
6012 if ( ! bSTGM_READ )
6013 if( bSTGM_WRITE && bSTGM_READWRITE )
6014 return E_FAIL;
6017 * STGM_SHARE_DENY_NONE | others
6018 * (I assume here that DENY_READ implies DENY_WRITE)
6020 if ( bSTGM_SHARE_DENY_NONE )
6021 if ( bSTGM_SHARE_DENY_READ ||
6022 bSTGM_SHARE_DENY_WRITE ||
6023 bSTGM_SHARE_EXCLUSIVE)
6024 return E_FAIL;
6027 * STGM_CREATE | STGM_CONVERT
6028 * if both are false, STGM_FAILIFTHERE is set to TRUE
6030 if ( bSTGM_CREATE && bSTGM_CONVERT )
6031 return E_FAIL;
6034 * STGM_NOSCRATCH requires STGM_TRANSACTED
6036 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6037 return E_FAIL;
6040 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6041 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6043 if (bSTGM_NOSNAPSHOT)
6045 if ( ! ( bSTGM_TRANSACTED &&
6046 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6047 return E_FAIL;
6050 return S_OK;
6053 /****************************************************************************
6054 * GetShareModeFromSTGM
6056 * This method will return a share mode flag from a STGM value.
6057 * The STGM value is assumed valid.
6059 static DWORD GetShareModeFromSTGM(DWORD stgm)
6061 DWORD dwShareMode = 0;
6062 BOOL bSTGM_SHARE_DENY_NONE =
6063 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6065 BOOL bSTGM_SHARE_DENY_READ =
6066 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6068 BOOL bSTGM_SHARE_DENY_WRITE =
6069 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6071 BOOL bSTGM_SHARE_EXCLUSIVE =
6072 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6074 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6075 dwShareMode = 0;
6077 if (bSTGM_SHARE_DENY_NONE)
6078 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6080 if (bSTGM_SHARE_DENY_WRITE)
6081 dwShareMode = FILE_SHARE_READ;
6083 return dwShareMode;
6086 /****************************************************************************
6087 * GetAccessModeFromSTGM
6089 * This method will return an access mode flag from a STGM value.
6090 * The STGM value is assumed valid.
6092 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6094 DWORD dwDesiredAccess = GENERIC_READ;
6095 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6096 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6097 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6099 if (bSTGM_READ)
6100 dwDesiredAccess = GENERIC_READ;
6102 if (bSTGM_WRITE)
6103 dwDesiredAccess |= GENERIC_WRITE;
6105 if (bSTGM_READWRITE)
6106 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6108 return dwDesiredAccess;
6111 /****************************************************************************
6112 * GetCreationModeFromSTGM
6114 * This method will return a creation mode flag from a STGM value.
6115 * The STGM value is assumed valid.
6117 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6119 if ( stgm & STGM_CREATE)
6120 return CREATE_ALWAYS;
6121 if (stgm & STGM_CONVERT) {
6122 FIXME("STGM_CONVERT not implemented!\n");
6123 return CREATE_NEW;
6125 /* All other cases */
6126 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6127 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6128 return CREATE_NEW;
6132 /*************************************************************************
6133 * OLECONVERT_LoadOLE10 [Internal]
6135 * Loads the OLE10 STREAM to memory
6137 * PARAMS
6138 * pOleStream [I] The OLESTREAM
6139 * pData [I] Data Structure for the OLESTREAM Data
6141 * RETURNS
6142 * Success: S_OK
6143 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6144 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6146 * NOTES
6147 * This function is used by OleConvertOLESTREAMToIStorage only.
6149 * Memory allocated for pData must be freed by the caller
6151 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6153 DWORD dwSize;
6154 HRESULT hRes = S_OK;
6155 int nTryCnt=0;
6156 int max_try = 6;
6158 pData->pData = NULL;
6159 pData->pstrOleObjFileName = (CHAR *) NULL;
6161 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6163 /* Get the OleID */
6164 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6165 if(dwSize != sizeof(pData->dwOleID))
6167 hRes = CONVERT10_E_OLESTREAM_GET;
6169 else if(pData->dwOleID != OLESTREAM_ID)
6171 hRes = CONVERT10_E_OLESTREAM_FMT;
6173 else
6175 hRes = S_OK;
6176 break;
6180 if(hRes == S_OK)
6182 /* Get the TypeID...more info needed for this field */
6183 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6184 if(dwSize != sizeof(pData->dwTypeID))
6186 hRes = CONVERT10_E_OLESTREAM_GET;
6189 if(hRes == S_OK)
6191 if(pData->dwTypeID != 0)
6193 /* Get the length of the OleTypeName */
6194 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6195 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6197 hRes = CONVERT10_E_OLESTREAM_GET;
6200 if(hRes == S_OK)
6202 if(pData->dwOleTypeNameLength > 0)
6204 /* Get the OleTypeName */
6205 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6206 if(dwSize != pData->dwOleTypeNameLength)
6208 hRes = CONVERT10_E_OLESTREAM_GET;
6212 if(bStrem1)
6214 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6215 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6217 hRes = CONVERT10_E_OLESTREAM_GET;
6219 if(hRes == S_OK)
6221 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6222 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6223 pData->pstrOleObjFileName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6224 if(pData->pstrOleObjFileName)
6226 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6227 if(dwSize != pData->dwOleObjFileNameLength)
6229 hRes = CONVERT10_E_OLESTREAM_GET;
6232 else
6233 hRes = CONVERT10_E_OLESTREAM_GET;
6236 else
6238 /* Get the Width of the Metafile */
6239 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6240 if(dwSize != sizeof(pData->dwMetaFileWidth))
6242 hRes = CONVERT10_E_OLESTREAM_GET;
6244 if(hRes == S_OK)
6246 /* Get the Height of the Metafile */
6247 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6248 if(dwSize != sizeof(pData->dwMetaFileHeight))
6250 hRes = CONVERT10_E_OLESTREAM_GET;
6254 if(hRes == S_OK)
6256 /* Get the Length of the Data */
6257 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6258 if(dwSize != sizeof(pData->dwDataLength))
6260 hRes = CONVERT10_E_OLESTREAM_GET;
6264 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6266 if(!bStrem1) /* if it is a second OLE stream data */
6268 pData->dwDataLength -= 8;
6269 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6270 if(dwSize != sizeof(pData->strUnknown))
6272 hRes = CONVERT10_E_OLESTREAM_GET;
6276 if(hRes == S_OK)
6278 if(pData->dwDataLength > 0)
6280 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6282 /* Get Data (ex. IStorage, Metafile, or BMP) */
6283 if(pData->pData)
6285 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6286 if(dwSize != pData->dwDataLength)
6288 hRes = CONVERT10_E_OLESTREAM_GET;
6291 else
6293 hRes = CONVERT10_E_OLESTREAM_GET;
6299 return hRes;
6302 /*************************************************************************
6303 * OLECONVERT_SaveOLE10 [Internal]
6305 * Saves the OLE10 STREAM From memory
6307 * PARAMS
6308 * pData [I] Data Structure for the OLESTREAM Data
6309 * pOleStream [I] The OLESTREAM to save
6311 * RETURNS
6312 * Success: S_OK
6313 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6315 * NOTES
6316 * This function is used by OleConvertIStorageToOLESTREAM only.
6319 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6321 DWORD dwSize;
6322 HRESULT hRes = S_OK;
6325 /* Set the OleID */
6326 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6327 if(dwSize != sizeof(pData->dwOleID))
6329 hRes = CONVERT10_E_OLESTREAM_PUT;
6332 if(hRes == S_OK)
6334 /* Set the TypeID */
6335 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6336 if(dwSize != sizeof(pData->dwTypeID))
6338 hRes = CONVERT10_E_OLESTREAM_PUT;
6342 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6344 /* Set the Length of the OleTypeName */
6345 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6346 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6348 hRes = CONVERT10_E_OLESTREAM_PUT;
6351 if(hRes == S_OK)
6353 if(pData->dwOleTypeNameLength > 0)
6355 /* Set the OleTypeName */
6356 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6357 if(dwSize != pData->dwOleTypeNameLength)
6359 hRes = CONVERT10_E_OLESTREAM_PUT;
6364 if(hRes == S_OK)
6366 /* Set the width of the Metafile */
6367 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6368 if(dwSize != sizeof(pData->dwMetaFileWidth))
6370 hRes = CONVERT10_E_OLESTREAM_PUT;
6374 if(hRes == S_OK)
6376 /* Set the height of the Metafile */
6377 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6378 if(dwSize != sizeof(pData->dwMetaFileHeight))
6380 hRes = CONVERT10_E_OLESTREAM_PUT;
6384 if(hRes == S_OK)
6386 /* Set the length of the Data */
6387 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6388 if(dwSize != sizeof(pData->dwDataLength))
6390 hRes = CONVERT10_E_OLESTREAM_PUT;
6394 if(hRes == S_OK)
6396 if(pData->dwDataLength > 0)
6398 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6399 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6400 if(dwSize != pData->dwDataLength)
6402 hRes = CONVERT10_E_OLESTREAM_PUT;
6407 return hRes;
6410 /*************************************************************************
6411 * OLECONVERT_GetOLE20FromOLE10[Internal]
6413 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6414 * opens it, and copies the content to the dest IStorage for
6415 * OleConvertOLESTREAMToIStorage
6418 * PARAMS
6419 * pDestStorage [I] The IStorage to copy the data to
6420 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6421 * nBufferLength [I] The size of the buffer
6423 * RETURNS
6424 * Nothing
6426 * NOTES
6430 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6432 HRESULT hRes;
6433 HANDLE hFile;
6434 IStorage *pTempStorage;
6435 DWORD dwNumOfBytesWritten;
6436 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6437 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6439 /* Create a temp File */
6440 GetTempPathW(MAX_PATH, wstrTempDir);
6441 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6442 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6444 if(hFile != INVALID_HANDLE_VALUE)
6446 /* Write IStorage Data to File */
6447 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6448 CloseHandle(hFile);
6450 /* Open and copy temp storage to the Dest Storage */
6451 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6452 if(hRes == S_OK)
6454 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6455 StorageBaseImpl_Release(pTempStorage);
6457 DeleteFileW(wstrTempFile);
6462 /*************************************************************************
6463 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6465 * Saves the OLE10 STREAM From memory
6467 * PARAMS
6468 * pStorage [I] The Src IStorage to copy
6469 * pData [I] The Dest Memory to write to.
6471 * RETURNS
6472 * The size in bytes allocated for pData
6474 * NOTES
6475 * Memory allocated for pData must be freed by the caller
6477 * Used by OleConvertIStorageToOLESTREAM only.
6480 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6482 HANDLE hFile;
6483 HRESULT hRes;
6484 DWORD nDataLength = 0;
6485 IStorage *pTempStorage;
6486 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6487 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6489 *pData = NULL;
6491 /* Create temp Storage */
6492 GetTempPathW(MAX_PATH, wstrTempDir);
6493 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6494 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6496 if(hRes == S_OK)
6498 /* Copy Src Storage to the Temp Storage */
6499 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6500 StorageBaseImpl_Release(pTempStorage);
6502 /* Open Temp Storage as a file and copy to memory */
6503 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6504 if(hFile != INVALID_HANDLE_VALUE)
6506 nDataLength = GetFileSize(hFile, NULL);
6507 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6508 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6509 CloseHandle(hFile);
6511 DeleteFileW(wstrTempFile);
6513 return nDataLength;
6516 /*************************************************************************
6517 * OLECONVERT_CreateOleStream [Internal]
6519 * Creates the "\001OLE" stream in the IStorage if necessary.
6521 * PARAMS
6522 * pStorage [I] Dest storage to create the stream in
6524 * RETURNS
6525 * Nothing
6527 * NOTES
6528 * This function is used by OleConvertOLESTREAMToIStorage only.
6530 * This stream is still unknown, MS Word seems to have extra data
6531 * but since the data is stored in the OLESTREAM there should be
6532 * no need to recreate the stream. If the stream is manually
6533 * deleted it will create it with this default data.
6536 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6538 HRESULT hRes;
6539 IStream *pStream;
6540 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6541 BYTE pOleStreamHeader [] =
6543 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6544 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6545 0x00, 0x00, 0x00, 0x00
6548 /* Create stream if not present */
6549 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6550 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6552 if(hRes == S_OK)
6554 /* Write default Data */
6555 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6556 IStream_Release(pStream);
6560 /* write a string to a stream, preceded by its length */
6561 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6563 HRESULT r;
6564 LPSTR str;
6565 DWORD len = 0;
6567 if( string )
6568 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6569 r = IStream_Write( stm, &len, sizeof(len), NULL);
6570 if( FAILED( r ) )
6571 return r;
6572 if(len == 0)
6573 return r;
6574 str = CoTaskMemAlloc( len );
6575 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6576 r = IStream_Write( stm, str, len, NULL);
6577 CoTaskMemFree( str );
6578 return r;
6581 /* read a string preceded by its length from a stream */
6582 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6584 HRESULT r;
6585 DWORD len, count = 0;
6586 LPSTR str;
6587 LPWSTR wstr;
6589 r = IStream_Read( stm, &len, sizeof(len), &count );
6590 if( FAILED( r ) )
6591 return r;
6592 if( count != sizeof(len) )
6593 return E_OUTOFMEMORY;
6595 TRACE("%ld bytes\n",len);
6597 str = CoTaskMemAlloc( len );
6598 if( !str )
6599 return E_OUTOFMEMORY;
6600 count = 0;
6601 r = IStream_Read( stm, str, len, &count );
6602 if( FAILED( r ) )
6603 return r;
6604 if( count != len )
6606 CoTaskMemFree( str );
6607 return E_OUTOFMEMORY;
6610 TRACE("Read string %s\n",debugstr_an(str,len));
6612 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6613 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6614 if( wstr )
6615 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6616 CoTaskMemFree( str );
6618 *string = wstr;
6620 return r;
6624 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6625 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6627 IStream *pstm;
6628 HRESULT r = S_OK;
6629 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6631 static const BYTE unknown1[12] =
6632 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6633 0xFF, 0xFF, 0xFF, 0xFF};
6634 static const BYTE unknown2[16] =
6635 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6636 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6638 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6639 debugstr_w(lpszUserType), debugstr_w(szClipName),
6640 debugstr_w(szProgIDName));
6642 /* Create a CompObj stream if it doesn't exist */
6643 r = IStorage_CreateStream(pstg, szwStreamName,
6644 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6645 if( FAILED (r) )
6646 return r;
6648 /* Write CompObj Structure to stream */
6649 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6651 if( SUCCEEDED( r ) )
6652 r = WriteClassStm( pstm, clsid );
6654 if( SUCCEEDED( r ) )
6655 r = STREAM_WriteString( pstm, lpszUserType );
6656 if( SUCCEEDED( r ) )
6657 r = STREAM_WriteString( pstm, szClipName );
6658 if( SUCCEEDED( r ) )
6659 r = STREAM_WriteString( pstm, szProgIDName );
6660 if( SUCCEEDED( r ) )
6661 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6663 IStream_Release( pstm );
6665 return r;
6668 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6669 static HRESULT CLSIDFromUserType(LPCWSTR lpszUserType, CLSID *clsid)
6671 LONG r, count, i, len;
6672 WCHAR szKey[0x40];
6673 HKEY hkey, hkeyclsid;
6674 LPWSTR buffer = NULL;
6675 BOOL found = FALSE;
6676 static const WCHAR szclsid[] = { 'C','L','S','I','D',0 };
6678 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType));
6680 r = RegOpenKeyW( HKEY_CLASSES_ROOT, szclsid, &hkeyclsid );
6681 if( r )
6682 return E_INVALIDARG;
6684 len = lstrlenW( lpszUserType ) + 1;
6685 buffer = CoTaskMemAlloc( len * sizeof (WCHAR) );
6686 if( !buffer )
6687 goto end;
6689 for(i=0; !found; i++ )
6691 r = RegEnumKeyW( hkeyclsid, i, szKey, sizeof(szKey)/sizeof(WCHAR));
6692 if( r != ERROR_SUCCESS )
6693 break;
6694 hkey = 0;
6695 r = RegOpenKeyW( hkeyclsid, szKey, &hkey );
6696 if( r != ERROR_SUCCESS )
6697 break;
6698 count = len * sizeof (WCHAR);
6699 r = RegQueryValueW( hkey, NULL, buffer, &count );
6700 found = ( r == ERROR_SUCCESS ) &&
6701 ( count == len*sizeof(WCHAR) ) &&
6702 !lstrcmpW( buffer, lpszUserType ) ;
6703 RegCloseKey( hkey );
6706 end:
6707 if( buffer )
6708 CoTaskMemFree( buffer );
6709 RegCloseKey( hkeyclsid );
6711 if ( !found )
6712 return E_INVALIDARG;
6714 TRACE("clsid is %s\n", debugstr_w( szKey ) );
6716 r = CLSIDFromString( szKey, clsid );
6718 return r;
6722 /***********************************************************************
6723 * WriteFmtUserTypeStg (OLE32.@)
6725 HRESULT WINAPI WriteFmtUserTypeStg(
6726 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6728 HRESULT r;
6729 WCHAR szwClipName[0x40];
6730 WCHAR szCLSIDName[OLESTREAM_MAX_STR_LEN];
6731 CLSID clsid;
6732 LPWSTR wstrProgID;
6733 DWORD n;
6734 LPMALLOC allocator = NULL;
6736 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6738 r = CoGetMalloc(0, &allocator);
6739 if( FAILED( r) )
6740 return E_OUTOFMEMORY;
6742 /* get the clipboard format name */
6743 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6744 szwClipName[n]=0;
6746 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6748 /* Get the CLSID */
6749 szCLSIDName[0]=0;
6750 r = CLSIDFromUserType(lpszUserType, &clsid);
6751 if( FAILED( r ) )
6752 return r;
6754 TRACE("CLSID is %s\n",debugstr_guid(&clsid));
6756 /* get the real program ID */
6757 r = ProgIDFromCLSID( &clsid, &wstrProgID);
6758 if( FAILED( r ) )
6759 return r;
6761 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6763 /* if we have a good string, write the stream */
6764 if( wstrProgID )
6765 r = STORAGE_WriteCompObj( pstg, &clsid,
6766 lpszUserType, szwClipName, wstrProgID );
6767 else
6768 r = E_OUTOFMEMORY;
6770 IMalloc_Free( allocator, wstrProgID);
6772 return r;
6776 /******************************************************************************
6777 * ReadFmtUserTypeStg [OLE32.@]
6779 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6781 HRESULT r;
6782 IStream *stm = 0;
6783 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6784 unsigned char unknown1[12];
6785 unsigned char unknown2[16];
6786 DWORD count;
6787 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6788 CLSID clsid;
6790 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6792 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6793 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6794 if( FAILED ( r ) )
6796 ERR("Failed to open stream\n");
6797 return r;
6800 /* read the various parts of the structure */
6801 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6802 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6803 goto end;
6804 r = ReadClassStm( stm, &clsid );
6805 if( FAILED( r ) )
6806 goto end;
6808 r = STREAM_ReadString( stm, &szCLSIDName );
6809 if( FAILED( r ) )
6810 goto end;
6812 r = STREAM_ReadString( stm, &szOleTypeName );
6813 if( FAILED( r ) )
6814 goto end;
6816 r = STREAM_ReadString( stm, &szProgIDName );
6817 if( FAILED( r ) )
6818 goto end;
6820 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6821 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6822 goto end;
6824 /* ok, success... now we just need to store what we found */
6825 if( pcf )
6826 *pcf = RegisterClipboardFormatW( szOleTypeName );
6827 CoTaskMemFree( szOleTypeName );
6829 if( lplpszUserType )
6830 *lplpszUserType = szCLSIDName;
6831 CoTaskMemFree( szProgIDName );
6833 end:
6834 IStream_Release( stm );
6836 return r;
6840 /*************************************************************************
6841 * OLECONVERT_CreateCompObjStream [Internal]
6843 * Creates a "\001CompObj" is the destination IStorage if necessary.
6845 * PARAMS
6846 * pStorage [I] The dest IStorage to create the CompObj Stream
6847 * if necessary.
6848 * strOleTypeName [I] The ProgID
6850 * RETURNS
6851 * Success: S_OK
6852 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6854 * NOTES
6855 * This function is used by OleConvertOLESTREAMToIStorage only.
6857 * The stream data is stored in the OLESTREAM and there should be
6858 * no need to recreate the stream. If the stream is manually
6859 * deleted it will attempt to create it by querying the registry.
6863 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6865 IStream *pStream;
6866 HRESULT hStorageRes, hRes = S_OK;
6867 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6868 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6869 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
6871 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6872 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6874 /* Initialize the CompObj structure */
6875 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6876 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6877 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6880 /* Create a CompObj stream if it doesn't exist */
6881 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6882 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6883 if(hStorageRes == S_OK)
6885 /* copy the OleTypeName to the compobj struct */
6886 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6887 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6889 /* copy the OleTypeName to the compobj struct */
6890 /* Note: in the test made, these were Identical */
6891 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6892 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6894 /* Get the CLSID */
6895 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
6896 bufferW, OLESTREAM_MAX_STR_LEN );
6897 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
6899 if(hRes == S_OK)
6901 HKEY hKey;
6902 LONG hErr;
6903 /* Get the CLSID Default Name from the Registry */
6904 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6905 if(hErr == ERROR_SUCCESS)
6907 char strTemp[OLESTREAM_MAX_STR_LEN];
6908 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6909 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6910 if(hErr == ERROR_SUCCESS)
6912 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6914 RegCloseKey(hKey);
6918 /* Write CompObj Structure to stream */
6919 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6921 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6923 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6924 if(IStorageCompObj.dwCLSIDNameLength > 0)
6926 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6928 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6929 if(IStorageCompObj.dwOleTypeNameLength > 0)
6931 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6933 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6934 if(IStorageCompObj.dwProgIDNameLength > 0)
6936 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6938 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6939 IStream_Release(pStream);
6941 return hRes;
6945 /*************************************************************************
6946 * OLECONVERT_CreateOlePresStream[Internal]
6948 * Creates the "\002OlePres000" Stream with the Metafile data
6950 * PARAMS
6951 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6952 * dwExtentX [I] Width of the Metafile
6953 * dwExtentY [I] Height of the Metafile
6954 * pData [I] Metafile data
6955 * dwDataLength [I] Size of the Metafile data
6957 * RETURNS
6958 * Success: S_OK
6959 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6961 * NOTES
6962 * This function is used by OleConvertOLESTREAMToIStorage only.
6965 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6967 HRESULT hRes;
6968 IStream *pStream;
6969 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6970 BYTE pOlePresStreamHeader [] =
6972 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6973 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6974 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6975 0x00, 0x00, 0x00, 0x00
6978 BYTE pOlePresStreamHeaderEmpty [] =
6980 0x00, 0x00, 0x00, 0x00,
6981 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6982 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6983 0x00, 0x00, 0x00, 0x00
6986 /* Create the OlePres000 Stream */
6987 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6988 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6990 if(hRes == S_OK)
6992 DWORD nHeaderSize;
6993 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6995 memset(&OlePres, 0, sizeof(OlePres));
6996 /* Do we have any metafile data to save */
6997 if(dwDataLength > 0)
6999 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7000 nHeaderSize = sizeof(pOlePresStreamHeader);
7002 else
7004 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7005 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7007 /* Set width and height of the metafile */
7008 OlePres.dwExtentX = dwExtentX;
7009 OlePres.dwExtentY = -dwExtentY;
7011 /* Set Data and Length */
7012 if(dwDataLength > sizeof(METAFILEPICT16))
7014 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7015 OlePres.pData = &(pData[8]);
7017 /* Save OlePres000 Data to Stream */
7018 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7019 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7020 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7021 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7022 if(OlePres.dwSize > 0)
7024 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7026 IStream_Release(pStream);
7030 /*************************************************************************
7031 * OLECONVERT_CreateOle10NativeStream [Internal]
7033 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7035 * PARAMS
7036 * pStorage [I] Dest storage to create the stream in
7037 * pData [I] Ole10 Native Data (ex. bmp)
7038 * dwDataLength [I] Size of the Ole10 Native Data
7040 * RETURNS
7041 * Nothing
7043 * NOTES
7044 * This function is used by OleConvertOLESTREAMToIStorage only.
7046 * Might need to verify the data and return appropriate error message
7049 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7051 HRESULT hRes;
7052 IStream *pStream;
7053 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7055 /* Create the Ole10Native Stream */
7056 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7057 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7059 if(hRes == S_OK)
7061 /* Write info to stream */
7062 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7063 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7064 IStream_Release(pStream);
7069 /*************************************************************************
7070 * OLECONVERT_GetOLE10ProgID [Internal]
7072 * Finds the ProgID (or OleTypeID) from the IStorage
7074 * PARAMS
7075 * pStorage [I] The Src IStorage to get the ProgID
7076 * strProgID [I] the ProgID string to get
7077 * dwSize [I] the size of the string
7079 * RETURNS
7080 * Success: S_OK
7081 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7083 * NOTES
7084 * This function is used by OleConvertIStorageToOLESTREAM only.
7088 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7090 HRESULT hRes;
7091 IStream *pStream;
7092 LARGE_INTEGER iSeekPos;
7093 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7094 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7096 /* Open the CompObj Stream */
7097 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7098 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7099 if(hRes == S_OK)
7102 /*Get the OleType from the CompObj Stream */
7103 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7104 iSeekPos.u.HighPart = 0;
7106 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7107 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7108 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7109 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7110 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7111 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7112 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7114 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7115 if(*dwSize > 0)
7117 IStream_Read(pStream, strProgID, *dwSize, NULL);
7119 IStream_Release(pStream);
7121 else
7123 STATSTG stat;
7124 LPOLESTR wstrProgID;
7126 /* Get the OleType from the registry */
7127 REFCLSID clsid = &(stat.clsid);
7128 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7129 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7130 if(hRes == S_OK)
7132 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7136 return hRes;
7139 /*************************************************************************
7140 * OLECONVERT_GetOle10PresData [Internal]
7142 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7144 * PARAMS
7145 * pStorage [I] Src IStroage
7146 * pOleStream [I] Dest OleStream Mem Struct
7148 * RETURNS
7149 * Nothing
7151 * NOTES
7152 * This function is used by OleConvertIStorageToOLESTREAM only.
7154 * Memory allocated for pData must be freed by the caller
7158 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7161 HRESULT hRes;
7162 IStream *pStream;
7163 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7165 /* Initialize Default data for OLESTREAM */
7166 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7167 pOleStreamData[0].dwTypeID = 2;
7168 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7169 pOleStreamData[1].dwTypeID = 0;
7170 pOleStreamData[0].dwMetaFileWidth = 0;
7171 pOleStreamData[0].dwMetaFileHeight = 0;
7172 pOleStreamData[0].pData = NULL;
7173 pOleStreamData[1].pData = NULL;
7175 /* Open Ole10Native Stream */
7176 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7177 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7178 if(hRes == S_OK)
7181 /* Read Size and Data */
7182 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7183 if(pOleStreamData->dwDataLength > 0)
7185 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7186 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7188 IStream_Release(pStream);
7194 /*************************************************************************
7195 * OLECONVERT_GetOle20PresData[Internal]
7197 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7199 * PARAMS
7200 * pStorage [I] Src IStroage
7201 * pOleStreamData [I] Dest OleStream Mem Struct
7203 * RETURNS
7204 * Nothing
7206 * NOTES
7207 * This function is used by OleConvertIStorageToOLESTREAM only.
7209 * Memory allocated for pData must be freed by the caller
7211 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7213 HRESULT hRes;
7214 IStream *pStream;
7215 OLECONVERT_ISTORAGE_OLEPRES olePress;
7216 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7218 /* Initialize Default data for OLESTREAM */
7219 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7220 pOleStreamData[0].dwTypeID = 2;
7221 pOleStreamData[0].dwMetaFileWidth = 0;
7222 pOleStreamData[0].dwMetaFileHeight = 0;
7223 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7224 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7225 pOleStreamData[1].dwTypeID = 0;
7226 pOleStreamData[1].dwOleTypeNameLength = 0;
7227 pOleStreamData[1].strOleTypeName[0] = 0;
7228 pOleStreamData[1].dwMetaFileWidth = 0;
7229 pOleStreamData[1].dwMetaFileHeight = 0;
7230 pOleStreamData[1].pData = NULL;
7231 pOleStreamData[1].dwDataLength = 0;
7234 /* Open OlePress000 stream */
7235 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7236 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7237 if(hRes == S_OK)
7239 LARGE_INTEGER iSeekPos;
7240 METAFILEPICT16 MetaFilePict;
7241 static const char strMetafilePictName[] = "METAFILEPICT";
7243 /* Set the TypeID for a Metafile */
7244 pOleStreamData[1].dwTypeID = 5;
7246 /* Set the OleTypeName to Metafile */
7247 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7248 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7250 iSeekPos.u.HighPart = 0;
7251 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7253 /* Get Presentation Data */
7254 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7255 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7256 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7257 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7259 /*Set width and Height */
7260 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7261 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7262 if(olePress.dwSize > 0)
7264 /* Set Length */
7265 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7267 /* Set MetaFilePict struct */
7268 MetaFilePict.mm = 8;
7269 MetaFilePict.xExt = olePress.dwExtentX;
7270 MetaFilePict.yExt = olePress.dwExtentY;
7271 MetaFilePict.hMF = 0;
7273 /* Get Metafile Data */
7274 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7275 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7276 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7278 IStream_Release(pStream);
7282 /*************************************************************************
7283 * OleConvertOLESTREAMToIStorage [OLE32.@]
7285 * Read info on MSDN
7287 * TODO
7288 * DVTARGETDEVICE paramenter is not handled
7289 * Still unsure of some mem fields for OLE 10 Stream
7290 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7291 * and "\001OLE" streams
7294 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7295 LPOLESTREAM pOleStream,
7296 LPSTORAGE pstg,
7297 const DVTARGETDEVICE* ptd)
7299 int i;
7300 HRESULT hRes=S_OK;
7301 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7303 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7305 if(ptd != NULL)
7307 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7310 if(pstg == NULL || pOleStream == NULL)
7312 hRes = E_INVALIDARG;
7315 if(hRes == S_OK)
7317 /* Load the OLESTREAM to Memory */
7318 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7321 if(hRes == S_OK)
7323 /* Load the OLESTREAM to Memory (part 2)*/
7324 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7327 if(hRes == S_OK)
7330 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7332 /* Do we have the IStorage Data in the OLESTREAM */
7333 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7335 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7336 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7338 else
7340 /* It must be an original OLE 1.0 source */
7341 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7344 else
7346 /* It must be an original OLE 1.0 source */
7347 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7350 /* Create CompObj Stream if necessary */
7351 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7352 if(hRes == S_OK)
7354 /*Create the Ole Stream if necessary */
7355 OLECONVERT_CreateOleStream(pstg);
7360 /* Free allocated memory */
7361 for(i=0; i < 2; i++)
7363 if(pOleStreamData[i].pData != NULL)
7365 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7367 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7369 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7370 pOleStreamData[i].pstrOleObjFileName = NULL;
7373 return hRes;
7376 /*************************************************************************
7377 * OleConvertIStorageToOLESTREAM [OLE32.@]
7379 * Read info on MSDN
7381 * Read info on MSDN
7383 * TODO
7384 * Still unsure of some mem fields for OLE 10 Stream
7385 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7386 * and "\001OLE" streams.
7389 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7390 LPSTORAGE pstg,
7391 LPOLESTREAM pOleStream)
7393 int i;
7394 HRESULT hRes = S_OK;
7395 IStream *pStream;
7396 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7397 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7400 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7402 if(pstg == NULL || pOleStream == NULL)
7404 hRes = E_INVALIDARG;
7406 if(hRes == S_OK)
7408 /* Get the ProgID */
7409 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7410 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7412 if(hRes == S_OK)
7414 /* Was it originally Ole10 */
7415 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7416 if(hRes == S_OK)
7418 IStream_Release(pStream);
7419 /* Get Presentation Data for Ole10Native */
7420 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7422 else
7424 /* Get Presentation Data (OLE20) */
7425 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7428 /* Save OLESTREAM */
7429 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7430 if(hRes == S_OK)
7432 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7437 /* Free allocated memory */
7438 for(i=0; i < 2; i++)
7440 if(pOleStreamData[i].pData != NULL)
7442 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7446 return hRes;
7449 /***********************************************************************
7450 * GetConvertStg (OLE32.@)
7452 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7453 FIXME("unimplemented stub!\n");
7454 return E_FAIL;
7457 /******************************************************************************
7458 * StgIsStorageFile [OLE32.@]
7460 HRESULT WINAPI
7461 StgIsStorageFile(LPCOLESTR fn)
7463 HANDLE hf;
7464 BYTE magic[8];
7465 DWORD bytes_read;
7467 TRACE("(\'%s\')\n", debugstr_w(fn));
7468 hf = CreateFileW(fn, GENERIC_READ,
7469 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7470 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7472 if (hf == INVALID_HANDLE_VALUE)
7473 return STG_E_FILENOTFOUND;
7475 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7477 WARN(" unable to read file\n");
7478 CloseHandle(hf);
7479 return S_FALSE;
7482 CloseHandle(hf);
7484 if (bytes_read != 8) {
7485 WARN(" too short\n");
7486 return S_FALSE;
7489 if (!memcmp(magic,STORAGE_magic,8)) {
7490 WARN(" -> YES\n");
7491 return S_OK;
7494 WARN(" -> Invalid header.\n");
7495 return S_FALSE;