Added two printer error codes.
[wine/multimedia.git] / dlls / ole32 / storage32.c
bloba925b95790d37a15612bd22f44a7c7a8bd868b2f
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 <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
32 #define NONAMELESSUNION
33 #define NONAMELESSSTRUCT
34 #include "winbase.h" /* for lstrlenW() and the likes */
35 #include "winnls.h"
36 #include "winuser.h"
37 #include "wine/unicode.h"
38 #include "wine/debug.h"
40 #include "storage32.h"
41 #include "ole2.h" /* For Write/ReadClassStm */
43 #include "winreg.h"
44 #include "wine/wingdi16.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(storage);
48 #define FILE_BEGIN 0
51 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
52 #define OLESTREAM_ID 0x501
53 #define OLESTREAM_MAX_STR_LEN 255
55 static const char rootPropertyName[] = "Root Entry";
58 /* OLESTREAM memory structure to use for Get and Put Routines */
59 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
60 typedef struct
62 DWORD dwOleID;
63 DWORD dwTypeID;
64 DWORD dwOleTypeNameLength;
65 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
66 CHAR *pstrOleObjFileName;
67 DWORD dwOleObjFileNameLength;
68 DWORD dwMetaFileWidth;
69 DWORD dwMetaFileHeight;
70 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
71 DWORD dwDataLength;
72 BYTE *pData;
73 }OLECONVERT_OLESTREAM_DATA;
75 /* CompObj Stream structure */
76 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
77 typedef struct
79 BYTE byUnknown1[12];
80 CLSID clsid;
81 DWORD dwCLSIDNameLength;
82 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
83 DWORD dwOleTypeNameLength;
84 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
85 DWORD dwProgIDNameLength;
86 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
87 BYTE byUnknown2[16];
88 }OLECONVERT_ISTORAGE_COMPOBJ;
91 /* Ole Presention Stream structure */
92 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
93 typedef struct
95 BYTE byUnknown1[28];
96 DWORD dwExtentX;
97 DWORD dwExtentY;
98 DWORD dwSize;
99 BYTE *pData;
100 }OLECONVERT_ISTORAGE_OLEPRES;
104 /***********************************************************************
105 * Forward declaration of internal functions used by the method DestroyElement
107 static HRESULT deleteStorageProperty(
108 StorageImpl *parentStorage,
109 ULONG foundPropertyIndexToDelete,
110 StgProperty propertyToDelete);
112 static HRESULT deleteStreamProperty(
113 StorageImpl *parentStorage,
114 ULONG foundPropertyIndexToDelete,
115 StgProperty propertyToDelete);
117 static HRESULT findPlaceholder(
118 StorageImpl *storage,
119 ULONG propertyIndexToStore,
120 ULONG storagePropertyIndex,
121 INT typeOfRelation);
123 static HRESULT adjustPropertyChain(
124 StorageImpl *This,
125 StgProperty propertyToDelete,
126 StgProperty parentProperty,
127 ULONG parentPropertyId,
128 INT typeOfRelation);
130 /***********************************************************************
131 * Declaration of the functions used to manipulate StgProperty
134 static ULONG getFreeProperty(
135 StorageImpl *storage);
137 static void updatePropertyChain(
138 StorageImpl *storage,
139 ULONG newPropertyIndex,
140 StgProperty newProperty);
142 static LONG propertyNameCmp(
143 OLECHAR *newProperty,
144 OLECHAR *currentProperty);
147 /***********************************************************************
148 * Declaration of miscellaneous functions...
150 static HRESULT validateSTGM(DWORD stgmValue);
152 static DWORD GetShareModeFromSTGM(DWORD stgm);
153 static DWORD GetAccessModeFromSTGM(DWORD stgm);
154 static DWORD GetCreationModeFromSTGM(DWORD stgm);
157 * Virtual function table for the IStorage32Impl class.
159 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
161 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
162 StorageBaseImpl_QueryInterface,
163 StorageBaseImpl_AddRef,
164 StorageBaseImpl_Release,
165 StorageBaseImpl_CreateStream,
166 StorageBaseImpl_OpenStream,
167 StorageImpl_CreateStorage,
168 StorageBaseImpl_OpenStorage,
169 StorageImpl_CopyTo,
170 StorageImpl_MoveElementTo,
171 StorageImpl_Commit,
172 StorageImpl_Revert,
173 StorageBaseImpl_EnumElements,
174 StorageImpl_DestroyElement,
175 StorageBaseImpl_RenameElement,
176 StorageImpl_SetElementTimes,
177 StorageBaseImpl_SetClass,
178 StorageImpl_SetStateBits,
179 StorageImpl_Stat
183 * Virtual function table for the Storage32InternalImpl class.
185 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
187 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
188 StorageBaseImpl_QueryInterface,
189 StorageBaseImpl_AddRef,
190 StorageBaseImpl_Release,
191 StorageBaseImpl_CreateStream,
192 StorageBaseImpl_OpenStream,
193 StorageImpl_CreateStorage,
194 StorageBaseImpl_OpenStorage,
195 StorageImpl_CopyTo,
196 StorageImpl_MoveElementTo,
197 StorageInternalImpl_Commit,
198 StorageInternalImpl_Revert,
199 StorageBaseImpl_EnumElements,
200 StorageImpl_DestroyElement,
201 StorageBaseImpl_RenameElement,
202 StorageImpl_SetElementTimes,
203 StorageBaseImpl_SetClass,
204 StorageImpl_SetStateBits,
205 StorageBaseImpl_Stat
209 * Virtual function table for the IEnumSTATSTGImpl class.
211 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
213 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
214 IEnumSTATSTGImpl_QueryInterface,
215 IEnumSTATSTGImpl_AddRef,
216 IEnumSTATSTGImpl_Release,
217 IEnumSTATSTGImpl_Next,
218 IEnumSTATSTGImpl_Skip,
219 IEnumSTATSTGImpl_Reset,
220 IEnumSTATSTGImpl_Clone
227 /************************************************************************
228 ** Storage32BaseImpl implementatiion
231 /************************************************************************
232 * Storage32BaseImpl_QueryInterface (IUnknown)
234 * This method implements the common QueryInterface for all IStorage32
235 * implementations contained in this file.
237 * See Windows documentation for more details on IUnknown methods.
239 HRESULT WINAPI StorageBaseImpl_QueryInterface(
240 IStorage* iface,
241 REFIID riid,
242 void** ppvObject)
244 ICOM_THIS(StorageBaseImpl,iface);
246 * Perform a sanity check on the parameters.
248 if ( (This==0) || (ppvObject==0) )
249 return E_INVALIDARG;
252 * Initialize the return parameter.
254 *ppvObject = 0;
257 * Compare the riid with the interface IDs implemented by this object.
259 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
261 *ppvObject = (IStorage*)This;
263 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
265 *ppvObject = (IStorage*)This;
269 * Check that we obtained an interface.
271 if ((*ppvObject)==0)
272 return E_NOINTERFACE;
275 * Query Interface always increases the reference count by one when it is
276 * successful
278 StorageBaseImpl_AddRef(iface);
280 return S_OK;
283 /************************************************************************
284 * Storage32BaseImpl_AddRef (IUnknown)
286 * This method implements the common AddRef for all IStorage32
287 * implementations contained in this file.
289 * See Windows documentation for more details on IUnknown methods.
291 ULONG WINAPI StorageBaseImpl_AddRef(
292 IStorage* iface)
294 ICOM_THIS(StorageBaseImpl,iface);
295 This->ref++;
297 return This->ref;
300 /************************************************************************
301 * Storage32BaseImpl_Release (IUnknown)
303 * This method implements the common Release for all IStorage32
304 * implementations contained in this file.
306 * See Windows documentation for more details on IUnknown methods.
308 ULONG WINAPI StorageBaseImpl_Release(
309 IStorage* iface)
311 ICOM_THIS(StorageBaseImpl,iface);
313 * Decrease the reference count on this object.
315 This->ref--;
318 * If the reference count goes down to 0, perform suicide.
320 if (This->ref==0)
323 * Since we are using a system of base-classes, we want to call the
324 * destructor of the appropriate derived class. To do this, we are
325 * using virtual functions to implement the destructor.
327 This->v_destructor(This);
329 return 0;
332 return This->ref;
335 /************************************************************************
336 * Storage32BaseImpl_OpenStream (IStorage)
338 * This method will open the specified stream object from the current storage.
340 * See Windows documentation for more details on IStorage methods.
342 HRESULT WINAPI StorageBaseImpl_OpenStream(
343 IStorage* iface,
344 const OLECHAR* pwcsName, /* [string][in] */
345 void* reserved1, /* [unique][in] */
346 DWORD grfMode, /* [in] */
347 DWORD reserved2, /* [in] */
348 IStream** ppstm) /* [out] */
350 ICOM_THIS(StorageBaseImpl,iface);
351 IEnumSTATSTGImpl* propertyEnumeration;
352 StgStreamImpl* newStream;
353 StgProperty currentProperty;
354 ULONG foundPropertyIndex;
355 HRESULT res = STG_E_UNKNOWN;
357 TRACE("(%p, %s, %p, %lx, %ld, %p)\n",
358 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
361 * Perform a sanity check on the parameters.
363 if ( (pwcsName==NULL) || (ppstm==0) )
365 res = E_INVALIDARG;
366 goto end;
370 * Initialize the out parameter
372 *ppstm = NULL;
375 * Validate the STGM flags
377 if ( FAILED( validateSTGM(grfMode) ))
379 res = STG_E_INVALIDFLAG;
380 goto end;
384 * As documented.
386 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
387 (grfMode & STGM_DELETEONRELEASE) ||
388 (grfMode & STGM_TRANSACTED) )
390 res = STG_E_INVALIDFUNCTION;
391 goto end;
395 * Create a property enumeration to search the properties
397 propertyEnumeration = IEnumSTATSTGImpl_Construct(
398 This->ancestorStorage,
399 This->rootPropertySetIndex);
402 * Search the enumeration for the property with the given name
404 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
405 propertyEnumeration,
406 pwcsName,
407 &currentProperty);
410 * Delete the property enumeration since we don't need it anymore
412 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
415 * If it was found, construct the stream object and return a pointer to it.
417 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
418 (currentProperty.propertyType==PROPTYPE_STREAM) )
420 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
422 if (newStream!=0)
424 newStream->grfMode = grfMode;
425 *ppstm = (IStream*)newStream;
428 * Since we are returning a pointer to the interface, we have to
429 * nail down the reference.
431 StgStreamImpl_AddRef(*ppstm);
433 res = S_OK;
434 goto end;
437 res = E_OUTOFMEMORY;
438 goto end;
441 res = STG_E_FILENOTFOUND;
443 end:
444 if (res == S_OK)
445 TRACE("<-- IStream %p\n", *ppstm);
446 TRACE("<-- %08lx\n", res);
447 return res;
450 /************************************************************************
451 * Storage32BaseImpl_OpenStorage (IStorage)
453 * This method will open a new storage object from the current storage.
455 * See Windows documentation for more details on IStorage methods.
457 HRESULT WINAPI StorageBaseImpl_OpenStorage(
458 IStorage* iface,
459 const OLECHAR* pwcsName, /* [string][unique][in] */
460 IStorage* pstgPriority, /* [unique][in] */
461 DWORD grfMode, /* [in] */
462 SNB snbExclude, /* [unique][in] */
463 DWORD reserved, /* [in] */
464 IStorage** ppstg) /* [out] */
466 ICOM_THIS(StorageBaseImpl,iface);
467 StorageInternalImpl* newStorage;
468 IEnumSTATSTGImpl* propertyEnumeration;
469 StgProperty currentProperty;
470 ULONG foundPropertyIndex;
471 HRESULT res = STG_E_UNKNOWN;
473 TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n",
474 iface, debugstr_w(pwcsName), pstgPriority,
475 grfMode, snbExclude, reserved, ppstg);
478 * Perform a sanity check on the parameters.
480 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
482 res = E_INVALIDARG;
483 goto end;
486 /* as documented */
487 if (snbExclude != NULL)
489 res = STG_E_INVALIDPARAMETER;
490 goto end;
494 * Validate the STGM flags
496 if ( FAILED( validateSTGM(grfMode) ))
498 res = STG_E_INVALIDFLAG;
499 goto end;
503 * As documented.
505 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
506 (grfMode & STGM_DELETEONRELEASE) ||
507 (grfMode & STGM_PRIORITY) )
509 res = STG_E_INVALIDFUNCTION;
510 goto end;
514 * Initialize the out parameter
516 *ppstg = NULL;
519 * Create a property enumeration to search the properties
521 propertyEnumeration = IEnumSTATSTGImpl_Construct(
522 This->ancestorStorage,
523 This->rootPropertySetIndex);
526 * Search the enumeration for the property with the given name
528 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
529 propertyEnumeration,
530 pwcsName,
531 &currentProperty);
534 * Delete the property enumeration since we don't need it anymore
536 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
539 * If it was found, construct the stream object and return a pointer to it.
541 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
542 (currentProperty.propertyType==PROPTYPE_STORAGE) )
545 * Construct a new Storage object
547 newStorage = StorageInternalImpl_Construct(
548 This->ancestorStorage,
549 foundPropertyIndex);
551 if (newStorage != 0)
553 *ppstg = (IStorage*)newStorage;
556 * Since we are returning a pointer to the interface,
557 * we have to nail down the reference.
559 StorageBaseImpl_AddRef(*ppstg);
561 res = S_OK;
562 goto end;
565 res = STG_E_INSUFFICIENTMEMORY;
566 goto end;
569 res = STG_E_FILENOTFOUND;
571 end:
572 TRACE("<-- %08lx\n", res);
573 return res;
576 /************************************************************************
577 * Storage32BaseImpl_EnumElements (IStorage)
579 * This method will create an enumerator object that can be used to
580 * retrieve informatino about all the properties in the storage object.
582 * See Windows documentation for more details on IStorage methods.
584 HRESULT WINAPI StorageBaseImpl_EnumElements(
585 IStorage* iface,
586 DWORD reserved1, /* [in] */
587 void* reserved2, /* [size_is][unique][in] */
588 DWORD reserved3, /* [in] */
589 IEnumSTATSTG** ppenum) /* [out] */
591 ICOM_THIS(StorageBaseImpl,iface);
592 IEnumSTATSTGImpl* newEnum;
594 TRACE("(%p, %ld, %p, %ld, %p)\n",
595 iface, reserved1, reserved2, reserved3, ppenum);
598 * Perform a sanity check on the parameters.
600 if ( (This==0) || (ppenum==0))
601 return E_INVALIDARG;
604 * Construct the enumerator.
606 newEnum = IEnumSTATSTGImpl_Construct(
607 This->ancestorStorage,
608 This->rootPropertySetIndex);
610 if (newEnum!=0)
612 *ppenum = (IEnumSTATSTG*)newEnum;
615 * Don't forget to nail down a reference to the new object before
616 * returning it.
618 IEnumSTATSTGImpl_AddRef(*ppenum);
620 return S_OK;
623 return E_OUTOFMEMORY;
626 /************************************************************************
627 * Storage32BaseImpl_Stat (IStorage)
629 * This method will retrieve information about this storage object.
631 * See Windows documentation for more details on IStorage methods.
633 HRESULT WINAPI StorageBaseImpl_Stat(
634 IStorage* iface,
635 STATSTG* pstatstg, /* [out] */
636 DWORD grfStatFlag) /* [in] */
638 ICOM_THIS(StorageBaseImpl,iface);
639 StgProperty curProperty;
640 BOOL readSuccessful;
641 HRESULT res = STG_E_UNKNOWN;
643 TRACE("(%p, %p, %lx)\n",
644 iface, pstatstg, grfStatFlag);
647 * Perform a sanity check on the parameters.
649 if ( (This==0) || (pstatstg==0))
651 res = E_INVALIDARG;
652 goto end;
656 * Read the information from the property.
658 readSuccessful = StorageImpl_ReadProperty(
659 This->ancestorStorage,
660 This->rootPropertySetIndex,
661 &curProperty);
663 if (readSuccessful)
665 StorageUtl_CopyPropertyToSTATSTG(
666 pstatstg,
667 &curProperty,
668 grfStatFlag);
670 res = S_OK;
671 goto end;
674 res = E_FAIL;
676 end:
677 if (res == S_OK)
679 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.s.LowPart, pstatstg->cbSize.s.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
681 TRACE("<-- %08lx\n", res);
682 return res;
685 /************************************************************************
686 * Storage32BaseImpl_RenameElement (IStorage)
688 * This method will rename the specified element.
690 * See Windows documentation for more details on IStorage methods.
692 * Implementation notes: The method used to rename consists of creating a clone
693 * of the deleted StgProperty object setting it with the new name and to
694 * perform a DestroyElement of the old StgProperty.
696 HRESULT WINAPI StorageBaseImpl_RenameElement(
697 IStorage* iface,
698 const OLECHAR* pwcsOldName, /* [in] */
699 const OLECHAR* pwcsNewName) /* [in] */
701 ICOM_THIS(StorageBaseImpl,iface);
702 IEnumSTATSTGImpl* propertyEnumeration;
703 StgProperty currentProperty;
704 ULONG foundPropertyIndex;
706 TRACE("(%p, %s, %s)\n",
707 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
710 * Create a property enumeration to search the properties
712 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
713 This->rootPropertySetIndex);
716 * Search the enumeration for the new property name
718 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
719 pwcsNewName,
720 &currentProperty);
722 if (foundPropertyIndex != PROPERTY_NULL)
725 * There is already a property with the new name
727 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
728 return STG_E_FILEALREADYEXISTS;
731 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
734 * Search the enumeration for the old property name
736 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
737 pwcsOldName,
738 &currentProperty);
741 * Delete the property enumeration since we don't need it anymore
743 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
745 if (foundPropertyIndex != PROPERTY_NULL)
747 StgProperty renamedProperty;
748 ULONG renamedPropertyIndex;
751 * Setup a new property for the renamed property
753 renamedProperty.sizeOfNameString =
754 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
756 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
757 return STG_E_INVALIDNAME;
759 strcpyW(renamedProperty.name, pwcsNewName);
761 renamedProperty.propertyType = currentProperty.propertyType;
762 renamedProperty.startingBlock = currentProperty.startingBlock;
763 renamedProperty.size.s.LowPart = currentProperty.size.s.LowPart;
764 renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
766 renamedProperty.previousProperty = PROPERTY_NULL;
767 renamedProperty.nextProperty = PROPERTY_NULL;
770 * Bring the dirProperty link in case it is a storage and in which
771 * case the renamed storage elements don't require to be reorganized.
773 renamedProperty.dirProperty = currentProperty.dirProperty;
775 /* call CoFileTime to get the current time
776 renamedProperty.timeStampS1
777 renamedProperty.timeStampD1
778 renamedProperty.timeStampS2
779 renamedProperty.timeStampD2
780 renamedProperty.propertyUniqueID
784 * Obtain a free property in the property chain
786 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
789 * Save the new property into the new property spot
791 StorageImpl_WriteProperty(
792 This->ancestorStorage,
793 renamedPropertyIndex,
794 &renamedProperty);
797 * Find a spot in the property chain for our newly created property.
799 updatePropertyChain(
800 (StorageImpl*)This,
801 renamedPropertyIndex,
802 renamedProperty);
805 * At this point the renamed property has been inserted in the tree,
806 * now, before to Destroy the old property we must zeroed it's dirProperty
807 * otherwise the DestroyProperty below will zap it all and we do not want
808 * this to happen.
809 * Also, we fake that the old property is a storage so the DestroyProperty
810 * will not do a SetSize(0) on the stream data.
812 * This means that we need to tweek the StgProperty if it is a stream or a
813 * non empty storage.
815 StorageImpl_ReadProperty(This->ancestorStorage,
816 foundPropertyIndex,
817 &currentProperty);
819 currentProperty.dirProperty = PROPERTY_NULL;
820 currentProperty.propertyType = PROPTYPE_STORAGE;
821 StorageImpl_WriteProperty(
822 This->ancestorStorage,
823 foundPropertyIndex,
824 &currentProperty);
827 * Invoke Destroy to get rid of the ole property and automatically redo
828 * the linking of it's previous and next members...
830 StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
833 else
836 * There is no property with the old name
838 return STG_E_FILENOTFOUND;
841 return S_OK;
844 /************************************************************************
845 * Storage32BaseImpl_CreateStream (IStorage)
847 * This method will create a stream object within this storage
849 * See Windows documentation for more details on IStorage methods.
851 HRESULT WINAPI StorageBaseImpl_CreateStream(
852 IStorage* iface,
853 const OLECHAR* pwcsName, /* [string][in] */
854 DWORD grfMode, /* [in] */
855 DWORD reserved1, /* [in] */
856 DWORD reserved2, /* [in] */
857 IStream** ppstm) /* [out] */
859 ICOM_THIS(StorageBaseImpl,iface);
860 IEnumSTATSTGImpl* propertyEnumeration;
861 StgStreamImpl* newStream;
862 StgProperty currentProperty, newStreamProperty;
863 ULONG foundPropertyIndex, newPropertyIndex;
865 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
866 iface, debugstr_w(pwcsName), grfMode,
867 reserved1, reserved2, ppstm);
870 * Validate parameters
872 if (ppstm == 0)
873 return STG_E_INVALIDPOINTER;
875 if (pwcsName == 0)
876 return STG_E_INVALIDNAME;
879 * Validate the STGM flags
881 if ( FAILED( validateSTGM(grfMode) ))
882 return STG_E_INVALIDFLAG;
885 * As documented.
887 if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
888 (grfMode & STGM_DELETEONRELEASE) ||
889 (grfMode & STGM_TRANSACTED) )
890 return STG_E_INVALIDFUNCTION;
893 * Initialize the out parameter
895 *ppstm = 0;
898 * Create a property enumeration to search the properties
900 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
901 This->rootPropertySetIndex);
903 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
904 pwcsName,
905 &currentProperty);
907 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
909 if (foundPropertyIndex != PROPERTY_NULL)
912 * An element with this name already exists
914 if (grfMode & STGM_CREATE)
916 IStorage_DestroyElement(iface, pwcsName);
918 else
919 return STG_E_FILEALREADYEXISTS;
923 * memset the empty property
925 memset(&newStreamProperty, 0, sizeof(StgProperty));
927 newStreamProperty.sizeOfNameString =
928 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
930 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
931 return STG_E_INVALIDNAME;
933 strcpyW(newStreamProperty.name, pwcsName);
935 newStreamProperty.propertyType = PROPTYPE_STREAM;
936 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
937 newStreamProperty.size.s.LowPart = 0;
938 newStreamProperty.size.s.HighPart = 0;
940 newStreamProperty.previousProperty = PROPERTY_NULL;
941 newStreamProperty.nextProperty = PROPERTY_NULL;
942 newStreamProperty.dirProperty = PROPERTY_NULL;
944 /* call CoFileTime to get the current time
945 newStreamProperty.timeStampS1
946 newStreamProperty.timeStampD1
947 newStreamProperty.timeStampS2
948 newStreamProperty.timeStampD2
951 /* newStreamProperty.propertyUniqueID */
954 * Get a free property or create a new one
956 newPropertyIndex = getFreeProperty(This->ancestorStorage);
959 * Save the new property into the new property spot
961 StorageImpl_WriteProperty(
962 This->ancestorStorage,
963 newPropertyIndex,
964 &newStreamProperty);
967 * Find a spot in the property chain for our newly created property.
969 updatePropertyChain(
970 (StorageImpl*)This,
971 newPropertyIndex,
972 newStreamProperty);
975 * Open the stream to return it.
977 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
979 if (newStream != 0)
981 *ppstm = (IStream*)newStream;
984 * Since we are returning a pointer to the interface, we have to nail down
985 * the reference.
987 StgStreamImpl_AddRef(*ppstm);
989 else
991 return STG_E_INSUFFICIENTMEMORY;
994 return S_OK;
997 /************************************************************************
998 * Storage32BaseImpl_SetClass (IStorage)
1000 * This method will write the specified CLSID in the property of this
1001 * storage.
1003 * See Windows documentation for more details on IStorage methods.
1005 HRESULT WINAPI StorageBaseImpl_SetClass(
1006 IStorage* iface,
1007 REFCLSID clsid) /* [in] */
1009 ICOM_THIS(StorageBaseImpl,iface);
1010 HRESULT hRes = E_FAIL;
1011 StgProperty curProperty;
1012 BOOL success;
1014 TRACE("(%p, %p)\n", iface, clsid);
1016 success = StorageImpl_ReadProperty(This->ancestorStorage,
1017 This->rootPropertySetIndex,
1018 &curProperty);
1019 if (success)
1021 curProperty.propertyUniqueID = *clsid;
1023 success = StorageImpl_WriteProperty(This->ancestorStorage,
1024 This->rootPropertySetIndex,
1025 &curProperty);
1026 if (success)
1027 hRes = S_OK;
1030 return hRes;
1033 /************************************************************************
1034 ** Storage32Impl implementation
1037 /************************************************************************
1038 * Storage32Impl_CreateStorage (IStorage)
1040 * This method will create the storage object within the provided storage.
1042 * See Windows documentation for more details on IStorage methods.
1044 HRESULT WINAPI StorageImpl_CreateStorage(
1045 IStorage* iface,
1046 const OLECHAR *pwcsName, /* [string][in] */
1047 DWORD grfMode, /* [in] */
1048 DWORD reserved1, /* [in] */
1049 DWORD reserved2, /* [in] */
1050 IStorage **ppstg) /* [out] */
1052 StorageImpl* const This=(StorageImpl*)iface;
1054 IEnumSTATSTGImpl *propertyEnumeration;
1055 StgProperty currentProperty;
1056 StgProperty newProperty;
1057 ULONG foundPropertyIndex;
1058 ULONG newPropertyIndex;
1059 HRESULT hr;
1061 TRACE("(%p, %s, %lx, %ld, %ld, %p)\n",
1062 iface, debugstr_w(pwcsName), grfMode,
1063 reserved1, reserved2, ppstg);
1066 * Validate parameters
1068 if (ppstg == 0)
1069 return STG_E_INVALIDPOINTER;
1071 if (pwcsName == 0)
1072 return STG_E_INVALIDNAME;
1075 * Validate the STGM flags
1077 if ( FAILED( validateSTGM(grfMode) ) ||
1078 (grfMode & STGM_DELETEONRELEASE) )
1079 return STG_E_INVALIDFLAG;
1082 * Initialize the out parameter
1084 *ppstg = 0;
1087 * Create a property enumeration and search the properties
1089 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1090 This->rootPropertySetIndex);
1092 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1093 pwcsName,
1094 &currentProperty);
1095 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1097 if (foundPropertyIndex != PROPERTY_NULL)
1100 * An element with this name already exists
1102 if (grfMode & STGM_CREATE)
1103 IStorage_DestroyElement(iface, pwcsName);
1104 else
1105 return STG_E_FILEALREADYEXISTS;
1109 * memset the empty property
1111 memset(&newProperty, 0, sizeof(StgProperty));
1113 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1115 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1116 return STG_E_INVALIDNAME;
1118 strcpyW(newProperty.name, pwcsName);
1120 newProperty.propertyType = PROPTYPE_STORAGE;
1121 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1122 newProperty.size.s.LowPart = 0;
1123 newProperty.size.s.HighPart = 0;
1125 newProperty.previousProperty = PROPERTY_NULL;
1126 newProperty.nextProperty = PROPERTY_NULL;
1127 newProperty.dirProperty = PROPERTY_NULL;
1129 /* call CoFileTime to get the current time
1130 newProperty.timeStampS1
1131 newProperty.timeStampD1
1132 newProperty.timeStampS2
1133 newProperty.timeStampD2
1136 /* newStorageProperty.propertyUniqueID */
1139 * Obtain a free property in the property chain
1141 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1144 * Save the new property into the new property spot
1146 StorageImpl_WriteProperty(
1147 This->ancestorStorage,
1148 newPropertyIndex,
1149 &newProperty);
1152 * Find a spot in the property chain for our newly created property.
1154 updatePropertyChain(
1155 This,
1156 newPropertyIndex,
1157 newProperty);
1160 * Open it to get a pointer to return.
1162 hr = IStorage_OpenStorage(
1163 iface,
1164 (OLECHAR*)pwcsName,
1166 grfMode,
1169 ppstg);
1171 if( (hr != S_OK) || (*ppstg == NULL))
1173 return hr;
1177 return S_OK;
1181 /***************************************************************************
1183 * Internal Method
1185 * Get a free property or create a new one.
1187 static ULONG getFreeProperty(
1188 StorageImpl *storage)
1190 ULONG currentPropertyIndex = 0;
1191 ULONG newPropertyIndex = PROPERTY_NULL;
1192 BOOL readSuccessful = TRUE;
1193 StgProperty currentProperty;
1198 * Start by reading the root property
1200 readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1201 currentPropertyIndex,
1202 &currentProperty);
1203 if (readSuccessful)
1205 if (currentProperty.sizeOfNameString == 0)
1208 * The property existis and is available, we found it.
1210 newPropertyIndex = currentPropertyIndex;
1213 else
1216 * We exhausted the property list, we will create more space below
1218 newPropertyIndex = currentPropertyIndex;
1220 currentPropertyIndex++;
1222 } while (newPropertyIndex == PROPERTY_NULL);
1225 * grow the property chain
1227 if (! readSuccessful)
1229 StgProperty emptyProperty;
1230 ULARGE_INTEGER newSize;
1231 ULONG propertyIndex;
1232 ULONG lastProperty = 0;
1233 ULONG blockCount = 0;
1236 * obtain the new count of property blocks
1238 blockCount = BlockChainStream_GetCount(
1239 storage->ancestorStorage->rootBlockChain)+1;
1242 * initialize the size used by the property stream
1244 newSize.s.HighPart = 0;
1245 newSize.s.LowPart = storage->bigBlockSize * blockCount;
1248 * add a property block to the property chain
1250 BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1253 * memset the empty property in order to initialize the unused newly
1254 * created property
1256 memset(&emptyProperty, 0, sizeof(StgProperty));
1259 * initialize them
1261 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1263 for(
1264 propertyIndex = newPropertyIndex;
1265 propertyIndex < lastProperty;
1266 propertyIndex++)
1268 StorageImpl_WriteProperty(
1269 storage->ancestorStorage,
1270 propertyIndex,
1271 &emptyProperty);
1275 return newPropertyIndex;
1278 /****************************************************************************
1280 * Internal Method
1282 * Case insensitive comparaison of StgProperty.name by first considering
1283 * their size.
1285 * Returns <0 when newPrpoerty < currentProperty
1286 * >0 when newPrpoerty > currentProperty
1287 * 0 when newPrpoerty == currentProperty
1289 static LONG propertyNameCmp(
1290 OLECHAR *newProperty,
1291 OLECHAR *currentProperty)
1293 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1295 if (diff == 0)
1298 * We compare the string themselves only when they are of the same length
1300 diff = lstrcmpiW( newProperty, currentProperty);
1303 return diff;
1306 /****************************************************************************
1308 * Internal Method
1310 * Properly link this new element in the property chain.
1312 static void updatePropertyChain(
1313 StorageImpl *storage,
1314 ULONG newPropertyIndex,
1315 StgProperty newProperty)
1317 StgProperty currentProperty;
1320 * Read the root property
1322 StorageImpl_ReadProperty(storage->ancestorStorage,
1323 storage->rootPropertySetIndex,
1324 &currentProperty);
1326 if (currentProperty.dirProperty != PROPERTY_NULL)
1329 * The root storage contains some element, therefore, start the research
1330 * for the appropriate location.
1332 BOOL found = 0;
1333 ULONG current, next, previous, currentPropertyId;
1336 * Keep the StgProperty sequence number of the storage first property
1338 currentPropertyId = currentProperty.dirProperty;
1341 * Read
1343 StorageImpl_ReadProperty(storage->ancestorStorage,
1344 currentProperty.dirProperty,
1345 &currentProperty);
1347 previous = currentProperty.previousProperty;
1348 next = currentProperty.nextProperty;
1349 current = currentPropertyId;
1351 while (found == 0)
1353 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1355 if (diff < 0)
1357 if (previous != PROPERTY_NULL)
1359 StorageImpl_ReadProperty(storage->ancestorStorage,
1360 previous,
1361 &currentProperty);
1362 current = previous;
1364 else
1366 currentProperty.previousProperty = newPropertyIndex;
1367 StorageImpl_WriteProperty(storage->ancestorStorage,
1368 current,
1369 &currentProperty);
1370 found = 1;
1373 else if (diff > 0)
1375 if (next != PROPERTY_NULL)
1377 StorageImpl_ReadProperty(storage->ancestorStorage,
1378 next,
1379 &currentProperty);
1380 current = next;
1382 else
1384 currentProperty.nextProperty = newPropertyIndex;
1385 StorageImpl_WriteProperty(storage->ancestorStorage,
1386 current,
1387 &currentProperty);
1388 found = 1;
1391 else
1394 * Trying to insert an item with the same name in the
1395 * subtree structure.
1397 assert(FALSE);
1400 previous = currentProperty.previousProperty;
1401 next = currentProperty.nextProperty;
1404 else
1407 * The root storage is empty, link the new property to it's dir property
1409 currentProperty.dirProperty = newPropertyIndex;
1410 StorageImpl_WriteProperty(storage->ancestorStorage,
1411 storage->rootPropertySetIndex,
1412 &currentProperty);
1417 /*************************************************************************
1418 * CopyTo (IStorage)
1420 HRESULT WINAPI StorageImpl_CopyTo(
1421 IStorage* iface,
1422 DWORD ciidExclude, /* [in] */
1423 const IID* rgiidExclude, /* [size_is][unique][in] */
1424 SNB snbExclude, /* [unique][in] */
1425 IStorage* pstgDest) /* [unique][in] */
1427 IEnumSTATSTG *elements = 0;
1428 STATSTG curElement, strStat;
1429 HRESULT hr;
1430 IStorage *pstgTmp, *pstgChild;
1431 IStream *pstrTmp, *pstrChild;
1433 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1434 FIXME("Exclude option not implemented\n");
1436 TRACE("(%p, %ld, %p, %p, %p)\n",
1437 iface, ciidExclude, rgiidExclude,
1438 snbExclude, pstgDest);
1441 * Perform a sanity check
1443 if ( pstgDest == 0 )
1444 return STG_E_INVALIDPOINTER;
1447 * Enumerate the elements
1449 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1451 if ( hr != S_OK )
1452 return hr;
1455 * set the class ID
1457 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1458 IStorage_SetClass( pstgDest, &curElement.clsid );
1463 * Obtain the next element
1465 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1467 if ( hr == S_FALSE )
1469 hr = S_OK; /* done, every element has been copied */
1470 break;
1473 if (curElement.type == STGTY_STORAGE)
1476 * open child source storage
1478 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1479 STGM_READ|STGM_SHARE_EXCLUSIVE,
1480 NULL, 0, &pstgChild );
1482 if (hr != S_OK)
1483 break;
1486 * Check if destination storage is not a child of the source
1487 * storage, which will cause an infinite loop
1489 if (pstgChild == pstgDest)
1491 IEnumSTATSTG_Release(elements);
1493 return STG_E_ACCESSDENIED;
1497 * create a new storage in destination storage
1499 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1500 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1501 0, 0,
1502 &pstgTmp );
1504 * if it already exist, don't create a new one use this one
1506 if (hr == STG_E_FILEALREADYEXISTS)
1508 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1509 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1510 NULL, 0, &pstgTmp );
1513 if (hr != S_OK)
1514 break;
1518 * do the copy recursively
1520 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1521 snbExclude, pstgTmp );
1523 IStorage_Release( pstgTmp );
1524 IStorage_Release( pstgChild );
1526 else if (curElement.type == STGTY_STREAM)
1529 * create a new stream in destination storage. If the stream already
1530 * exist, it will be deleted and a new one will be created.
1532 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1533 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1534 0, 0, &pstrTmp );
1536 if (hr != S_OK)
1537 break;
1540 * open child stream storage
1542 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1543 STGM_READ|STGM_SHARE_EXCLUSIVE,
1544 0, &pstrChild );
1546 if (hr != S_OK)
1547 break;
1550 * Get the size of the source stream
1552 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1555 * Set the size of the destination stream.
1557 IStream_SetSize(pstrTmp, strStat.cbSize);
1560 * do the copy
1562 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1563 NULL, NULL );
1565 IStream_Release( pstrTmp );
1566 IStream_Release( pstrChild );
1568 else
1570 WARN("unknown element type: %ld\n", curElement.type);
1573 } while (hr == S_OK);
1576 * Clean-up
1578 IEnumSTATSTG_Release(elements);
1580 return hr;
1583 /*************************************************************************
1584 * MoveElementTo (IStorage)
1586 HRESULT WINAPI StorageImpl_MoveElementTo(
1587 IStorage* iface,
1588 const OLECHAR *pwcsName, /* [string][in] */
1589 IStorage *pstgDest, /* [unique][in] */
1590 const OLECHAR *pwcsNewName,/* [string][in] */
1591 DWORD grfFlags) /* [in] */
1593 FIXME("not implemented!\n");
1594 return E_NOTIMPL;
1597 /*************************************************************************
1598 * Commit (IStorage)
1600 HRESULT WINAPI StorageImpl_Commit(
1601 IStorage* iface,
1602 DWORD grfCommitFlags)/* [in] */
1604 FIXME("(%ld): stub!\n", grfCommitFlags);
1605 return S_OK;
1608 /*************************************************************************
1609 * Revert (IStorage)
1611 HRESULT WINAPI StorageImpl_Revert(
1612 IStorage* iface)
1614 FIXME("not implemented!\n");
1615 return E_NOTIMPL;
1618 /*************************************************************************
1619 * DestroyElement (IStorage)
1621 * Stategy: This implementation is build this way for simplicity not for speed.
1622 * I always delete the top most element of the enumeration and adjust
1623 * the deleted element pointer all the time. This takes longer to
1624 * do but allow to reinvoke DestroyElement whenever we encounter a
1625 * storage object. The optimisation reside in the usage of another
1626 * enumeration stategy that would give all the leaves of a storage
1627 * first. (postfix order)
1629 HRESULT WINAPI StorageImpl_DestroyElement(
1630 IStorage* iface,
1631 const OLECHAR *pwcsName)/* [string][in] */
1633 StorageImpl* const This=(StorageImpl*)iface;
1635 IEnumSTATSTGImpl* propertyEnumeration;
1636 HRESULT hr = S_OK;
1637 BOOL res;
1638 StgProperty propertyToDelete;
1639 StgProperty parentProperty;
1640 ULONG foundPropertyIndexToDelete;
1641 ULONG typeOfRelation;
1642 ULONG parentPropertyId;
1644 TRACE("(%p, %s)\n",
1645 iface, debugstr_w(pwcsName));
1648 * Perform a sanity check on the parameters.
1650 if (pwcsName==NULL)
1651 return STG_E_INVALIDPOINTER;
1654 * Create a property enumeration to search the property with the given name
1656 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1657 This->ancestorStorage,
1658 This->rootPropertySetIndex);
1660 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1661 propertyEnumeration,
1662 pwcsName,
1663 &propertyToDelete);
1665 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1667 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1669 return STG_E_FILENOTFOUND;
1673 * Find the parent property of the property to delete (the one that
1674 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1675 * the parent is This. Otherwise, the parent is one of it's sibling...
1679 * First, read This's StgProperty..
1681 res = StorageImpl_ReadProperty(
1682 This->ancestorStorage,
1683 This->rootPropertySetIndex,
1684 &parentProperty);
1686 assert(res==TRUE);
1689 * Second, check to see if by any chance the actual storage (This) is not
1690 * the parent of the property to delete... We never know...
1692 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1695 * Set data as it would have been done in the else part...
1697 typeOfRelation = PROPERTY_RELATION_DIR;
1698 parentPropertyId = This->rootPropertySetIndex;
1700 else
1703 * Create a property enumeration to search the parent properties, and
1704 * delete it once done.
1706 IEnumSTATSTGImpl* propertyEnumeration2;
1708 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1709 This->ancestorStorage,
1710 This->rootPropertySetIndex);
1712 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1713 propertyEnumeration2,
1714 foundPropertyIndexToDelete,
1715 &parentProperty,
1716 &parentPropertyId);
1718 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1721 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1723 hr = deleteStorageProperty(
1724 This,
1725 foundPropertyIndexToDelete,
1726 propertyToDelete);
1728 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1730 hr = deleteStreamProperty(
1731 This,
1732 foundPropertyIndexToDelete,
1733 propertyToDelete);
1736 if (hr!=S_OK)
1737 return hr;
1740 * Adjust the property chain
1742 hr = adjustPropertyChain(
1743 This,
1744 propertyToDelete,
1745 parentProperty,
1746 parentPropertyId,
1747 typeOfRelation);
1749 return hr;
1753 /************************************************************************
1754 * StorageImpl_Stat (IStorage)
1756 * This method will retrieve information about this storage object.
1758 * See Windows documentation for more details on IStorage methods.
1760 HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1761 STATSTG* pstatstg, /* [out] */
1762 DWORD grfStatFlag) /* [in] */
1764 StorageImpl* const This = (StorageImpl*)iface;
1765 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1767 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1769 CoTaskMemFree(pstatstg->pwcsName);
1770 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1771 strcpyW(pstatstg->pwcsName, This->pwcsName);
1774 return result;
1779 /*********************************************************************
1781 * Internal Method
1783 * Perform the deletion of a complete storage node
1786 static HRESULT deleteStorageProperty(
1787 StorageImpl *parentStorage,
1788 ULONG indexOfPropertyToDelete,
1789 StgProperty propertyToDelete)
1791 IEnumSTATSTG *elements = 0;
1792 IStorage *childStorage = 0;
1793 STATSTG currentElement;
1794 HRESULT hr;
1795 HRESULT destroyHr = S_OK;
1798 * Open the storage and enumerate it
1800 hr = StorageBaseImpl_OpenStorage(
1801 (IStorage*)parentStorage,
1802 propertyToDelete.name,
1804 STGM_SHARE_EXCLUSIVE,
1807 &childStorage);
1809 if (hr != S_OK)
1811 return hr;
1815 * Enumerate the elements
1817 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1822 * Obtain the next element
1824 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1825 if (hr==S_OK)
1827 destroyHr = StorageImpl_DestroyElement(
1828 (IStorage*)childStorage,
1829 (OLECHAR*)currentElement.pwcsName);
1831 CoTaskMemFree(currentElement.pwcsName);
1835 * We need to Reset the enumeration every time because we delete elements
1836 * and the enumeration could be invalid
1838 IEnumSTATSTG_Reset(elements);
1840 } while ((hr == S_OK) && (destroyHr == S_OK));
1843 * Invalidate the property by zeroing it's name member.
1845 propertyToDelete.sizeOfNameString = 0;
1847 StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1848 indexOfPropertyToDelete,
1849 &propertyToDelete);
1851 IStorage_Release(childStorage);
1852 IEnumSTATSTG_Release(elements);
1854 return destroyHr;
1857 /*********************************************************************
1859 * Internal Method
1861 * Perform the deletion of a stream node
1864 static HRESULT deleteStreamProperty(
1865 StorageImpl *parentStorage,
1866 ULONG indexOfPropertyToDelete,
1867 StgProperty propertyToDelete)
1869 IStream *pis;
1870 HRESULT hr;
1871 ULARGE_INTEGER size;
1873 size.s.HighPart = 0;
1874 size.s.LowPart = 0;
1876 hr = StorageBaseImpl_OpenStream(
1877 (IStorage*)parentStorage,
1878 (OLECHAR*)propertyToDelete.name,
1879 NULL,
1880 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1882 &pis);
1884 if (hr!=S_OK)
1886 return(hr);
1890 * Zap the stream
1892 hr = IStream_SetSize(pis, size);
1894 if(hr != S_OK)
1896 return hr;
1900 * Release the stream object.
1902 IStream_Release(pis);
1905 * Invalidate the property by zeroing it's name member.
1907 propertyToDelete.sizeOfNameString = 0;
1910 * Here we should re-read the property so we get the updated pointer
1911 * but since we are here to zap it, I don't do it...
1913 StorageImpl_WriteProperty(
1914 parentStorage->ancestorStorage,
1915 indexOfPropertyToDelete,
1916 &propertyToDelete);
1918 return S_OK;
1921 /*********************************************************************
1923 * Internal Method
1925 * Finds a placeholder for the StgProperty within the Storage
1928 static HRESULT findPlaceholder(
1929 StorageImpl *storage,
1930 ULONG propertyIndexToStore,
1931 ULONG storePropertyIndex,
1932 INT typeOfRelation)
1934 StgProperty storeProperty;
1935 HRESULT hr = S_OK;
1936 BOOL res = TRUE;
1939 * Read the storage property
1941 res = StorageImpl_ReadProperty(
1942 storage->ancestorStorage,
1943 storePropertyIndex,
1944 &storeProperty);
1946 if(! res)
1948 return E_FAIL;
1951 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1953 if (storeProperty.previousProperty != PROPERTY_NULL)
1955 return findPlaceholder(
1956 storage,
1957 propertyIndexToStore,
1958 storeProperty.previousProperty,
1959 typeOfRelation);
1961 else
1963 storeProperty.previousProperty = propertyIndexToStore;
1966 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1968 if (storeProperty.nextProperty != PROPERTY_NULL)
1970 return findPlaceholder(
1971 storage,
1972 propertyIndexToStore,
1973 storeProperty.nextProperty,
1974 typeOfRelation);
1976 else
1978 storeProperty.nextProperty = propertyIndexToStore;
1981 else if (typeOfRelation == PROPERTY_RELATION_DIR)
1983 if (storeProperty.dirProperty != PROPERTY_NULL)
1985 return findPlaceholder(
1986 storage,
1987 propertyIndexToStore,
1988 storeProperty.dirProperty,
1989 typeOfRelation);
1991 else
1993 storeProperty.dirProperty = propertyIndexToStore;
1997 hr = StorageImpl_WriteProperty(
1998 storage->ancestorStorage,
1999 storePropertyIndex,
2000 &storeProperty);
2002 if(! hr)
2004 return E_FAIL;
2007 return S_OK;
2010 /*************************************************************************
2012 * Internal Method
2014 * This method takes the previous and the next property link of a property
2015 * to be deleted and find them a place in the Storage.
2017 static HRESULT adjustPropertyChain(
2018 StorageImpl *This,
2019 StgProperty propertyToDelete,
2020 StgProperty parentProperty,
2021 ULONG parentPropertyId,
2022 INT typeOfRelation)
2024 ULONG newLinkProperty = PROPERTY_NULL;
2025 BOOL needToFindAPlaceholder = FALSE;
2026 ULONG storeNode = PROPERTY_NULL;
2027 ULONG toStoreNode = PROPERTY_NULL;
2028 INT relationType = 0;
2029 HRESULT hr = S_OK;
2030 BOOL res = TRUE;
2032 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2034 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2037 * Set the parent previous to the property to delete previous
2039 newLinkProperty = propertyToDelete.previousProperty;
2041 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2044 * We also need to find a storage for the other link, setup variables
2045 * to do this at the end...
2047 needToFindAPlaceholder = TRUE;
2048 storeNode = propertyToDelete.previousProperty;
2049 toStoreNode = propertyToDelete.nextProperty;
2050 relationType = PROPERTY_RELATION_NEXT;
2053 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2056 * Set the parent previous to the property to delete next
2058 newLinkProperty = propertyToDelete.nextProperty;
2062 * Link it for real...
2064 parentProperty.previousProperty = newLinkProperty;
2067 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2069 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2072 * Set the parent next to the property to delete next previous
2074 newLinkProperty = propertyToDelete.previousProperty;
2076 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2079 * We also need to find a storage for the other link, setup variables
2080 * to do this at the end...
2082 needToFindAPlaceholder = TRUE;
2083 storeNode = propertyToDelete.previousProperty;
2084 toStoreNode = propertyToDelete.nextProperty;
2085 relationType = PROPERTY_RELATION_NEXT;
2088 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2091 * Set the parent next to the property to delete next
2093 newLinkProperty = propertyToDelete.nextProperty;
2097 * Link it for real...
2099 parentProperty.nextProperty = newLinkProperty;
2101 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2103 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2106 * Set the parent dir to the property to delete previous
2108 newLinkProperty = propertyToDelete.previousProperty;
2110 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2113 * We also need to find a storage for the other link, setup variables
2114 * to do this at the end...
2116 needToFindAPlaceholder = TRUE;
2117 storeNode = propertyToDelete.previousProperty;
2118 toStoreNode = propertyToDelete.nextProperty;
2119 relationType = PROPERTY_RELATION_NEXT;
2122 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2125 * Set the parent dir to the property to delete next
2127 newLinkProperty = propertyToDelete.nextProperty;
2131 * Link it for real...
2133 parentProperty.dirProperty = newLinkProperty;
2137 * Write back the parent property
2139 res = StorageImpl_WriteProperty(
2140 This->ancestorStorage,
2141 parentPropertyId,
2142 &parentProperty);
2143 if(! res)
2145 return E_FAIL;
2149 * If a placeholder is required for the other link, then, find one and
2150 * get out of here...
2152 if (needToFindAPlaceholder)
2154 hr = findPlaceholder(
2155 This,
2156 toStoreNode,
2157 storeNode,
2158 relationType);
2161 return hr;
2165 /******************************************************************************
2166 * SetElementTimes (IStorage)
2168 HRESULT WINAPI StorageImpl_SetElementTimes(
2169 IStorage* iface,
2170 const OLECHAR *pwcsName,/* [string][in] */
2171 const FILETIME *pctime, /* [in] */
2172 const FILETIME *patime, /* [in] */
2173 const FILETIME *pmtime) /* [in] */
2175 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2176 return S_OK;
2179 /******************************************************************************
2180 * SetStateBits (IStorage)
2182 HRESULT WINAPI StorageImpl_SetStateBits(
2183 IStorage* iface,
2184 DWORD grfStateBits,/* [in] */
2185 DWORD grfMask) /* [in] */
2187 FIXME("not implemented!\n");
2188 return E_NOTIMPL;
2191 HRESULT StorageImpl_Construct(
2192 StorageImpl* This,
2193 HANDLE hFile,
2194 LPCOLESTR pwcsName,
2195 ILockBytes* pLkbyt,
2196 DWORD openFlags,
2197 BOOL fileBased,
2198 BOOL fileCreate)
2200 HRESULT hr = S_OK;
2201 StgProperty currentProperty;
2202 BOOL readSuccessful;
2203 ULONG currentPropertyIndex;
2205 if ( FAILED( validateSTGM(openFlags) ))
2206 return STG_E_INVALIDFLAG;
2208 memset(This, 0, sizeof(StorageImpl));
2211 * Initialize the virtual function table.
2213 This->lpVtbl = &Storage32Impl_Vtbl;
2214 This->v_destructor = &StorageImpl_Destroy;
2217 * This is the top-level storage so initialize the ancestor pointer
2218 * to this.
2220 This->ancestorStorage = This;
2223 * Initialize the physical support of the storage.
2225 This->hFile = hFile;
2228 * Store copy of file path.
2230 if(pwcsName) {
2231 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2232 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2233 if (!This->pwcsName)
2234 return STG_E_INSUFFICIENTMEMORY;
2235 strcpyW(This->pwcsName, pwcsName);
2239 * Initialize the big block cache.
2241 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2242 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2243 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2244 pLkbyt,
2245 openFlags,
2246 This->bigBlockSize,
2247 fileBased);
2249 if (This->bigBlockFile == 0)
2250 return E_FAIL;
2252 if (fileCreate)
2254 ULARGE_INTEGER size;
2255 BYTE* bigBlockBuffer;
2258 * Initialize all header variables:
2259 * - The big block depot consists of one block and it is at block 0
2260 * - The properties start at block 1
2261 * - There is no small block depot
2263 memset( This->bigBlockDepotStart,
2264 BLOCK_UNUSED,
2265 sizeof(This->bigBlockDepotStart));
2267 This->bigBlockDepotCount = 1;
2268 This->bigBlockDepotStart[0] = 0;
2269 This->rootStartBlock = 1;
2270 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2271 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2272 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2273 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2274 This->extBigBlockDepotCount = 0;
2276 StorageImpl_SaveFileHeader(This);
2279 * Add one block for the big block depot and one block for the properties
2281 size.s.HighPart = 0;
2282 size.s.LowPart = This->bigBlockSize * 3;
2283 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2286 * Initialize the big block depot
2288 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2289 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2290 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2291 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2292 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2294 else
2297 * Load the header for the file.
2299 hr = StorageImpl_LoadFileHeader(This);
2301 if (FAILED(hr))
2303 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2305 return hr;
2310 * There is no block depot cached yet.
2312 This->indexBlockDepotCached = 0xFFFFFFFF;
2315 * Start searching for free blocks with block 0.
2317 This->prevFreeBlock = 0;
2320 * Create the block chain abstractions.
2322 if(!(This->rootBlockChain =
2323 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2324 return STG_E_READFAULT;
2326 if(!(This->smallBlockDepotChain =
2327 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2328 PROPERTY_NULL)))
2329 return STG_E_READFAULT;
2332 * Write the root property
2334 if (fileCreate)
2336 StgProperty rootProp;
2338 * Initialize the property chain
2340 memset(&rootProp, 0, sizeof(rootProp));
2341 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2342 sizeof(rootProp.name)/sizeof(WCHAR) );
2343 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2344 rootProp.propertyType = PROPTYPE_ROOT;
2345 rootProp.previousProperty = PROPERTY_NULL;
2346 rootProp.nextProperty = PROPERTY_NULL;
2347 rootProp.dirProperty = PROPERTY_NULL;
2348 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2349 rootProp.size.s.HighPart = 0;
2350 rootProp.size.s.LowPart = 0;
2352 StorageImpl_WriteProperty(This, 0, &rootProp);
2356 * Find the ID of the root in the property sets.
2358 currentPropertyIndex = 0;
2362 readSuccessful = StorageImpl_ReadProperty(
2363 This,
2364 currentPropertyIndex,
2365 &currentProperty);
2367 if (readSuccessful)
2369 if ( (currentProperty.sizeOfNameString != 0 ) &&
2370 (currentProperty.propertyType == PROPTYPE_ROOT) )
2372 This->rootPropertySetIndex = currentPropertyIndex;
2376 currentPropertyIndex++;
2378 } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2380 if (!readSuccessful)
2382 /* TODO CLEANUP */
2383 return STG_E_READFAULT;
2387 * Create the block chain abstraction for the small block root chain.
2389 if(!(This->smallBlockRootChain =
2390 BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2391 return STG_E_READFAULT;
2393 return hr;
2396 void StorageImpl_Destroy(
2397 StorageImpl* This)
2399 TRACE("(%p)\n", This);
2401 if(This->pwcsName)
2402 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2404 BlockChainStream_Destroy(This->smallBlockRootChain);
2405 BlockChainStream_Destroy(This->rootBlockChain);
2406 BlockChainStream_Destroy(This->smallBlockDepotChain);
2408 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2409 return;
2412 /******************************************************************************
2413 * Storage32Impl_GetNextFreeBigBlock
2415 * Returns the index of the next free big block.
2416 * If the big block depot is filled, this method will enlarge it.
2419 ULONG StorageImpl_GetNextFreeBigBlock(
2420 StorageImpl* This)
2422 ULONG depotBlockIndexPos;
2423 void *depotBuffer;
2424 ULONG depotBlockOffset;
2425 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2426 ULONG nextBlockIndex = BLOCK_SPECIAL;
2427 int depotIndex = 0;
2428 ULONG freeBlock = BLOCK_UNUSED;
2430 depotIndex = This->prevFreeBlock / blocksPerDepot;
2431 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2434 * Scan the entire big block depot until we find a block marked free
2436 while (nextBlockIndex != BLOCK_UNUSED)
2438 if (depotIndex < COUNT_BBDEPOTINHEADER)
2440 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2443 * Grow the primary depot.
2445 if (depotBlockIndexPos == BLOCK_UNUSED)
2447 depotBlockIndexPos = depotIndex*blocksPerDepot;
2450 * Add a block depot.
2452 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2453 This->bigBlockDepotCount++;
2454 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2457 * Flag it as a block depot.
2459 StorageImpl_SetNextBlockInChain(This,
2460 depotBlockIndexPos,
2461 BLOCK_SPECIAL);
2463 /* Save new header information.
2465 StorageImpl_SaveFileHeader(This);
2468 else
2470 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2472 if (depotBlockIndexPos == BLOCK_UNUSED)
2475 * Grow the extended depot.
2477 ULONG extIndex = BLOCK_UNUSED;
2478 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2479 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2481 if (extBlockOffset == 0)
2483 /* We need an extended block.
2485 extIndex = Storage32Impl_AddExtBlockDepot(This);
2486 This->extBigBlockDepotCount++;
2487 depotBlockIndexPos = extIndex + 1;
2489 else
2490 depotBlockIndexPos = depotIndex * blocksPerDepot;
2493 * Add a block depot and mark it in the extended block.
2495 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2496 This->bigBlockDepotCount++;
2497 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2499 /* Flag the block depot.
2501 StorageImpl_SetNextBlockInChain(This,
2502 depotBlockIndexPos,
2503 BLOCK_SPECIAL);
2505 /* If necessary, flag the extended depot block.
2507 if (extIndex != BLOCK_UNUSED)
2508 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2510 /* Save header information.
2512 StorageImpl_SaveFileHeader(This);
2516 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2518 if (depotBuffer != 0)
2520 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2521 ( nextBlockIndex != BLOCK_UNUSED))
2523 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2525 if (nextBlockIndex == BLOCK_UNUSED)
2527 freeBlock = (depotIndex * blocksPerDepot) +
2528 (depotBlockOffset/sizeof(ULONG));
2531 depotBlockOffset += sizeof(ULONG);
2534 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2537 depotIndex++;
2538 depotBlockOffset = 0;
2541 This->prevFreeBlock = freeBlock;
2543 return freeBlock;
2546 /******************************************************************************
2547 * Storage32Impl_AddBlockDepot
2549 * This will create a depot block, essentially it is a block initialized
2550 * to BLOCK_UNUSEDs.
2552 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2554 BYTE* blockBuffer;
2556 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2559 * Initialize blocks as free
2561 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2563 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2566 /******************************************************************************
2567 * Storage32Impl_GetExtDepotBlock
2569 * Returns the index of the block that corresponds to the specified depot
2570 * index. This method is only for depot indexes equal or greater than
2571 * COUNT_BBDEPOTINHEADER.
2573 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2575 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2576 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2577 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2578 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2579 ULONG blockIndex = BLOCK_UNUSED;
2580 ULONG extBlockIndex = This->extBigBlockDepotStart;
2582 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2584 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2585 return BLOCK_UNUSED;
2587 while (extBlockCount > 0)
2589 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2590 extBlockCount--;
2593 if (extBlockIndex != BLOCK_UNUSED)
2595 BYTE* depotBuffer;
2597 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2599 if (depotBuffer != 0)
2601 StorageUtl_ReadDWord(depotBuffer,
2602 extBlockOffset * sizeof(ULONG),
2603 &blockIndex);
2605 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2609 return blockIndex;
2612 /******************************************************************************
2613 * Storage32Impl_SetExtDepotBlock
2615 * Associates the specified block index to the specified depot index.
2616 * This method is only for depot indexes equal or greater than
2617 * COUNT_BBDEPOTINHEADER.
2619 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2620 ULONG depotIndex,
2621 ULONG blockIndex)
2623 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2624 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2625 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2626 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2627 ULONG extBlockIndex = This->extBigBlockDepotStart;
2629 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2631 while (extBlockCount > 0)
2633 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2634 extBlockCount--;
2637 if (extBlockIndex != BLOCK_UNUSED)
2639 BYTE* depotBuffer;
2641 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2643 if (depotBuffer != 0)
2645 StorageUtl_WriteDWord(depotBuffer,
2646 extBlockOffset * sizeof(ULONG),
2647 blockIndex);
2649 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2654 /******************************************************************************
2655 * Storage32Impl_AddExtBlockDepot
2657 * Creates an extended depot block.
2659 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2661 ULONG numExtBlocks = This->extBigBlockDepotCount;
2662 ULONG nextExtBlock = This->extBigBlockDepotStart;
2663 BYTE* depotBuffer = NULL;
2664 ULONG index = BLOCK_UNUSED;
2665 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2666 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2667 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2669 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2670 blocksPerDepotBlock;
2672 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2675 * The first extended block.
2677 This->extBigBlockDepotStart = index;
2679 else
2681 int i;
2683 * Follow the chain to the last one.
2685 for (i = 0; i < (numExtBlocks - 1); i++)
2687 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2691 * Add the new extended block to the chain.
2693 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2694 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2695 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2699 * Initialize this block.
2701 depotBuffer = StorageImpl_GetBigBlock(This, index);
2702 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2703 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2705 return index;
2708 /******************************************************************************
2709 * Storage32Impl_FreeBigBlock
2711 * This method will flag the specified block as free in the big block depot.
2713 void StorageImpl_FreeBigBlock(
2714 StorageImpl* This,
2715 ULONG blockIndex)
2717 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2719 if (blockIndex < This->prevFreeBlock)
2720 This->prevFreeBlock = blockIndex;
2723 /************************************************************************
2724 * Storage32Impl_GetNextBlockInChain
2726 * This method will retrieve the block index of the next big block in
2727 * in the chain.
2729 * Params: This - Pointer to the Storage object.
2730 * blockIndex - Index of the block to retrieve the chain
2731 * for.
2732 * nextBlockIndex - receives the return value.
2734 * Returns: This method returns the index of the next block in the chain.
2735 * It will return the constants:
2736 * BLOCK_SPECIAL - If the block given was not part of a
2737 * chain.
2738 * BLOCK_END_OF_CHAIN - If the block given was the last in
2739 * a chain.
2740 * BLOCK_UNUSED - If the block given was not past of a chain
2741 * and is available.
2742 * BLOCK_EXTBBDEPOT - This block is part of the extended
2743 * big block depot.
2745 * See Windows documentation for more details on IStorage methods.
2747 HRESULT StorageImpl_GetNextBlockInChain(
2748 StorageImpl* This,
2749 ULONG blockIndex,
2750 ULONG* nextBlockIndex)
2752 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2753 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2754 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2755 void* depotBuffer;
2756 ULONG depotBlockIndexPos;
2757 int index;
2759 *nextBlockIndex = BLOCK_SPECIAL;
2761 if(depotBlockCount >= This->bigBlockDepotCount)
2763 WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2764 This->bigBlockDepotCount);
2765 return STG_E_READFAULT;
2769 * Cache the currently accessed depot block.
2771 if (depotBlockCount != This->indexBlockDepotCached)
2773 This->indexBlockDepotCached = depotBlockCount;
2775 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2777 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2779 else
2782 * We have to look in the extended depot.
2784 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2787 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2789 if (!depotBuffer)
2790 return STG_E_READFAULT;
2792 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2794 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2795 This->blockDepotCached[index] = *nextBlockIndex;
2797 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2800 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2802 return S_OK;
2805 /******************************************************************************
2806 * Storage32Impl_GetNextExtendedBlock
2808 * Given an extended block this method will return the next extended block.
2810 * NOTES:
2811 * The last ULONG of an extended block is the block index of the next
2812 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2813 * depot.
2815 * Return values:
2816 * - The index of the next extended block
2817 * - BLOCK_UNUSED: there is no next extended block.
2818 * - Any other return values denotes failure.
2820 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2822 ULONG nextBlockIndex = BLOCK_SPECIAL;
2823 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2824 void* depotBuffer;
2826 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2828 if (depotBuffer!=0)
2830 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2832 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2835 return nextBlockIndex;
2838 /******************************************************************************
2839 * Storage32Impl_SetNextBlockInChain
2841 * This method will write the index of the specified block's next block
2842 * in the big block depot.
2844 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2845 * do the following
2847 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2848 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2849 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2852 void StorageImpl_SetNextBlockInChain(
2853 StorageImpl* This,
2854 ULONG blockIndex,
2855 ULONG nextBlock)
2857 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2858 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2859 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2860 ULONG depotBlockIndexPos;
2861 void* depotBuffer;
2863 assert(depotBlockCount < This->bigBlockDepotCount);
2864 assert(blockIndex != nextBlock);
2866 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2868 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2870 else
2873 * We have to look in the extended depot.
2875 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2878 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2880 if (depotBuffer!=0)
2882 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2883 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2887 * Update the cached block depot, if necessary.
2889 if (depotBlockCount == This->indexBlockDepotCached)
2891 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2895 /******************************************************************************
2896 * Storage32Impl_LoadFileHeader
2898 * This method will read in the file header, i.e. big block index -1.
2900 HRESULT StorageImpl_LoadFileHeader(
2901 StorageImpl* This)
2903 HRESULT hr = STG_E_FILENOTFOUND;
2904 void* headerBigBlock = NULL;
2905 int index;
2908 * Get a pointer to the big block of data containing the header.
2910 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2913 * Extract the information from the header.
2915 if (headerBigBlock!=0)
2918 * Check for the "magic number" signature and return an error if it is not
2919 * found.
2921 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2923 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2924 return STG_E_OLDFORMAT;
2927 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2929 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2930 return STG_E_INVALIDHEADER;
2933 StorageUtl_ReadWord(
2934 headerBigBlock,
2935 OFFSET_BIGBLOCKSIZEBITS,
2936 &This->bigBlockSizeBits);
2938 StorageUtl_ReadWord(
2939 headerBigBlock,
2940 OFFSET_SMALLBLOCKSIZEBITS,
2941 &This->smallBlockSizeBits);
2943 StorageUtl_ReadDWord(
2944 headerBigBlock,
2945 OFFSET_BBDEPOTCOUNT,
2946 &This->bigBlockDepotCount);
2948 StorageUtl_ReadDWord(
2949 headerBigBlock,
2950 OFFSET_ROOTSTARTBLOCK,
2951 &This->rootStartBlock);
2953 StorageUtl_ReadDWord(
2954 headerBigBlock,
2955 OFFSET_SBDEPOTSTART,
2956 &This->smallBlockDepotStart);
2958 StorageUtl_ReadDWord(
2959 headerBigBlock,
2960 OFFSET_EXTBBDEPOTSTART,
2961 &This->extBigBlockDepotStart);
2963 StorageUtl_ReadDWord(
2964 headerBigBlock,
2965 OFFSET_EXTBBDEPOTCOUNT,
2966 &This->extBigBlockDepotCount);
2968 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2970 StorageUtl_ReadDWord(
2971 headerBigBlock,
2972 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2973 &(This->bigBlockDepotStart[index]));
2977 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2979 if ((1 << 2) == 4)
2981 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2982 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2984 else
2986 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2987 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2991 * Right now, the code is making some assumptions about the size of the
2992 * blocks, just make sure they are what we're expecting.
2994 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2995 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2997 WARN("Broken OLE storage file\n");
2998 hr = STG_E_INVALIDHEADER;
3000 else
3001 hr = S_OK;
3004 * Release the block.
3006 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3009 return hr;
3012 /******************************************************************************
3013 * Storage32Impl_SaveFileHeader
3015 * This method will save to the file the header, i.e. big block -1.
3017 void StorageImpl_SaveFileHeader(
3018 StorageImpl* This)
3020 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3021 int index;
3022 BOOL success;
3025 * Get a pointer to the big block of data containing the header.
3027 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3030 * If the block read failed, the file is probably new.
3032 if (!success)
3035 * Initialize for all unknown fields.
3037 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3040 * Initialize the magic number.
3042 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3045 * And a bunch of things we don't know what they mean
3047 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3048 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3049 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3050 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3054 * Write the information to the header.
3056 StorageUtl_WriteWord(
3057 headerBigBlock,
3058 OFFSET_BIGBLOCKSIZEBITS,
3059 This->bigBlockSizeBits);
3061 StorageUtl_WriteWord(
3062 headerBigBlock,
3063 OFFSET_SMALLBLOCKSIZEBITS,
3064 This->smallBlockSizeBits);
3066 StorageUtl_WriteDWord(
3067 headerBigBlock,
3068 OFFSET_BBDEPOTCOUNT,
3069 This->bigBlockDepotCount);
3071 StorageUtl_WriteDWord(
3072 headerBigBlock,
3073 OFFSET_ROOTSTARTBLOCK,
3074 This->rootStartBlock);
3076 StorageUtl_WriteDWord(
3077 headerBigBlock,
3078 OFFSET_SBDEPOTSTART,
3079 This->smallBlockDepotStart);
3081 StorageUtl_WriteDWord(
3082 headerBigBlock,
3083 OFFSET_SBDEPOTCOUNT,
3084 This->smallBlockDepotChain ?
3085 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3087 StorageUtl_WriteDWord(
3088 headerBigBlock,
3089 OFFSET_EXTBBDEPOTSTART,
3090 This->extBigBlockDepotStart);
3092 StorageUtl_WriteDWord(
3093 headerBigBlock,
3094 OFFSET_EXTBBDEPOTCOUNT,
3095 This->extBigBlockDepotCount);
3097 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3099 StorageUtl_WriteDWord(
3100 headerBigBlock,
3101 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3102 (This->bigBlockDepotStart[index]));
3106 * Write the big block back to the file.
3108 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3111 /******************************************************************************
3112 * Storage32Impl_ReadProperty
3114 * This method will read the specified property from the property chain.
3116 BOOL StorageImpl_ReadProperty(
3117 StorageImpl* This,
3118 ULONG index,
3119 StgProperty* buffer)
3121 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3122 ULARGE_INTEGER offsetInPropSet;
3123 BOOL readSuccessful;
3124 ULONG bytesRead;
3126 offsetInPropSet.s.HighPart = 0;
3127 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3129 readSuccessful = BlockChainStream_ReadAt(
3130 This->rootBlockChain,
3131 offsetInPropSet,
3132 PROPSET_BLOCK_SIZE,
3133 currentProperty,
3134 &bytesRead);
3136 if (readSuccessful)
3138 /* replace the name of root entry (often "Root Entry") by the file name */
3139 WCHAR *propName = (index == This->rootPropertySetIndex) ?
3140 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3142 memset(buffer->name, 0, sizeof(buffer->name));
3143 memcpy(
3144 buffer->name,
3145 propName,
3146 PROPERTY_NAME_BUFFER_LEN );
3147 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3149 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3151 StorageUtl_ReadWord(
3152 currentProperty,
3153 OFFSET_PS_NAMELENGTH,
3154 &buffer->sizeOfNameString);
3156 StorageUtl_ReadDWord(
3157 currentProperty,
3158 OFFSET_PS_PREVIOUSPROP,
3159 &buffer->previousProperty);
3161 StorageUtl_ReadDWord(
3162 currentProperty,
3163 OFFSET_PS_NEXTPROP,
3164 &buffer->nextProperty);
3166 StorageUtl_ReadDWord(
3167 currentProperty,
3168 OFFSET_PS_DIRPROP,
3169 &buffer->dirProperty);
3171 StorageUtl_ReadGUID(
3172 currentProperty,
3173 OFFSET_PS_GUID,
3174 &buffer->propertyUniqueID);
3176 StorageUtl_ReadDWord(
3177 currentProperty,
3178 OFFSET_PS_TSS1,
3179 &buffer->timeStampS1);
3181 StorageUtl_ReadDWord(
3182 currentProperty,
3183 OFFSET_PS_TSD1,
3184 &buffer->timeStampD1);
3186 StorageUtl_ReadDWord(
3187 currentProperty,
3188 OFFSET_PS_TSS2,
3189 &buffer->timeStampS2);
3191 StorageUtl_ReadDWord(
3192 currentProperty,
3193 OFFSET_PS_TSD2,
3194 &buffer->timeStampD2);
3196 StorageUtl_ReadDWord(
3197 currentProperty,
3198 OFFSET_PS_STARTBLOCK,
3199 &buffer->startingBlock);
3201 StorageUtl_ReadDWord(
3202 currentProperty,
3203 OFFSET_PS_SIZE,
3204 &buffer->size.s.LowPart);
3206 buffer->size.s.HighPart = 0;
3209 return readSuccessful;
3212 /*********************************************************************
3213 * Write the specified property into the property chain
3215 BOOL StorageImpl_WriteProperty(
3216 StorageImpl* This,
3217 ULONG index,
3218 StgProperty* buffer)
3220 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3221 ULARGE_INTEGER offsetInPropSet;
3222 BOOL writeSuccessful;
3223 ULONG bytesWritten;
3225 offsetInPropSet.s.HighPart = 0;
3226 offsetInPropSet.s.LowPart = index * PROPSET_BLOCK_SIZE;
3228 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3230 memcpy(
3231 currentProperty + OFFSET_PS_NAME,
3232 buffer->name,
3233 PROPERTY_NAME_BUFFER_LEN );
3235 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3237 StorageUtl_WriteWord(
3238 currentProperty,
3239 OFFSET_PS_NAMELENGTH,
3240 buffer->sizeOfNameString);
3242 StorageUtl_WriteDWord(
3243 currentProperty,
3244 OFFSET_PS_PREVIOUSPROP,
3245 buffer->previousProperty);
3247 StorageUtl_WriteDWord(
3248 currentProperty,
3249 OFFSET_PS_NEXTPROP,
3250 buffer->nextProperty);
3252 StorageUtl_WriteDWord(
3253 currentProperty,
3254 OFFSET_PS_DIRPROP,
3255 buffer->dirProperty);
3257 StorageUtl_WriteGUID(
3258 currentProperty,
3259 OFFSET_PS_GUID,
3260 &buffer->propertyUniqueID);
3262 StorageUtl_WriteDWord(
3263 currentProperty,
3264 OFFSET_PS_TSS1,
3265 buffer->timeStampS1);
3267 StorageUtl_WriteDWord(
3268 currentProperty,
3269 OFFSET_PS_TSD1,
3270 buffer->timeStampD1);
3272 StorageUtl_WriteDWord(
3273 currentProperty,
3274 OFFSET_PS_TSS2,
3275 buffer->timeStampS2);
3277 StorageUtl_WriteDWord(
3278 currentProperty,
3279 OFFSET_PS_TSD2,
3280 buffer->timeStampD2);
3282 StorageUtl_WriteDWord(
3283 currentProperty,
3284 OFFSET_PS_STARTBLOCK,
3285 buffer->startingBlock);
3287 StorageUtl_WriteDWord(
3288 currentProperty,
3289 OFFSET_PS_SIZE,
3290 buffer->size.s.LowPart);
3292 writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3293 offsetInPropSet,
3294 PROPSET_BLOCK_SIZE,
3295 currentProperty,
3296 &bytesWritten);
3297 return writeSuccessful;
3300 BOOL StorageImpl_ReadBigBlock(
3301 StorageImpl* This,
3302 ULONG blockIndex,
3303 void* buffer)
3305 void* bigBlockBuffer;
3307 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3309 if (bigBlockBuffer!=0)
3311 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3313 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3315 return TRUE;
3318 return FALSE;
3321 BOOL StorageImpl_WriteBigBlock(
3322 StorageImpl* This,
3323 ULONG blockIndex,
3324 void* buffer)
3326 void* bigBlockBuffer;
3328 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3330 if (bigBlockBuffer!=0)
3332 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3334 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3336 return TRUE;
3339 return FALSE;
3342 void* StorageImpl_GetROBigBlock(
3343 StorageImpl* This,
3344 ULONG blockIndex)
3346 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3349 void* StorageImpl_GetBigBlock(
3350 StorageImpl* This,
3351 ULONG blockIndex)
3353 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3356 void StorageImpl_ReleaseBigBlock(
3357 StorageImpl* This,
3358 void* pBigBlock)
3360 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3363 /******************************************************************************
3364 * Storage32Impl_SmallBlocksToBigBlocks
3366 * This method will convert a small block chain to a big block chain.
3367 * The small block chain will be destroyed.
3369 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3370 StorageImpl* This,
3371 SmallBlockChainStream** ppsbChain)
3373 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3374 ULARGE_INTEGER size, offset;
3375 ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3376 ULONG propertyIndex;
3377 BOOL successRead, successWrite;
3378 StgProperty chainProperty;
3379 BYTE *buffer;
3380 BlockChainStream *bbTempChain = NULL;
3381 BlockChainStream *bigBlockChain = NULL;
3384 * Create a temporary big block chain that doesn't have
3385 * an associated property. This temporary chain will be
3386 * used to copy data from small blocks to big blocks.
3388 bbTempChain = BlockChainStream_Construct(This,
3389 &bbHeadOfChain,
3390 PROPERTY_NULL);
3391 if(!bbTempChain) return NULL;
3393 * Grow the big block chain.
3395 size = SmallBlockChainStream_GetSize(*ppsbChain);
3396 BlockChainStream_SetSize(bbTempChain, size);
3399 * Copy the contents of the small block chain to the big block chain
3400 * by small block size increments.
3402 offset.s.LowPart = 0;
3403 offset.s.HighPart = 0;
3404 cbTotalRead = 0;
3405 cbTotalWritten = 0;
3407 buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3410 successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3411 offset,
3412 DEF_SMALL_BLOCK_SIZE,
3413 buffer,
3414 &cbRead);
3415 cbTotalRead += cbRead;
3417 successWrite = BlockChainStream_WriteAt(bbTempChain,
3418 offset,
3419 cbRead,
3420 buffer,
3421 &cbWritten);
3422 cbTotalWritten += cbWritten;
3424 offset.s.LowPart += This->smallBlockSize;
3426 } while (successRead && successWrite);
3427 HeapFree(GetProcessHeap(),0,buffer);
3429 assert(cbTotalRead == cbTotalWritten);
3432 * Destroy the small block chain.
3434 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3435 size.s.HighPart = 0;
3436 size.s.LowPart = 0;
3437 SmallBlockChainStream_SetSize(*ppsbChain, size);
3438 SmallBlockChainStream_Destroy(*ppsbChain);
3439 *ppsbChain = 0;
3442 * Change the property information. This chain is now a big block chain
3443 * and it doesn't reside in the small blocks chain anymore.
3445 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3447 chainProperty.startingBlock = bbHeadOfChain;
3449 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3452 * Destroy the temporary propertyless big block chain.
3453 * Create a new big block chain associated with this property.
3455 BlockChainStream_Destroy(bbTempChain);
3456 bigBlockChain = BlockChainStream_Construct(This,
3457 NULL,
3458 propertyIndex);
3460 return bigBlockChain;
3463 /******************************************************************************
3464 ** Storage32InternalImpl implementation
3467 StorageInternalImpl* StorageInternalImpl_Construct(
3468 StorageImpl* ancestorStorage,
3469 ULONG rootPropertyIndex)
3471 StorageInternalImpl* newStorage;
3474 * Allocate space for the new storage object
3476 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3478 if (newStorage!=0)
3480 memset(newStorage, 0, sizeof(StorageInternalImpl));
3483 * Initialize the virtual function table.
3485 newStorage->lpVtbl = &Storage32InternalImpl_Vtbl;
3486 newStorage->v_destructor = &StorageInternalImpl_Destroy;
3489 * Keep the ancestor storage pointer and nail a reference to it.
3491 newStorage->ancestorStorage = ancestorStorage;
3492 StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3495 * Keep the index of the root property set for this storage,
3497 newStorage->rootPropertySetIndex = rootPropertyIndex;
3499 return newStorage;
3502 return 0;
3505 void StorageInternalImpl_Destroy(
3506 StorageInternalImpl* This)
3508 StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3509 HeapFree(GetProcessHeap(), 0, This);
3512 /******************************************************************************
3514 ** Storage32InternalImpl_Commit
3516 ** The non-root storages cannot be opened in transacted mode thus this function
3517 ** does nothing.
3519 HRESULT WINAPI StorageInternalImpl_Commit(
3520 IStorage* iface,
3521 DWORD grfCommitFlags) /* [in] */
3523 return S_OK;
3526 /******************************************************************************
3528 ** Storage32InternalImpl_Revert
3530 ** The non-root storages cannot be opened in transacted mode thus this function
3531 ** does nothing.
3533 HRESULT WINAPI StorageInternalImpl_Revert(
3534 IStorage* iface)
3536 return S_OK;
3539 /******************************************************************************
3540 ** IEnumSTATSTGImpl implementation
3543 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3544 StorageImpl* parentStorage,
3545 ULONG firstPropertyNode)
3547 IEnumSTATSTGImpl* newEnumeration;
3549 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3551 if (newEnumeration!=0)
3554 * Set-up the virtual function table and reference count.
3556 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3557 newEnumeration->ref = 0;
3560 * We want to nail-down the reference to the storage in case the
3561 * enumeration out-lives the storage in the client application.
3563 newEnumeration->parentStorage = parentStorage;
3564 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3566 newEnumeration->firstPropertyNode = firstPropertyNode;
3569 * Initialize the search stack
3571 newEnumeration->stackSize = 0;
3572 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3573 newEnumeration->stackToVisit =
3574 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3577 * Make sure the current node of the iterator is the first one.
3579 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3582 return newEnumeration;
3585 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3587 IStorage_Release((IStorage*)This->parentStorage);
3588 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3589 HeapFree(GetProcessHeap(), 0, This);
3592 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3593 IEnumSTATSTG* iface,
3594 REFIID riid,
3595 void** ppvObject)
3597 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3600 * Perform a sanity check on the parameters.
3602 if (ppvObject==0)
3603 return E_INVALIDARG;
3606 * Initialize the return parameter.
3608 *ppvObject = 0;
3611 * Compare the riid with the interface IDs implemented by this object.
3613 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3615 *ppvObject = (IEnumSTATSTG*)This;
3617 else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3619 *ppvObject = (IEnumSTATSTG*)This;
3623 * Check that we obtained an interface.
3625 if ((*ppvObject)==0)
3626 return E_NOINTERFACE;
3629 * Query Interface always increases the reference count by one when it is
3630 * successful
3632 IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3634 return S_OK;
3637 ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3638 IEnumSTATSTG* iface)
3640 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3642 This->ref++;
3643 return This->ref;
3646 ULONG WINAPI IEnumSTATSTGImpl_Release(
3647 IEnumSTATSTG* iface)
3649 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3651 ULONG newRef;
3653 This->ref--;
3654 newRef = This->ref;
3657 * If the reference count goes down to 0, perform suicide.
3659 if (newRef==0)
3661 IEnumSTATSTGImpl_Destroy(This);
3664 return newRef;
3667 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3668 IEnumSTATSTG* iface,
3669 ULONG celt,
3670 STATSTG* rgelt,
3671 ULONG* pceltFetched)
3673 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3675 StgProperty currentProperty;
3676 STATSTG* currentReturnStruct = rgelt;
3677 ULONG objectFetched = 0;
3678 ULONG currentSearchNode;
3681 * Perform a sanity check on the parameters.
3683 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3684 return E_INVALIDARG;
3687 * To avoid the special case, get another pointer to a ULONG value if
3688 * the caller didn't supply one.
3690 if (pceltFetched==0)
3691 pceltFetched = &objectFetched;
3694 * Start the iteration, we will iterate until we hit the end of the
3695 * linked list or until we hit the number of items to iterate through
3697 *pceltFetched = 0;
3700 * Start with the node at the top of the stack.
3702 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3704 while ( ( *pceltFetched < celt) &&
3705 ( currentSearchNode!=PROPERTY_NULL) )
3708 * Remove the top node from the stack
3710 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3713 * Read the property from the storage.
3715 StorageImpl_ReadProperty(This->parentStorage,
3716 currentSearchNode,
3717 &currentProperty);
3720 * Copy the information to the return buffer.
3722 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3723 &currentProperty,
3724 STATFLAG_DEFAULT);
3727 * Step to the next item in the iteration
3729 (*pceltFetched)++;
3730 currentReturnStruct++;
3733 * Push the next search node in the search stack.
3735 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3738 * continue the iteration.
3740 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3743 if (*pceltFetched == celt)
3744 return S_OK;
3746 return S_FALSE;
3750 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3751 IEnumSTATSTG* iface,
3752 ULONG celt)
3754 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3756 StgProperty currentProperty;
3757 ULONG objectFetched = 0;
3758 ULONG currentSearchNode;
3761 * Start with the node at the top of the stack.
3763 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3765 while ( (objectFetched < celt) &&
3766 (currentSearchNode!=PROPERTY_NULL) )
3769 * Remove the top node from the stack
3771 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3774 * Read the property from the storage.
3776 StorageImpl_ReadProperty(This->parentStorage,
3777 currentSearchNode,
3778 &currentProperty);
3781 * Step to the next item in the iteration
3783 objectFetched++;
3786 * Push the next search node in the search stack.
3788 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3791 * continue the iteration.
3793 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3796 if (objectFetched == celt)
3797 return S_OK;
3799 return S_FALSE;
3802 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3803 IEnumSTATSTG* iface)
3805 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3807 StgProperty rootProperty;
3808 BOOL readSuccessful;
3811 * Re-initialize the search stack to an empty stack
3813 This->stackSize = 0;
3816 * Read the root property from the storage.
3818 readSuccessful = StorageImpl_ReadProperty(
3819 This->parentStorage,
3820 This->firstPropertyNode,
3821 &rootProperty);
3823 if (readSuccessful)
3825 assert(rootProperty.sizeOfNameString!=0);
3828 * Push the search node in the search stack.
3830 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3833 return S_OK;
3836 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3837 IEnumSTATSTG* iface,
3838 IEnumSTATSTG** ppenum)
3840 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3842 IEnumSTATSTGImpl* newClone;
3845 * Perform a sanity check on the parameters.
3847 if (ppenum==0)
3848 return E_INVALIDARG;
3850 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3851 This->firstPropertyNode);
3855 * The new clone enumeration must point to the same current node as
3856 * the ole one.
3858 newClone->stackSize = This->stackSize ;
3859 newClone->stackMaxSize = This->stackMaxSize ;
3860 newClone->stackToVisit =
3861 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3863 memcpy(
3864 newClone->stackToVisit,
3865 This->stackToVisit,
3866 sizeof(ULONG) * newClone->stackSize);
3868 *ppenum = (IEnumSTATSTG*)newClone;
3871 * Don't forget to nail down a reference to the clone before
3872 * returning it.
3874 IEnumSTATSTGImpl_AddRef(*ppenum);
3876 return S_OK;
3879 INT IEnumSTATSTGImpl_FindParentProperty(
3880 IEnumSTATSTGImpl *This,
3881 ULONG childProperty,
3882 StgProperty *currentProperty,
3883 ULONG *thisNodeId)
3885 ULONG currentSearchNode;
3886 ULONG foundNode;
3889 * To avoid the special case, get another pointer to a ULONG value if
3890 * the caller didn't supply one.
3892 if (thisNodeId==0)
3893 thisNodeId = &foundNode;
3896 * Start with the node at the top of the stack.
3898 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3901 while (currentSearchNode!=PROPERTY_NULL)
3904 * Store the current node in the returned parameters
3906 *thisNodeId = currentSearchNode;
3909 * Remove the top node from the stack
3911 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3914 * Read the property from the storage.
3916 StorageImpl_ReadProperty(
3917 This->parentStorage,
3918 currentSearchNode,
3919 currentProperty);
3921 if (currentProperty->previousProperty == childProperty)
3922 return PROPERTY_RELATION_PREVIOUS;
3924 else if (currentProperty->nextProperty == childProperty)
3925 return PROPERTY_RELATION_NEXT;
3927 else if (currentProperty->dirProperty == childProperty)
3928 return PROPERTY_RELATION_DIR;
3931 * Push the next search node in the search stack.
3933 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3936 * continue the iteration.
3938 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3941 return PROPERTY_NULL;
3944 ULONG IEnumSTATSTGImpl_FindProperty(
3945 IEnumSTATSTGImpl* This,
3946 const OLECHAR* lpszPropName,
3947 StgProperty* currentProperty)
3949 ULONG currentSearchNode;
3952 * Start with the node at the top of the stack.
3954 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3956 while (currentSearchNode!=PROPERTY_NULL)
3959 * Remove the top node from the stack
3961 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3964 * Read the property from the storage.
3966 StorageImpl_ReadProperty(This->parentStorage,
3967 currentSearchNode,
3968 currentProperty);
3970 if ( propertyNameCmp(
3971 (OLECHAR*)currentProperty->name,
3972 (OLECHAR*)lpszPropName) == 0)
3973 return currentSearchNode;
3976 * Push the next search node in the search stack.
3978 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3981 * continue the iteration.
3983 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3986 return PROPERTY_NULL;
3989 void IEnumSTATSTGImpl_PushSearchNode(
3990 IEnumSTATSTGImpl* This,
3991 ULONG nodeToPush)
3993 StgProperty rootProperty;
3994 BOOL readSuccessful;
3997 * First, make sure we're not trying to push an unexisting node.
3999 if (nodeToPush==PROPERTY_NULL)
4000 return;
4003 * First push the node to the stack
4005 if (This->stackSize == This->stackMaxSize)
4007 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4009 This->stackToVisit = HeapReAlloc(
4010 GetProcessHeap(),
4012 This->stackToVisit,
4013 sizeof(ULONG) * This->stackMaxSize);
4016 This->stackToVisit[This->stackSize] = nodeToPush;
4017 This->stackSize++;
4020 * Read the root property from the storage.
4022 readSuccessful = StorageImpl_ReadProperty(
4023 This->parentStorage,
4024 nodeToPush,
4025 &rootProperty);
4027 if (readSuccessful)
4029 assert(rootProperty.sizeOfNameString!=0);
4032 * Push the previous search node in the search stack.
4034 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4038 ULONG IEnumSTATSTGImpl_PopSearchNode(
4039 IEnumSTATSTGImpl* This,
4040 BOOL remove)
4042 ULONG topNode;
4044 if (This->stackSize == 0)
4045 return PROPERTY_NULL;
4047 topNode = This->stackToVisit[This->stackSize-1];
4049 if (remove)
4050 This->stackSize--;
4052 return topNode;
4055 /******************************************************************************
4056 ** StorageUtl implementation
4059 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4061 memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4064 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4066 memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4069 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4071 memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4074 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4076 memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4079 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4081 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4082 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4083 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4085 memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4088 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4090 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4091 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4092 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4094 memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4097 void StorageUtl_CopyPropertyToSTATSTG(
4098 STATSTG* destination,
4099 StgProperty* source,
4100 int statFlags)
4103 * The copy of the string occurs only when the flag is not set
4105 if ((statFlags & STATFLAG_NONAME) != 0)
4107 destination->pwcsName = 0;
4109 else
4111 destination->pwcsName =
4112 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4114 strcpyW((LPWSTR)destination->pwcsName, source->name);
4117 switch (source->propertyType)
4119 case PROPTYPE_STORAGE:
4120 case PROPTYPE_ROOT:
4121 destination->type = STGTY_STORAGE;
4122 break;
4123 case PROPTYPE_STREAM:
4124 destination->type = STGTY_STREAM;
4125 break;
4126 default:
4127 destination->type = STGTY_STREAM;
4128 break;
4131 destination->cbSize = source->size;
4133 currentReturnStruct->mtime = {0}; TODO
4134 currentReturnStruct->ctime = {0};
4135 currentReturnStruct->atime = {0};
4137 destination->grfMode = 0;
4138 destination->grfLocksSupported = 0;
4139 destination->clsid = source->propertyUniqueID;
4140 destination->grfStateBits = 0;
4141 destination->reserved = 0;
4144 /******************************************************************************
4145 ** BlockChainStream implementation
4148 BlockChainStream* BlockChainStream_Construct(
4149 StorageImpl* parentStorage,
4150 ULONG* headOfStreamPlaceHolder,
4151 ULONG propertyIndex)
4153 BlockChainStream* newStream;
4154 ULONG blockIndex;
4156 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4158 newStream->parentStorage = parentStorage;
4159 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4160 newStream->ownerPropertyIndex = propertyIndex;
4161 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4162 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4163 newStream->numBlocks = 0;
4165 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4167 while (blockIndex != BLOCK_END_OF_CHAIN)
4169 newStream->numBlocks++;
4170 newStream->tailIndex = blockIndex;
4172 if(FAILED(StorageImpl_GetNextBlockInChain(
4173 parentStorage,
4174 blockIndex,
4175 &blockIndex)))
4177 HeapFree(GetProcessHeap(), 0, newStream);
4178 return NULL;
4182 return newStream;
4185 void BlockChainStream_Destroy(BlockChainStream* This)
4187 HeapFree(GetProcessHeap(), 0, This);
4190 /******************************************************************************
4191 * BlockChainStream_GetHeadOfChain
4193 * Returns the head of this stream chain.
4194 * Some special chains don't have properties, their heads are kept in
4195 * This->headOfStreamPlaceHolder.
4198 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4200 StgProperty chainProperty;
4201 BOOL readSuccessful;
4203 if (This->headOfStreamPlaceHolder != 0)
4204 return *(This->headOfStreamPlaceHolder);
4206 if (This->ownerPropertyIndex != PROPERTY_NULL)
4208 readSuccessful = StorageImpl_ReadProperty(
4209 This->parentStorage,
4210 This->ownerPropertyIndex,
4211 &chainProperty);
4213 if (readSuccessful)
4215 return chainProperty.startingBlock;
4219 return BLOCK_END_OF_CHAIN;
4222 /******************************************************************************
4223 * BlockChainStream_GetCount
4225 * Returns the number of blocks that comprises this chain.
4226 * This is not the size of the stream as the last block may not be full!
4229 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4231 ULONG blockIndex;
4232 ULONG count = 0;
4234 blockIndex = BlockChainStream_GetHeadOfChain(This);
4236 while (blockIndex != BLOCK_END_OF_CHAIN)
4238 count++;
4240 if(FAILED(StorageImpl_GetNextBlockInChain(
4241 This->parentStorage,
4242 blockIndex,
4243 &blockIndex)))
4244 return 0;
4247 return count;
4250 /******************************************************************************
4251 * BlockChainStream_ReadAt
4253 * Reads a specified number of bytes from this chain at the specified offset.
4254 * bytesRead may be NULL.
4255 * Failure will be returned if the specified number of bytes has not been read.
4257 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4258 ULARGE_INTEGER offset,
4259 ULONG size,
4260 void* buffer,
4261 ULONG* bytesRead)
4263 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4264 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4265 ULONG bytesToReadInBuffer;
4266 ULONG blockIndex;
4267 BYTE* bufferWalker;
4268 BYTE* bigBlockBuffer;
4271 * Find the first block in the stream that contains part of the buffer.
4273 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4274 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4275 (blockNoInSequence < This->lastBlockNoInSequence) )
4277 blockIndex = BlockChainStream_GetHeadOfChain(This);
4278 This->lastBlockNoInSequence = blockNoInSequence;
4280 else
4282 ULONG temp = blockNoInSequence;
4284 blockIndex = This->lastBlockNoInSequenceIndex;
4285 blockNoInSequence -= This->lastBlockNoInSequence;
4286 This->lastBlockNoInSequence = temp;
4289 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4291 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4292 return FALSE;
4293 blockNoInSequence--;
4296 This->lastBlockNoInSequenceIndex = blockIndex;
4299 * Start reading the buffer.
4301 *bytesRead = 0;
4302 bufferWalker = buffer;
4304 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4307 * Calculate how many bytes we can copy from this big block.
4309 bytesToReadInBuffer =
4310 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4313 * Copy those bytes to the buffer
4315 bigBlockBuffer =
4316 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4318 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4320 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4323 * Step to the next big block.
4325 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4326 return FALSE;
4328 bufferWalker += bytesToReadInBuffer;
4329 size -= bytesToReadInBuffer;
4330 *bytesRead += bytesToReadInBuffer;
4331 offsetInBlock = 0; /* There is no offset on the next block */
4335 return (size == 0);
4338 /******************************************************************************
4339 * BlockChainStream_WriteAt
4341 * Writes the specified number of bytes to this chain at the specified offset.
4342 * bytesWritten may be NULL.
4343 * Will fail if not all specified number of bytes have been written.
4345 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4346 ULARGE_INTEGER offset,
4347 ULONG size,
4348 const void* buffer,
4349 ULONG* bytesWritten)
4351 ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4352 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->bigBlockSize;
4353 ULONG bytesToWrite;
4354 ULONG blockIndex;
4355 BYTE* bufferWalker;
4356 BYTE* bigBlockBuffer;
4359 * Find the first block in the stream that contains part of the buffer.
4361 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4362 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4363 (blockNoInSequence < This->lastBlockNoInSequence) )
4365 blockIndex = BlockChainStream_GetHeadOfChain(This);
4366 This->lastBlockNoInSequence = blockNoInSequence;
4368 else
4370 ULONG temp = blockNoInSequence;
4372 blockIndex = This->lastBlockNoInSequenceIndex;
4373 blockNoInSequence -= This->lastBlockNoInSequence;
4374 This->lastBlockNoInSequence = temp;
4377 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4379 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4380 &blockIndex)))
4381 return FALSE;
4382 blockNoInSequence--;
4385 This->lastBlockNoInSequenceIndex = blockIndex;
4388 * Here, I'm casting away the constness on the buffer variable
4389 * This is OK since we don't intend to modify that buffer.
4391 *bytesWritten = 0;
4392 bufferWalker = (BYTE*)buffer;
4394 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4397 * Calculate how many bytes we can copy from this big block.
4399 bytesToWrite =
4400 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4403 * Copy those bytes to the buffer
4405 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4407 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4409 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4412 * Step to the next big block.
4414 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4415 &blockIndex)))
4416 return FALSE;
4417 bufferWalker += bytesToWrite;
4418 size -= bytesToWrite;
4419 *bytesWritten += bytesToWrite;
4420 offsetInBlock = 0; /* There is no offset on the next block */
4423 return (size == 0);
4426 /******************************************************************************
4427 * BlockChainStream_Shrink
4429 * Shrinks this chain in the big block depot.
4431 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4432 ULARGE_INTEGER newSize)
4434 ULONG blockIndex, extraBlock;
4435 ULONG numBlocks;
4436 ULONG count = 1;
4439 * Reset the last accessed block cache.
4441 This->lastBlockNoInSequence = 0xFFFFFFFF;
4442 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4445 * Figure out how many blocks are needed to contain the new size
4447 numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4449 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4450 numBlocks++;
4452 blockIndex = BlockChainStream_GetHeadOfChain(This);
4455 * Go to the new end of chain
4457 while (count < numBlocks)
4459 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4460 &blockIndex)))
4461 return FALSE;
4462 count++;
4465 /* Get the next block before marking the new end */
4466 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4467 &extraBlock)))
4468 return FALSE;
4470 /* Mark the new end of chain */
4471 StorageImpl_SetNextBlockInChain(
4472 This->parentStorage,
4473 blockIndex,
4474 BLOCK_END_OF_CHAIN);
4476 This->tailIndex = blockIndex;
4477 This->numBlocks = numBlocks;
4480 * Mark the extra blocks as free
4482 while (extraBlock != BLOCK_END_OF_CHAIN)
4484 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4485 &blockIndex)))
4486 return FALSE;
4487 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4488 extraBlock = blockIndex;
4491 return TRUE;
4494 /******************************************************************************
4495 * BlockChainStream_Enlarge
4497 * Grows this chain in the big block depot.
4499 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4500 ULARGE_INTEGER newSize)
4502 ULONG blockIndex, currentBlock;
4503 ULONG newNumBlocks;
4504 ULONG oldNumBlocks = 0;
4506 blockIndex = BlockChainStream_GetHeadOfChain(This);
4509 * Empty chain. Create the head.
4511 if (blockIndex == BLOCK_END_OF_CHAIN)
4513 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4514 StorageImpl_SetNextBlockInChain(This->parentStorage,
4515 blockIndex,
4516 BLOCK_END_OF_CHAIN);
4518 if (This->headOfStreamPlaceHolder != 0)
4520 *(This->headOfStreamPlaceHolder) = blockIndex;
4522 else
4524 StgProperty chainProp;
4525 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4527 StorageImpl_ReadProperty(
4528 This->parentStorage,
4529 This->ownerPropertyIndex,
4530 &chainProp);
4532 chainProp.startingBlock = blockIndex;
4534 StorageImpl_WriteProperty(
4535 This->parentStorage,
4536 This->ownerPropertyIndex,
4537 &chainProp);
4540 This->tailIndex = blockIndex;
4541 This->numBlocks = 1;
4545 * Figure out how many blocks are needed to contain this stream
4547 newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4549 if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4550 newNumBlocks++;
4553 * Go to the current end of chain
4555 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4557 currentBlock = blockIndex;
4559 while (blockIndex != BLOCK_END_OF_CHAIN)
4561 This->numBlocks++;
4562 currentBlock = blockIndex;
4564 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4565 &blockIndex)))
4566 return FALSE;
4569 This->tailIndex = currentBlock;
4572 currentBlock = This->tailIndex;
4573 oldNumBlocks = This->numBlocks;
4576 * Add new blocks to the chain
4578 if (oldNumBlocks < newNumBlocks)
4580 while (oldNumBlocks < newNumBlocks)
4582 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4584 StorageImpl_SetNextBlockInChain(
4585 This->parentStorage,
4586 currentBlock,
4587 blockIndex);
4589 StorageImpl_SetNextBlockInChain(
4590 This->parentStorage,
4591 blockIndex,
4592 BLOCK_END_OF_CHAIN);
4594 currentBlock = blockIndex;
4595 oldNumBlocks++;
4598 This->tailIndex = blockIndex;
4599 This->numBlocks = newNumBlocks;
4602 return TRUE;
4605 /******************************************************************************
4606 * BlockChainStream_SetSize
4608 * Sets the size of this stream. The big block depot will be updated.
4609 * The file will grow if we grow the chain.
4611 * TODO: Free the actual blocks in the file when we shrink the chain.
4612 * Currently, the blocks are still in the file. So the file size
4613 * doesn't shrink even if we shrink streams.
4615 BOOL BlockChainStream_SetSize(
4616 BlockChainStream* This,
4617 ULARGE_INTEGER newSize)
4619 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4621 if (newSize.s.LowPart == size.s.LowPart)
4622 return TRUE;
4624 if (newSize.s.LowPart < size.s.LowPart)
4626 BlockChainStream_Shrink(This, newSize);
4628 else
4630 ULARGE_INTEGER fileSize =
4631 BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4633 ULONG diff = newSize.s.LowPart - size.s.LowPart;
4636 * Make sure the file stays a multiple of blocksize
4638 if ((diff % This->parentStorage->bigBlockSize) != 0)
4639 diff += (This->parentStorage->bigBlockSize -
4640 (diff % This->parentStorage->bigBlockSize) );
4642 fileSize.s.LowPart += diff;
4643 BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4645 BlockChainStream_Enlarge(This, newSize);
4648 return TRUE;
4651 /******************************************************************************
4652 * BlockChainStream_GetSize
4654 * Returns the size of this chain.
4655 * Will return the block count if this chain doesn't have a property.
4657 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4659 StgProperty chainProperty;
4661 if(This->headOfStreamPlaceHolder == NULL)
4664 * This chain is a data stream read the property and return
4665 * the appropriate size
4667 StorageImpl_ReadProperty(
4668 This->parentStorage,
4669 This->ownerPropertyIndex,
4670 &chainProperty);
4672 return chainProperty.size;
4674 else
4677 * this chain is a chain that does not have a property, figure out the
4678 * size by making the product number of used blocks times the
4679 * size of them
4681 ULARGE_INTEGER result;
4682 result.s.HighPart = 0;
4684 result.s.LowPart =
4685 BlockChainStream_GetCount(This) *
4686 This->parentStorage->bigBlockSize;
4688 return result;
4692 /******************************************************************************
4693 ** SmallBlockChainStream implementation
4696 SmallBlockChainStream* SmallBlockChainStream_Construct(
4697 StorageImpl* parentStorage,
4698 ULONG propertyIndex)
4700 SmallBlockChainStream* newStream;
4702 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4704 newStream->parentStorage = parentStorage;
4705 newStream->ownerPropertyIndex = propertyIndex;
4707 return newStream;
4710 void SmallBlockChainStream_Destroy(
4711 SmallBlockChainStream* This)
4713 HeapFree(GetProcessHeap(), 0, This);
4716 /******************************************************************************
4717 * SmallBlockChainStream_GetHeadOfChain
4719 * Returns the head of this chain of small blocks.
4721 ULONG SmallBlockChainStream_GetHeadOfChain(
4722 SmallBlockChainStream* This)
4724 StgProperty chainProperty;
4725 BOOL readSuccessful;
4727 if (This->ownerPropertyIndex)
4729 readSuccessful = StorageImpl_ReadProperty(
4730 This->parentStorage,
4731 This->ownerPropertyIndex,
4732 &chainProperty);
4734 if (readSuccessful)
4736 return chainProperty.startingBlock;
4741 return BLOCK_END_OF_CHAIN;
4744 /******************************************************************************
4745 * SmallBlockChainStream_GetNextBlockInChain
4747 * Returns the index of the next small block in this chain.
4749 * Return Values:
4750 * - BLOCK_END_OF_CHAIN: end of this chain
4751 * - BLOCK_UNUSED: small block 'blockIndex' is free
4753 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4754 SmallBlockChainStream* This,
4755 ULONG blockIndex,
4756 ULONG* nextBlockInChain)
4758 ULARGE_INTEGER offsetOfBlockInDepot;
4759 DWORD buffer;
4760 ULONG bytesRead;
4761 BOOL success;
4763 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4765 offsetOfBlockInDepot.s.HighPart = 0;
4766 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4769 * Read those bytes in the buffer from the small block file.
4771 success = BlockChainStream_ReadAt(
4772 This->parentStorage->smallBlockDepotChain,
4773 offsetOfBlockInDepot,
4774 sizeof(DWORD),
4775 &buffer,
4776 &bytesRead);
4778 if (success)
4780 StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4781 return S_OK;
4784 return STG_E_READFAULT;
4787 /******************************************************************************
4788 * SmallBlockChainStream_SetNextBlockInChain
4790 * Writes the index of the next block of the specified block in the small
4791 * block depot.
4792 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4793 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4795 void SmallBlockChainStream_SetNextBlockInChain(
4796 SmallBlockChainStream* This,
4797 ULONG blockIndex,
4798 ULONG nextBlock)
4800 ULARGE_INTEGER offsetOfBlockInDepot;
4801 DWORD buffer;
4802 ULONG bytesWritten;
4804 offsetOfBlockInDepot.s.HighPart = 0;
4805 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4807 StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4810 * Read those bytes in the buffer from the small block file.
4812 BlockChainStream_WriteAt(
4813 This->parentStorage->smallBlockDepotChain,
4814 offsetOfBlockInDepot,
4815 sizeof(DWORD),
4816 &buffer,
4817 &bytesWritten);
4820 /******************************************************************************
4821 * SmallBlockChainStream_FreeBlock
4823 * Flag small block 'blockIndex' as free in the small block depot.
4825 void SmallBlockChainStream_FreeBlock(
4826 SmallBlockChainStream* This,
4827 ULONG blockIndex)
4829 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4832 /******************************************************************************
4833 * SmallBlockChainStream_GetNextFreeBlock
4835 * Returns the index of a free small block. The small block depot will be
4836 * enlarged if necessary. The small block chain will also be enlarged if
4837 * necessary.
4839 ULONG SmallBlockChainStream_GetNextFreeBlock(
4840 SmallBlockChainStream* This)
4842 ULARGE_INTEGER offsetOfBlockInDepot;
4843 DWORD buffer;
4844 ULONG bytesRead;
4845 ULONG blockIndex = 0;
4846 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4847 BOOL success = TRUE;
4848 ULONG smallBlocksPerBigBlock;
4850 offsetOfBlockInDepot.s.HighPart = 0;
4853 * Scan the small block depot for a free block
4855 while (nextBlockIndex != BLOCK_UNUSED)
4857 offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4859 success = BlockChainStream_ReadAt(
4860 This->parentStorage->smallBlockDepotChain,
4861 offsetOfBlockInDepot,
4862 sizeof(DWORD),
4863 &buffer,
4864 &bytesRead);
4867 * If we run out of space for the small block depot, enlarge it
4869 if (success)
4871 StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4873 if (nextBlockIndex != BLOCK_UNUSED)
4874 blockIndex++;
4876 else
4878 ULONG count =
4879 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4881 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4882 ULONG nextBlock, newsbdIndex;
4883 BYTE* smallBlockDepot;
4885 nextBlock = sbdIndex;
4886 while (nextBlock != BLOCK_END_OF_CHAIN)
4888 sbdIndex = nextBlock;
4889 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4892 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4893 if (sbdIndex != BLOCK_END_OF_CHAIN)
4894 StorageImpl_SetNextBlockInChain(
4895 This->parentStorage,
4896 sbdIndex,
4897 newsbdIndex);
4899 StorageImpl_SetNextBlockInChain(
4900 This->parentStorage,
4901 newsbdIndex,
4902 BLOCK_END_OF_CHAIN);
4905 * Initialize all the small blocks to free
4907 smallBlockDepot =
4908 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4910 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4911 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4913 if (count == 0)
4916 * We have just created the small block depot.
4918 StgProperty rootProp;
4919 ULONG sbStartIndex;
4922 * Save it in the header
4924 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4925 StorageImpl_SaveFileHeader(This->parentStorage);
4928 * And allocate the first big block that will contain small blocks
4930 sbStartIndex =
4931 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4933 StorageImpl_SetNextBlockInChain(
4934 This->parentStorage,
4935 sbStartIndex,
4936 BLOCK_END_OF_CHAIN);
4938 StorageImpl_ReadProperty(
4939 This->parentStorage,
4940 This->parentStorage->rootPropertySetIndex,
4941 &rootProp);
4943 rootProp.startingBlock = sbStartIndex;
4944 rootProp.size.s.HighPart = 0;
4945 rootProp.size.s.LowPart = This->parentStorage->bigBlockSize;
4947 StorageImpl_WriteProperty(
4948 This->parentStorage,
4949 This->parentStorage->rootPropertySetIndex,
4950 &rootProp);
4955 smallBlocksPerBigBlock =
4956 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4959 * Verify if we have to allocate big blocks to contain small blocks
4961 if (blockIndex % smallBlocksPerBigBlock == 0)
4963 StgProperty rootProp;
4964 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4966 StorageImpl_ReadProperty(
4967 This->parentStorage,
4968 This->parentStorage->rootPropertySetIndex,
4969 &rootProp);
4971 if (rootProp.size.s.LowPart <
4972 (blocksRequired * This->parentStorage->bigBlockSize))
4974 rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4976 BlockChainStream_SetSize(
4977 This->parentStorage->smallBlockRootChain,
4978 rootProp.size);
4980 StorageImpl_WriteProperty(
4981 This->parentStorage,
4982 This->parentStorage->rootPropertySetIndex,
4983 &rootProp);
4987 return blockIndex;
4990 /******************************************************************************
4991 * SmallBlockChainStream_ReadAt
4993 * Reads a specified number of bytes from this chain at the specified offset.
4994 * bytesRead may be NULL.
4995 * Failure will be returned if the specified number of bytes has not been read.
4997 BOOL SmallBlockChainStream_ReadAt(
4998 SmallBlockChainStream* This,
4999 ULARGE_INTEGER offset,
5000 ULONG size,
5001 void* buffer,
5002 ULONG* bytesRead)
5004 ULARGE_INTEGER offsetInBigBlockFile;
5005 ULONG blockNoInSequence =
5006 offset.s.LowPart / This->parentStorage->smallBlockSize;
5008 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5009 ULONG bytesToReadInBuffer;
5010 ULONG blockIndex;
5011 ULONG bytesReadFromBigBlockFile;
5012 BYTE* bufferWalker;
5015 * This should never happen on a small block file.
5017 assert(offset.s.HighPart==0);
5020 * Find the first block in the stream that contains part of the buffer.
5022 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5024 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5026 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5027 &blockIndex)))
5028 return FALSE;
5029 blockNoInSequence--;
5033 * Start reading the buffer.
5035 *bytesRead = 0;
5036 bufferWalker = buffer;
5038 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5041 * Calculate how many bytes we can copy from this small block.
5043 bytesToReadInBuffer =
5044 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5047 * Calculate the offset of the small block in the small block file.
5049 offsetInBigBlockFile.s.HighPart = 0;
5050 offsetInBigBlockFile.s.LowPart =
5051 blockIndex * This->parentStorage->smallBlockSize;
5053 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5056 * Read those bytes in the buffer from the small block file.
5058 BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5059 offsetInBigBlockFile,
5060 bytesToReadInBuffer,
5061 bufferWalker,
5062 &bytesReadFromBigBlockFile);
5064 assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5067 * Step to the next big block.
5069 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5070 return FALSE;
5071 bufferWalker += bytesToReadInBuffer;
5072 size -= bytesToReadInBuffer;
5073 *bytesRead += bytesToReadInBuffer;
5074 offsetInBlock = 0; /* There is no offset on the next block */
5077 return (size == 0);
5080 /******************************************************************************
5081 * SmallBlockChainStream_WriteAt
5083 * Writes the specified number of bytes to this chain at the specified offset.
5084 * bytesWritten may be NULL.
5085 * Will fail if not all specified number of bytes have been written.
5087 BOOL SmallBlockChainStream_WriteAt(
5088 SmallBlockChainStream* This,
5089 ULARGE_INTEGER offset,
5090 ULONG size,
5091 const void* buffer,
5092 ULONG* bytesWritten)
5094 ULARGE_INTEGER offsetInBigBlockFile;
5095 ULONG blockNoInSequence =
5096 offset.s.LowPart / This->parentStorage->smallBlockSize;
5098 ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
5099 ULONG bytesToWriteInBuffer;
5100 ULONG blockIndex;
5101 ULONG bytesWrittenFromBigBlockFile;
5102 BYTE* bufferWalker;
5105 * This should never happen on a small block file.
5107 assert(offset.s.HighPart==0);
5110 * Find the first block in the stream that contains part of the buffer.
5112 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5114 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5116 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5117 return FALSE;
5118 blockNoInSequence--;
5122 * Start writing the buffer.
5124 * Here, I'm casting away the constness on the buffer variable
5125 * This is OK since we don't intend to modify that buffer.
5127 *bytesWritten = 0;
5128 bufferWalker = (BYTE*)buffer;
5129 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5132 * Calculate how many bytes we can copy to this small block.
5134 bytesToWriteInBuffer =
5135 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5138 * Calculate the offset of the small block in the small block file.
5140 offsetInBigBlockFile.s.HighPart = 0;
5141 offsetInBigBlockFile.s.LowPart =
5142 blockIndex * This->parentStorage->smallBlockSize;
5144 offsetInBigBlockFile.s.LowPart += offsetInBlock;
5147 * Write those bytes in the buffer to the small block file.
5149 BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5150 offsetInBigBlockFile,
5151 bytesToWriteInBuffer,
5152 bufferWalker,
5153 &bytesWrittenFromBigBlockFile);
5155 assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5158 * Step to the next big block.
5160 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5161 &blockIndex)))
5162 return FALSE;
5163 bufferWalker += bytesToWriteInBuffer;
5164 size -= bytesToWriteInBuffer;
5165 *bytesWritten += bytesToWriteInBuffer;
5166 offsetInBlock = 0; /* There is no offset on the next block */
5169 return (size == 0);
5172 /******************************************************************************
5173 * SmallBlockChainStream_Shrink
5175 * Shrinks this chain in the small block depot.
5177 BOOL SmallBlockChainStream_Shrink(
5178 SmallBlockChainStream* This,
5179 ULARGE_INTEGER newSize)
5181 ULONG blockIndex, extraBlock;
5182 ULONG numBlocks;
5183 ULONG count = 0;
5185 numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5187 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5188 numBlocks++;
5190 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5193 * Go to the new end of chain
5195 while (count < numBlocks)
5197 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5198 &blockIndex)))
5199 return FALSE;
5200 count++;
5204 * If the count is 0, we have a special case, the head of the chain was
5205 * just freed.
5207 if (count == 0)
5209 StgProperty chainProp;
5211 StorageImpl_ReadProperty(This->parentStorage,
5212 This->ownerPropertyIndex,
5213 &chainProp);
5215 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5217 StorageImpl_WriteProperty(This->parentStorage,
5218 This->ownerPropertyIndex,
5219 &chainProp);
5222 * We start freeing the chain at the head block.
5224 extraBlock = blockIndex;
5226 else
5228 /* Get the next block before marking the new end */
5229 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5230 &extraBlock)))
5231 return FALSE;
5233 /* Mark the new end of chain */
5234 SmallBlockChainStream_SetNextBlockInChain(
5235 This,
5236 blockIndex,
5237 BLOCK_END_OF_CHAIN);
5241 * Mark the extra blocks as free
5243 while (extraBlock != BLOCK_END_OF_CHAIN)
5245 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5246 &blockIndex)))
5247 return FALSE;
5248 SmallBlockChainStream_FreeBlock(This, extraBlock);
5249 extraBlock = blockIndex;
5252 return TRUE;
5255 /******************************************************************************
5256 * SmallBlockChainStream_Enlarge
5258 * Grows this chain in the small block depot.
5260 BOOL SmallBlockChainStream_Enlarge(
5261 SmallBlockChainStream* This,
5262 ULARGE_INTEGER newSize)
5264 ULONG blockIndex, currentBlock;
5265 ULONG newNumBlocks;
5266 ULONG oldNumBlocks = 0;
5268 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5271 * Empty chain
5273 if (blockIndex == BLOCK_END_OF_CHAIN)
5276 StgProperty chainProp;
5278 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5279 &chainProp);
5281 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5283 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5284 &chainProp);
5286 blockIndex = chainProp.startingBlock;
5287 SmallBlockChainStream_SetNextBlockInChain(
5288 This,
5289 blockIndex,
5290 BLOCK_END_OF_CHAIN);
5293 currentBlock = blockIndex;
5296 * Figure out how many blocks are needed to contain this stream
5298 newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5300 if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5301 newNumBlocks++;
5304 * Go to the current end of chain
5306 while (blockIndex != BLOCK_END_OF_CHAIN)
5308 oldNumBlocks++;
5309 currentBlock = blockIndex;
5310 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5311 return FALSE;
5315 * Add new blocks to the chain
5317 while (oldNumBlocks < newNumBlocks)
5319 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5320 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5322 SmallBlockChainStream_SetNextBlockInChain(
5323 This,
5324 blockIndex,
5325 BLOCK_END_OF_CHAIN);
5327 currentBlock = blockIndex;
5328 oldNumBlocks++;
5331 return TRUE;
5334 /******************************************************************************
5335 * SmallBlockChainStream_GetCount
5337 * Returns the number of blocks that comprises this chain.
5338 * This is not the size of this chain as the last block may not be full!
5340 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5342 ULONG blockIndex;
5343 ULONG count = 0;
5345 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5347 while (blockIndex != BLOCK_END_OF_CHAIN)
5349 count++;
5351 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5352 return 0;
5355 return count;
5358 /******************************************************************************
5359 * SmallBlockChainStream_SetSize
5361 * Sets the size of this stream.
5362 * The file will grow if we grow the chain.
5364 * TODO: Free the actual blocks in the file when we shrink the chain.
5365 * Currently, the blocks are still in the file. So the file size
5366 * doesn't shrink even if we shrink streams.
5368 BOOL SmallBlockChainStream_SetSize(
5369 SmallBlockChainStream* This,
5370 ULARGE_INTEGER newSize)
5372 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5374 if (newSize.s.LowPart == size.s.LowPart)
5375 return TRUE;
5377 if (newSize.s.LowPart < size.s.LowPart)
5379 SmallBlockChainStream_Shrink(This, newSize);
5381 else
5383 SmallBlockChainStream_Enlarge(This, newSize);
5386 return TRUE;
5389 /******************************************************************************
5390 * SmallBlockChainStream_GetSize
5392 * Returns the size of this chain.
5394 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5396 StgProperty chainProperty;
5398 StorageImpl_ReadProperty(
5399 This->parentStorage,
5400 This->ownerPropertyIndex,
5401 &chainProperty);
5403 return chainProperty.size;
5406 /******************************************************************************
5407 * StgCreateDocfile [OLE32.144]
5409 HRESULT WINAPI StgCreateDocfile(
5410 LPCOLESTR pwcsName,
5411 DWORD grfMode,
5412 DWORD reserved,
5413 IStorage **ppstgOpen)
5415 StorageImpl* newStorage = 0;
5416 HANDLE hFile = INVALID_HANDLE_VALUE;
5417 HRESULT hr = S_OK;
5418 DWORD shareMode;
5419 DWORD accessMode;
5420 DWORD creationMode;
5421 DWORD fileAttributes;
5422 WCHAR tempFileName[MAX_PATH];
5424 TRACE("(%s, %lx, %ld, %p)\n",
5425 debugstr_w(pwcsName), grfMode,
5426 reserved, ppstgOpen);
5429 * Validate the parameters
5431 if (ppstgOpen == 0)
5432 return STG_E_INVALIDPOINTER;
5435 * Validate the STGM flags
5437 if ( FAILED( validateSTGM(grfMode) ))
5438 return STG_E_INVALIDFLAG;
5441 * Generate a unique name.
5443 if (pwcsName == 0)
5445 WCHAR tempPath[MAX_PATH];
5446 WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5448 if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5449 return STG_E_INVALIDFLAG;
5450 if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5451 return STG_E_INVALIDFLAG;
5453 memset(tempPath, 0, sizeof(tempPath));
5454 memset(tempFileName, 0, sizeof(tempFileName));
5456 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5457 tempPath[0] = '.';
5459 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5460 pwcsName = tempFileName;
5461 else
5462 return STG_E_INSUFFICIENTMEMORY;
5464 creationMode = TRUNCATE_EXISTING;
5466 else
5468 creationMode = GetCreationModeFromSTGM(grfMode);
5472 * Interpret the STGM value grfMode
5474 shareMode = GetShareModeFromSTGM(grfMode);
5475 accessMode = GetAccessModeFromSTGM(grfMode);
5477 if (grfMode & STGM_DELETEONRELEASE)
5478 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5479 else
5480 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5482 if (grfMode & STGM_TRANSACTED)
5483 FIXME("Transacted mode not implemented.\n");
5486 * Initialize the "out" parameter.
5488 *ppstgOpen = 0;
5490 hFile = CreateFileW(pwcsName,
5491 accessMode,
5492 shareMode,
5493 NULL,
5494 creationMode,
5495 fileAttributes,
5498 if (hFile == INVALID_HANDLE_VALUE)
5500 return E_FAIL;
5504 * Allocate and initialize the new IStorage32object.
5506 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5508 if (newStorage == 0)
5509 return STG_E_INSUFFICIENTMEMORY;
5511 hr = StorageImpl_Construct(
5512 newStorage,
5513 hFile,
5514 pwcsName,
5515 NULL,
5516 grfMode,
5517 TRUE,
5518 TRUE);
5520 if (FAILED(hr))
5522 HeapFree(GetProcessHeap(), 0, newStorage);
5523 return hr;
5527 * Get an "out" pointer for the caller.
5529 hr = StorageBaseImpl_QueryInterface(
5530 (IStorage*)newStorage,
5531 (REFIID)&IID_IStorage,
5532 (void**)ppstgOpen);
5534 return hr;
5537 /******************************************************************************
5538 * StgOpenStorage [OLE32.148]
5540 HRESULT WINAPI StgOpenStorage(
5541 const OLECHAR *pwcsName,
5542 IStorage *pstgPriority,
5543 DWORD grfMode,
5544 SNB snbExclude,
5545 DWORD reserved,
5546 IStorage **ppstgOpen)
5548 StorageImpl* newStorage = 0;
5549 HRESULT hr = S_OK;
5550 HANDLE hFile = 0;
5551 DWORD shareMode;
5552 DWORD accessMode;
5553 WCHAR fullname[MAX_PATH];
5554 DWORD length;
5556 TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5557 debugstr_w(pwcsName), pstgPriority, grfMode,
5558 snbExclude, reserved, ppstgOpen);
5561 * Perform a sanity check
5563 if (( pwcsName == 0) || (ppstgOpen == 0) )
5565 hr = STG_E_INVALIDPOINTER;
5566 goto end;
5570 * Validate the STGM flags
5572 if ( FAILED( validateSTGM(grfMode) ))
5574 hr = STG_E_INVALIDFLAG;
5575 goto end;
5579 * Interpret the STGM value grfMode
5581 shareMode = GetShareModeFromSTGM(grfMode);
5582 accessMode = GetAccessModeFromSTGM(grfMode);
5585 * Initialize the "out" parameter.
5587 *ppstgOpen = 0;
5589 hFile = CreateFileW( pwcsName,
5590 accessMode,
5591 shareMode,
5592 NULL,
5593 OPEN_EXISTING,
5594 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5597 length = GetFileSize(hFile, NULL);
5599 if (hFile==INVALID_HANDLE_VALUE)
5601 DWORD last_error = GetLastError();
5603 hr = E_FAIL;
5605 switch (last_error)
5607 case ERROR_FILE_NOT_FOUND:
5608 hr = STG_E_FILENOTFOUND;
5609 break;
5611 case ERROR_PATH_NOT_FOUND:
5612 hr = STG_E_PATHNOTFOUND;
5613 break;
5615 case ERROR_ACCESS_DENIED:
5616 case ERROR_WRITE_PROTECT:
5617 hr = STG_E_ACCESSDENIED;
5618 break;
5620 case ERROR_SHARING_VIOLATION:
5621 hr = STG_E_SHAREVIOLATION;
5622 break;
5624 default:
5625 hr = E_FAIL;
5628 goto end;
5632 * Allocate and initialize the new IStorage32object.
5634 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5636 if (newStorage == 0)
5638 hr = STG_E_INSUFFICIENTMEMORY;
5639 goto end;
5642 /* if the file's length was zero, initialize the storage */
5643 hr = StorageImpl_Construct(
5644 newStorage,
5645 hFile,
5646 pwcsName,
5647 NULL,
5648 grfMode,
5649 TRUE,
5650 !length );
5652 if (FAILED(hr))
5654 HeapFree(GetProcessHeap(), 0, newStorage);
5656 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5658 if(hr == STG_E_INVALIDHEADER)
5659 hr = STG_E_FILEALREADYEXISTS;
5660 goto end;
5663 /* prepare the file name string given in lieu of the root property name */
5664 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5665 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5666 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5669 * Get an "out" pointer for the caller.
5671 hr = StorageBaseImpl_QueryInterface(
5672 (IStorage*)newStorage,
5673 (REFIID)&IID_IStorage,
5674 (void**)ppstgOpen);
5676 end:
5677 TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5678 return hr;
5681 /******************************************************************************
5682 * StgCreateDocfileOnILockBytes [OLE32.145]
5684 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5685 ILockBytes *plkbyt,
5686 DWORD grfMode,
5687 DWORD reserved,
5688 IStorage** ppstgOpen)
5690 StorageImpl* newStorage = 0;
5691 HRESULT hr = S_OK;
5694 * Validate the parameters
5696 if ((ppstgOpen == 0) || (plkbyt == 0))
5697 return STG_E_INVALIDPOINTER;
5700 * Allocate and initialize the new IStorage object.
5702 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5704 if (newStorage == 0)
5705 return STG_E_INSUFFICIENTMEMORY;
5707 hr = StorageImpl_Construct(
5708 newStorage,
5711 plkbyt,
5712 grfMode,
5713 FALSE,
5714 TRUE);
5716 if (FAILED(hr))
5718 HeapFree(GetProcessHeap(), 0, newStorage);
5719 return hr;
5723 * Get an "out" pointer for the caller.
5725 hr = StorageBaseImpl_QueryInterface(
5726 (IStorage*)newStorage,
5727 (REFIID)&IID_IStorage,
5728 (void**)ppstgOpen);
5730 return hr;
5733 /******************************************************************************
5734 * StgOpenStorageOnILockBytes [OLE32.149]
5736 HRESULT WINAPI StgOpenStorageOnILockBytes(
5737 ILockBytes *plkbyt,
5738 IStorage *pstgPriority,
5739 DWORD grfMode,
5740 SNB snbExclude,
5741 DWORD reserved,
5742 IStorage **ppstgOpen)
5744 StorageImpl* newStorage = 0;
5745 HRESULT hr = S_OK;
5748 * Perform a sanity check
5750 if ((plkbyt == 0) || (ppstgOpen == 0))
5751 return STG_E_INVALIDPOINTER;
5754 * Validate the STGM flags
5756 if ( FAILED( validateSTGM(grfMode) ))
5757 return STG_E_INVALIDFLAG;
5760 * Initialize the "out" parameter.
5762 *ppstgOpen = 0;
5765 * Allocate and initialize the new IStorage object.
5767 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5769 if (newStorage == 0)
5770 return STG_E_INSUFFICIENTMEMORY;
5772 hr = StorageImpl_Construct(
5773 newStorage,
5776 plkbyt,
5777 grfMode,
5778 FALSE,
5779 FALSE);
5781 if (FAILED(hr))
5783 HeapFree(GetProcessHeap(), 0, newStorage);
5784 return hr;
5788 * Get an "out" pointer for the caller.
5790 hr = StorageBaseImpl_QueryInterface(
5791 (IStorage*)newStorage,
5792 (REFIID)&IID_IStorage,
5793 (void**)ppstgOpen);
5795 return hr;
5798 /******************************************************************************
5799 * StgSetTimes [ole32.150]
5800 * StgSetTimes [OLE32.150]
5804 HRESULT WINAPI StgSetTimes(OLECHAR *str, FILETIME *a, FILETIME *b, FILETIME *c )
5806 FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5807 return S_OK;
5810 /******************************************************************************
5811 * StgIsStorageILockBytes [OLE32.147]
5813 * Determines if the ILockBytes contains a storage object.
5815 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5817 BYTE sig[8];
5818 ULARGE_INTEGER offset;
5820 offset.s.HighPart = 0;
5821 offset.s.LowPart = 0;
5823 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5825 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5826 return S_OK;
5828 return S_FALSE;
5831 /******************************************************************************
5832 * WriteClassStg [OLE32.158]
5834 * This method will store the specified CLSID in the specified storage object
5836 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5838 HRESULT hRes;
5840 assert(pStg != 0);
5842 hRes = IStorage_SetClass(pStg, rclsid);
5844 return hRes;
5847 /***********************************************************************
5848 * ReadClassStg (OLE32.134)
5850 * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5852 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5854 STATSTG pstatstg;
5855 HRESULT hRes;
5857 TRACE("()\n");
5859 if(pclsid==NULL)
5860 return E_POINTER;
5862 * read a STATSTG structure (contains the clsid) from the storage
5864 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5866 if(SUCCEEDED(hRes))
5867 *pclsid=pstatstg.clsid;
5869 return hRes;
5872 /***********************************************************************
5873 * OleLoadFromStream (OLE32.113)
5875 * This function loads an object from stream
5877 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5879 CLSID clsid;
5880 HRESULT res;
5881 LPPERSISTSTREAM xstm;
5883 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5885 res=ReadClassStm(pStm,&clsid);
5886 if (!SUCCEEDED(res))
5887 return res;
5888 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5889 if (!SUCCEEDED(res))
5890 return res;
5891 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5892 if (!SUCCEEDED(res)) {
5893 IUnknown_Release((IUnknown*)*ppvObj);
5894 return res;
5896 res=IPersistStream_Load(xstm,pStm);
5897 IPersistStream_Release(xstm);
5898 /* FIXME: all refcounts ok at this point? I think they should be:
5899 * pStm : unchanged
5900 * ppvObj : 1
5901 * xstm : 0 (released)
5903 return res;
5906 /***********************************************************************
5907 * OleSaveToStream (OLE32.125)
5909 * This function saves an object with the IPersistStream interface on it
5910 * to the specified stream.
5912 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5915 CLSID clsid;
5916 HRESULT res;
5918 TRACE("(%p,%p)\n",pPStm,pStm);
5920 res=IPersistStream_GetClassID(pPStm,&clsid);
5922 if (SUCCEEDED(res)){
5924 res=WriteClassStm(pStm,&clsid);
5926 if (SUCCEEDED(res))
5928 res=IPersistStream_Save(pPStm,pStm,TRUE);
5931 TRACE("Finished Save\n");
5932 return res;
5935 /****************************************************************************
5936 * This method validate a STGM parameter that can contain the values below
5938 * STGM_DIRECT 0x00000000
5939 * STGM_TRANSACTED 0x00010000
5940 * STGM_SIMPLE 0x08000000
5942 * STGM_READ 0x00000000
5943 * STGM_WRITE 0x00000001
5944 * STGM_READWRITE 0x00000002
5946 * STGM_SHARE_DENY_NONE 0x00000040
5947 * STGM_SHARE_DENY_READ 0x00000030
5948 * STGM_SHARE_DENY_WRITE 0x00000020
5949 * STGM_SHARE_EXCLUSIVE 0x00000010
5951 * STGM_PRIORITY 0x00040000
5952 * STGM_DELETEONRELEASE 0x04000000
5954 * STGM_CREATE 0x00001000
5955 * STGM_CONVERT 0x00020000
5956 * STGM_FAILIFTHERE 0x00000000
5958 * STGM_NOSCRATCH 0x00100000
5959 * STGM_NOSNAPSHOT 0x00200000
5961 static HRESULT validateSTGM(DWORD stgm)
5963 BOOL bSTGM_TRANSACTED = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5964 BOOL bSTGM_SIMPLE = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5965 BOOL bSTGM_DIRECT = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5967 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
5968 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5969 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
5971 BOOL bSTGM_SHARE_DENY_NONE =
5972 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
5974 BOOL bSTGM_SHARE_DENY_READ =
5975 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
5977 BOOL bSTGM_SHARE_DENY_WRITE =
5978 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5980 BOOL bSTGM_SHARE_EXCLUSIVE =
5981 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
5983 BOOL bSTGM_CREATE = ((stgm & STGM_CREATE) == STGM_CREATE);
5984 BOOL bSTGM_CONVERT = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5986 BOOL bSTGM_NOSCRATCH = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5987 BOOL bSTGM_NOSNAPSHOT = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5990 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5992 if ( ! bSTGM_DIRECT )
5993 if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5994 return E_FAIL;
5997 * STGM_WRITE | STGM_READWRITE | STGM_READ
5999 if ( ! bSTGM_READ )
6000 if( bSTGM_WRITE && bSTGM_READWRITE )
6001 return E_FAIL;
6004 * STGM_SHARE_DENY_NONE | others
6005 * (I assume here that DENY_READ implies DENY_WRITE)
6007 if ( bSTGM_SHARE_DENY_NONE )
6008 if ( bSTGM_SHARE_DENY_READ ||
6009 bSTGM_SHARE_DENY_WRITE ||
6010 bSTGM_SHARE_EXCLUSIVE)
6011 return E_FAIL;
6014 * STGM_CREATE | STGM_CONVERT
6015 * if both are false, STGM_FAILIFTHERE is set to TRUE
6017 if ( bSTGM_CREATE && bSTGM_CONVERT )
6018 return E_FAIL;
6021 * STGM_NOSCRATCH requires STGM_TRANSACTED
6023 if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6024 return E_FAIL;
6027 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6028 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6030 if (bSTGM_NOSNAPSHOT)
6032 if ( ! ( bSTGM_TRANSACTED &&
6033 !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6034 return E_FAIL;
6037 return S_OK;
6040 /****************************************************************************
6041 * GetShareModeFromSTGM
6043 * This method will return a share mode flag from a STGM value.
6044 * The STGM value is assumed valid.
6046 static DWORD GetShareModeFromSTGM(DWORD stgm)
6048 DWORD dwShareMode = 0;
6049 BOOL bSTGM_SHARE_DENY_NONE =
6050 ((stgm & STGM_SHARE_DENY_NONE) == STGM_SHARE_DENY_NONE);
6052 BOOL bSTGM_SHARE_DENY_READ =
6053 ((stgm & STGM_SHARE_DENY_READ) == STGM_SHARE_DENY_READ);
6055 BOOL bSTGM_SHARE_DENY_WRITE =
6056 ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6058 BOOL bSTGM_SHARE_EXCLUSIVE =
6059 ((stgm & STGM_SHARE_EXCLUSIVE) == STGM_SHARE_EXCLUSIVE);
6061 if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6062 dwShareMode = 0;
6064 if (bSTGM_SHARE_DENY_NONE)
6065 dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6067 if (bSTGM_SHARE_DENY_WRITE)
6068 dwShareMode = FILE_SHARE_READ;
6070 return dwShareMode;
6073 /****************************************************************************
6074 * GetAccessModeFromSTGM
6076 * This method will return an access mode flag from a STGM value.
6077 * The STGM value is assumed valid.
6079 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6081 DWORD dwDesiredAccess = GENERIC_READ;
6082 BOOL bSTGM_WRITE = ((stgm & STGM_WRITE) == STGM_WRITE);
6083 BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6084 BOOL bSTGM_READ = ! (bSTGM_WRITE || bSTGM_READWRITE);
6086 if (bSTGM_READ)
6087 dwDesiredAccess = GENERIC_READ;
6089 if (bSTGM_WRITE)
6090 dwDesiredAccess |= GENERIC_WRITE;
6092 if (bSTGM_READWRITE)
6093 dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6095 return dwDesiredAccess;
6098 /****************************************************************************
6099 * GetCreationModeFromSTGM
6101 * This method will return a creation mode flag from a STGM value.
6102 * The STGM value is assumed valid.
6104 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6106 if ( stgm & STGM_CREATE)
6107 return CREATE_ALWAYS;
6108 if (stgm & STGM_CONVERT) {
6109 FIXME("STGM_CONVERT not implemented!\n");
6110 return CREATE_NEW;
6112 /* All other cases */
6113 if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6114 FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6115 return CREATE_NEW;
6119 /*************************************************************************
6120 * OLECONVERT_LoadOLE10 [Internal]
6122 * Loads the OLE10 STREAM to memory
6124 * PARAMS
6125 * pOleStream [I] The OLESTREAM
6126 * pData [I] Data Structure for the OLESTREAM Data
6128 * RETURNS
6129 * Success: S_OK
6130 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6131 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6133 * NOTES
6134 * This function is used by OleConvertOLESTREAMToIStorage only.
6136 * Memory allocated for pData must be freed by the caller
6138 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6140 DWORD dwSize;
6141 HRESULT hRes = S_OK;
6142 int nTryCnt=0;
6143 int max_try = 6;
6145 pData->pData = NULL;
6146 pData->pstrOleObjFileName = (CHAR *) NULL;
6148 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6150 /* Get the OleID */
6151 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6152 if(dwSize != sizeof(pData->dwOleID))
6154 hRes = CONVERT10_E_OLESTREAM_GET;
6156 else if(pData->dwOleID != OLESTREAM_ID)
6158 hRes = CONVERT10_E_OLESTREAM_FMT;
6160 else
6162 hRes = S_OK;
6163 break;
6167 if(hRes == S_OK)
6169 /* Get the TypeID...more info needed for this field */
6170 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6171 if(dwSize != sizeof(pData->dwTypeID))
6173 hRes = CONVERT10_E_OLESTREAM_GET;
6176 if(hRes == S_OK)
6178 if(pData->dwTypeID != 0)
6180 /* Get the length of the OleTypeName */
6181 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6182 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6184 hRes = CONVERT10_E_OLESTREAM_GET;
6187 if(hRes == S_OK)
6189 if(pData->dwOleTypeNameLength > 0)
6191 /* Get the OleTypeName */
6192 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6193 if(dwSize != pData->dwOleTypeNameLength)
6195 hRes = CONVERT10_E_OLESTREAM_GET;
6199 if(bStrem1)
6201 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6202 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6204 hRes = CONVERT10_E_OLESTREAM_GET;
6206 if(hRes == S_OK)
6208 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6209 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6210 pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6211 if(pData->pstrOleObjFileName)
6213 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6214 if(dwSize != pData->dwOleObjFileNameLength)
6216 hRes = CONVERT10_E_OLESTREAM_GET;
6219 else
6220 hRes = CONVERT10_E_OLESTREAM_GET;
6223 else
6225 /* Get the Width of the Metafile */
6226 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6227 if(dwSize != sizeof(pData->dwMetaFileWidth))
6229 hRes = CONVERT10_E_OLESTREAM_GET;
6231 if(hRes == S_OK)
6233 /* Get the Height of the Metafile */
6234 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6235 if(dwSize != sizeof(pData->dwMetaFileHeight))
6237 hRes = CONVERT10_E_OLESTREAM_GET;
6241 if(hRes == S_OK)
6243 /* Get the Length of the Data */
6244 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6245 if(dwSize != sizeof(pData->dwDataLength))
6247 hRes = CONVERT10_E_OLESTREAM_GET;
6251 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6253 if(!bStrem1) /* if it is a second OLE stream data */
6255 pData->dwDataLength -= 8;
6256 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6257 if(dwSize != sizeof(pData->strUnknown))
6259 hRes = CONVERT10_E_OLESTREAM_GET;
6263 if(hRes == S_OK)
6265 if(pData->dwDataLength > 0)
6267 pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6269 /* Get Data (ex. IStorage, Metafile, or BMP) */
6270 if(pData->pData)
6272 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6273 if(dwSize != pData->dwDataLength)
6275 hRes = CONVERT10_E_OLESTREAM_GET;
6278 else
6280 hRes = CONVERT10_E_OLESTREAM_GET;
6286 return hRes;
6289 /*************************************************************************
6290 * OLECONVERT_SaveOLE10 [Internal]
6292 * Saves the OLE10 STREAM From memory
6294 * PARAMS
6295 * pData [I] Data Structure for the OLESTREAM Data
6296 * pOleStream [I] The OLESTREAM to save
6298 * RETURNS
6299 * Success: S_OK
6300 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6302 * NOTES
6303 * This function is used by OleConvertIStorageToOLESTREAM only.
6306 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6308 DWORD dwSize;
6309 HRESULT hRes = S_OK;
6312 /* Set the OleID */
6313 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6314 if(dwSize != sizeof(pData->dwOleID))
6316 hRes = CONVERT10_E_OLESTREAM_PUT;
6319 if(hRes == S_OK)
6321 /* Set the TypeID */
6322 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6323 if(dwSize != sizeof(pData->dwTypeID))
6325 hRes = CONVERT10_E_OLESTREAM_PUT;
6329 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6331 /* Set the Length of the OleTypeName */
6332 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6333 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6335 hRes = CONVERT10_E_OLESTREAM_PUT;
6338 if(hRes == S_OK)
6340 if(pData->dwOleTypeNameLength > 0)
6342 /* Set the OleTypeName */
6343 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6344 if(dwSize != pData->dwOleTypeNameLength)
6346 hRes = CONVERT10_E_OLESTREAM_PUT;
6351 if(hRes == S_OK)
6353 /* Set the width of the Metafile */
6354 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6355 if(dwSize != sizeof(pData->dwMetaFileWidth))
6357 hRes = CONVERT10_E_OLESTREAM_PUT;
6361 if(hRes == S_OK)
6363 /* Set the height of the Metafile */
6364 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6365 if(dwSize != sizeof(pData->dwMetaFileHeight))
6367 hRes = CONVERT10_E_OLESTREAM_PUT;
6371 if(hRes == S_OK)
6373 /* Set the length of the Data */
6374 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6375 if(dwSize != sizeof(pData->dwDataLength))
6377 hRes = CONVERT10_E_OLESTREAM_PUT;
6381 if(hRes == S_OK)
6383 if(pData->dwDataLength > 0)
6385 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6386 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6387 if(dwSize != pData->dwDataLength)
6389 hRes = CONVERT10_E_OLESTREAM_PUT;
6394 return hRes;
6397 /*************************************************************************
6398 * OLECONVERT_GetOLE20FromOLE10[Internal]
6400 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6401 * opens it, and copies the content to the dest IStorage for
6402 * OleConvertOLESTREAMToIStorage
6405 * PARAMS
6406 * pDestStorage [I] The IStorage to copy the data to
6407 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6408 * nBufferLength [I] The size of the buffer
6410 * RETURNS
6411 * Nothing
6413 * NOTES
6417 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6419 HRESULT hRes;
6420 HANDLE hFile;
6421 IStorage *pTempStorage;
6422 DWORD dwNumOfBytesWritten;
6423 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6424 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6426 /* Create a temp File */
6427 GetTempPathW(MAX_PATH, wstrTempDir);
6428 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6429 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6431 if(hFile != INVALID_HANDLE_VALUE)
6433 /* Write IStorage Data to File */
6434 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6435 CloseHandle(hFile);
6437 /* Open and copy temp storage to the Dest Storage */
6438 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6439 if(hRes == S_OK)
6441 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6442 StorageBaseImpl_Release(pTempStorage);
6444 DeleteFileW(wstrTempFile);
6449 /*************************************************************************
6450 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6452 * Saves the OLE10 STREAM From memory
6454 * PARAMS
6455 * pStorage [I] The Src IStorage to copy
6456 * pData [I] The Dest Memory to write to.
6458 * RETURNS
6459 * The size in bytes allocated for pData
6461 * NOTES
6462 * Memory allocated for pData must be freed by the caller
6464 * Used by OleConvertIStorageToOLESTREAM only.
6467 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6469 HANDLE hFile;
6470 HRESULT hRes;
6471 DWORD nDataLength = 0;
6472 IStorage *pTempStorage;
6473 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6474 WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6476 *pData = NULL;
6478 /* Create temp Storage */
6479 GetTempPathW(MAX_PATH, wstrTempDir);
6480 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6481 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6483 if(hRes == S_OK)
6485 /* Copy Src Storage to the Temp Storage */
6486 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6487 StorageBaseImpl_Release(pTempStorage);
6489 /* Open Temp Storage as a file and copy to memory */
6490 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6491 if(hFile != INVALID_HANDLE_VALUE)
6493 nDataLength = GetFileSize(hFile, NULL);
6494 *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6495 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6496 CloseHandle(hFile);
6498 DeleteFileW(wstrTempFile);
6500 return nDataLength;
6503 /*************************************************************************
6504 * OLECONVERT_CreateOleStream [Internal]
6506 * Creates the "\001OLE" stream in the IStorage if necessary.
6508 * PARAMS
6509 * pStorage [I] Dest storage to create the stream in
6511 * RETURNS
6512 * Nothing
6514 * NOTES
6515 * This function is used by OleConvertOLESTREAMToIStorage only.
6517 * This stream is still unknown, MS Word seems to have extra data
6518 * but since the data is stored in the OLESTREAM there should be
6519 * no need to recreate the stream. If the stream is manually
6520 * deleted it will create it with this default data.
6523 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6525 HRESULT hRes;
6526 IStream *pStream;
6527 WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6528 BYTE pOleStreamHeader [] =
6530 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6531 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6532 0x00, 0x00, 0x00, 0x00
6535 /* Create stream if not present */
6536 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6537 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6539 if(hRes == S_OK)
6541 /* Write default Data */
6542 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6543 IStream_Release(pStream);
6547 /* write a string to a stream, preceded by its length */
6548 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6550 HRESULT r;
6551 LPSTR str;
6552 DWORD len = 0;
6554 if( string )
6555 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6556 r = IStream_Write( stm, &len, sizeof(len), NULL);
6557 if( FAILED( r ) )
6558 return r;
6559 if(len == 0)
6560 return r;
6561 str = CoTaskMemAlloc( len );
6562 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6563 r = IStream_Write( stm, str, len, NULL);
6564 CoTaskMemFree( str );
6565 return r;
6568 /* read a string preceded by its length from a stream */
6569 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6571 HRESULT r;
6572 DWORD len, count = 0;
6573 LPSTR str;
6574 LPWSTR wstr;
6576 r = IStream_Read( stm, &len, sizeof(len), &count );
6577 if( FAILED( r ) )
6578 return r;
6579 if( count != sizeof(len) )
6580 return E_OUTOFMEMORY;
6582 TRACE("%ld bytes\n",len);
6584 str = CoTaskMemAlloc( len );
6585 if( !str )
6586 return E_OUTOFMEMORY;
6587 count = 0;
6588 r = IStream_Read( stm, str, len, &count );
6589 if( FAILED( r ) )
6590 return r;
6591 if( count != len )
6593 CoTaskMemFree( str );
6594 return E_OUTOFMEMORY;
6597 TRACE("Read string %s\n",debugstr_an(str,len));
6599 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6600 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6601 if( wstr )
6602 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6603 CoTaskMemFree( str );
6605 *string = wstr;
6607 return r;
6611 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6612 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6614 IStream *pstm;
6615 HRESULT r = S_OK;
6616 WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6618 static const BYTE unknown1[12] =
6619 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6620 0xFF, 0xFF, 0xFF, 0xFF};
6621 static const BYTE unknown2[16] =
6622 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6623 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6625 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6626 debugstr_w(lpszUserType), debugstr_w(szClipName),
6627 debugstr_w(szProgIDName));
6629 /* Create a CompObj stream if it doesn't exist */
6630 r = IStorage_CreateStream(pstg, szwStreamName,
6631 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6632 if( FAILED (r) )
6633 return r;
6635 /* Write CompObj Structure to stream */
6636 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6638 if( SUCCEEDED( r ) )
6639 r = WriteClassStm( pstm, clsid );
6641 if( SUCCEEDED( r ) )
6642 r = STREAM_WriteString( pstm, lpszUserType );
6643 if( SUCCEEDED( r ) )
6644 r = STREAM_WriteString( pstm, szClipName );
6645 if( SUCCEEDED( r ) )
6646 r = STREAM_WriteString( pstm, szProgIDName );
6647 if( SUCCEEDED( r ) )
6648 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6650 IStream_Release( pstm );
6652 return r;
6655 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6656 static HRESULT CLSIDFromUserType(LPCWSTR lpszUserType, CLSID *clsid)
6658 LONG r, count, i, len;
6659 WCHAR szKey[0x40];
6660 HKEY hkey, hkeyclsid;
6661 LPWSTR buffer = NULL;
6662 BOOL found = FALSE;
6663 const WCHAR szclsid[] = { 'C','L','S','I','D',0 };
6665 TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType));
6667 r = RegOpenKeyW( HKEY_CLASSES_ROOT, szclsid, &hkeyclsid );
6668 if( r )
6669 return E_INVALIDARG;
6671 len = lstrlenW( lpszUserType ) + 1;
6672 buffer = CoTaskMemAlloc( len * sizeof (WCHAR) );
6673 if( !buffer )
6674 goto end;
6676 for(i=0; !found; i++ )
6678 r = RegEnumKeyW( hkeyclsid, i, szKey, sizeof(szKey)/sizeof(WCHAR));
6679 if( r != ERROR_SUCCESS )
6680 break;
6681 hkey = 0;
6682 r = RegOpenKeyW( hkeyclsid, szKey, &hkey );
6683 if( r != ERROR_SUCCESS )
6684 break;
6685 count = len * sizeof (WCHAR);
6686 r = RegQueryValueW( hkey, NULL, buffer, &count );
6687 found = ( r == ERROR_SUCCESS ) &&
6688 ( count == len*sizeof(WCHAR) ) &&
6689 !lstrcmpW( buffer, lpszUserType ) ;
6690 RegCloseKey( hkey );
6693 end:
6694 if( buffer )
6695 CoTaskMemFree( buffer );
6696 RegCloseKey( hkeyclsid );
6698 if ( !found )
6699 return E_INVALIDARG;
6701 TRACE("clsid is %s\n", debugstr_w( szKey ) );
6703 r = CLSIDFromString( szKey, clsid );
6705 return r;
6709 /***********************************************************************
6710 * WriteFmtUserTypeStg (OLE32.160)
6712 HRESULT WINAPI WriteFmtUserTypeStg(
6713 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6715 HRESULT r;
6716 WCHAR szwClipName[0x40];
6717 WCHAR szCLSIDName[OLESTREAM_MAX_STR_LEN];
6718 CLSID clsid;
6719 LPWSTR wstrProgID;
6720 DWORD n;
6721 LPMALLOC allocator = NULL;
6723 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6725 r = CoGetMalloc(0, &allocator);
6726 if( FAILED( r) )
6727 return E_OUTOFMEMORY;
6729 /* get the clipboard format name */
6730 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6731 szwClipName[n]=0;
6733 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6735 /* Get the CLSID */
6736 szCLSIDName[0]=0;
6737 r = CLSIDFromUserType(lpszUserType, &clsid);
6738 if( FAILED( r ) )
6739 return r;
6741 TRACE("CLSID is %s\n",debugstr_guid(&clsid));
6743 /* get the real program ID */
6744 r = ProgIDFromCLSID( &clsid, &wstrProgID);
6745 if( FAILED( r ) )
6746 return r;
6748 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6750 /* if we have a good string, write the stream */
6751 if( wstrProgID )
6752 r = STORAGE_WriteCompObj( pstg, &clsid,
6753 lpszUserType, szwClipName, wstrProgID );
6754 else
6755 r = E_OUTOFMEMORY;
6757 IMalloc_Free( allocator, wstrProgID);
6759 return r;
6763 /******************************************************************************
6764 * ReadFmtUserTypeStg [OLE32.136]
6766 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6768 HRESULT r;
6769 IStream *stm = 0;
6770 const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6771 unsigned char unknown1[12];
6772 unsigned char unknown2[16];
6773 DWORD count;
6774 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6775 CLSID clsid;
6777 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6779 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6780 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6781 if( FAILED ( r ) )
6783 ERR("Failed to open stream\n");
6784 return r;
6787 /* read the various parts of the structure */
6788 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6789 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6790 goto end;
6791 r = ReadClassStm( stm, &clsid );
6792 if( FAILED( r ) )
6793 goto end;
6795 r = STREAM_ReadString( stm, &szCLSIDName );
6796 if( FAILED( r ) )
6797 goto end;
6799 r = STREAM_ReadString( stm, &szOleTypeName );
6800 if( FAILED( r ) )
6801 goto end;
6803 r = STREAM_ReadString( stm, &szProgIDName );
6804 if( FAILED( r ) )
6805 goto end;
6807 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6808 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6809 goto end;
6811 /* ok, success... now we just need to store what we found */
6812 if( pcf )
6813 *pcf = RegisterClipboardFormatW( szOleTypeName );
6814 CoTaskMemFree( szOleTypeName );
6816 if( lplpszUserType )
6817 *lplpszUserType = szCLSIDName;
6818 CoTaskMemFree( szProgIDName );
6820 end:
6821 IStream_Release( stm );
6823 return r;
6827 /*************************************************************************
6828 * OLECONVERT_CreateCompObjStream [Internal]
6830 * Creates a "\001CompObj" is the destination IStorage if necessary.
6832 * PARAMS
6833 * pStorage [I] The dest IStorage to create the CompObj Stream
6834 * if necessary.
6835 * strOleTypeName [I] The ProgID
6837 * RETURNS
6838 * Success: S_OK
6839 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6841 * NOTES
6842 * This function is used by OleConvertOLESTREAMToIStorage only.
6844 * The stream data is stored in the OLESTREAM and there should be
6845 * no need to recreate the stream. If the stream is manually
6846 * deleted it will attempt to create it by querying the registry.
6850 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6852 IStream *pStream;
6853 HRESULT hStorageRes, hRes = S_OK;
6854 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6855 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6857 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6858 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6860 /* Initialize the CompObj structure */
6861 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6862 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6863 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6866 /* Create a CompObj stream if it doesn't exist */
6867 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6868 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6869 if(hStorageRes == S_OK)
6871 /* copy the OleTypeName to the compobj struct */
6872 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6873 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6875 /* copy the OleTypeName to the compobj struct */
6876 /* Note: in the test made, these were Identical */
6877 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6878 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6880 /* Get the CLSID */
6881 hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6883 if(hRes == S_OK)
6885 HKEY hKey;
6886 LONG hErr;
6887 /* Get the CLSID Default Name from the Registry */
6888 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6889 if(hErr == ERROR_SUCCESS)
6891 char strTemp[OLESTREAM_MAX_STR_LEN];
6892 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6893 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6894 if(hErr == ERROR_SUCCESS)
6896 strcpy(IStorageCompObj.strCLSIDName, strTemp);
6898 RegCloseKey(hKey);
6902 /* Write CompObj Structure to stream */
6903 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6905 WriteClassStm(pStream,&(IStorageCompObj.clsid));
6907 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6908 if(IStorageCompObj.dwCLSIDNameLength > 0)
6910 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6912 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6913 if(IStorageCompObj.dwOleTypeNameLength > 0)
6915 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6917 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6918 if(IStorageCompObj.dwProgIDNameLength > 0)
6920 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6922 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6923 IStream_Release(pStream);
6925 return hRes;
6929 /*************************************************************************
6930 * OLECONVERT_CreateOlePresStream[Internal]
6932 * Creates the "\002OlePres000" Stream with the Metafile data
6934 * PARAMS
6935 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
6936 * dwExtentX [I] Width of the Metafile
6937 * dwExtentY [I] Height of the Metafile
6938 * pData [I] Metafile data
6939 * dwDataLength [I] Size of the Metafile data
6941 * RETURNS
6942 * Success: S_OK
6943 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6945 * NOTES
6946 * This function is used by OleConvertOLESTREAMToIStorage only.
6949 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6951 HRESULT hRes;
6952 IStream *pStream;
6953 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6954 BYTE pOlePresStreamHeader [] =
6956 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6957 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6958 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6959 0x00, 0x00, 0x00, 0x00
6962 BYTE pOlePresStreamHeaderEmpty [] =
6964 0x00, 0x00, 0x00, 0x00,
6965 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6966 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6967 0x00, 0x00, 0x00, 0x00
6970 /* Create the OlePres000 Stream */
6971 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6972 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6974 if(hRes == S_OK)
6976 DWORD nHeaderSize;
6977 OLECONVERT_ISTORAGE_OLEPRES OlePres;
6979 memset(&OlePres, 0, sizeof(OlePres));
6980 /* Do we have any metafile data to save */
6981 if(dwDataLength > 0)
6983 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6984 nHeaderSize = sizeof(pOlePresStreamHeader);
6986 else
6988 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6989 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6991 /* Set width and height of the metafile */
6992 OlePres.dwExtentX = dwExtentX;
6993 OlePres.dwExtentY = -dwExtentY;
6995 /* Set Data and Length */
6996 if(dwDataLength > sizeof(METAFILEPICT16))
6998 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6999 OlePres.pData = &(pData[8]);
7001 /* Save OlePres000 Data to Stream */
7002 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7003 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7004 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7005 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7006 if(OlePres.dwSize > 0)
7008 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7010 IStream_Release(pStream);
7014 /*************************************************************************
7015 * OLECONVERT_CreateOle10NativeStream [Internal]
7017 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7019 * PARAMS
7020 * pStorage [I] Dest storage to create the stream in
7021 * pData [I] Ole10 Native Data (ex. bmp)
7022 * dwDataLength [I] Size of the Ole10 Native Data
7024 * RETURNS
7025 * Nothing
7027 * NOTES
7028 * This function is used by OleConvertOLESTREAMToIStorage only.
7030 * Might need to verify the data and return appropriate error message
7033 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7035 HRESULT hRes;
7036 IStream *pStream;
7037 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7039 /* Create the Ole10Native Stream */
7040 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7041 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7043 if(hRes == S_OK)
7045 /* Write info to stream */
7046 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7047 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7048 IStream_Release(pStream);
7053 /*************************************************************************
7054 * OLECONVERT_GetOLE10ProgID [Internal]
7056 * Finds the ProgID (or OleTypeID) from the IStorage
7058 * PARAMS
7059 * pStorage [I] The Src IStorage to get the ProgID
7060 * strProgID [I] the ProgID string to get
7061 * dwSize [I] the size of the string
7063 * RETURNS
7064 * Success: S_OK
7065 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7067 * NOTES
7068 * This function is used by OleConvertIStorageToOLESTREAM only.
7072 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7074 HRESULT hRes;
7075 IStream *pStream;
7076 LARGE_INTEGER iSeekPos;
7077 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7078 WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7080 /* Open the CompObj Stream */
7081 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7082 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7083 if(hRes == S_OK)
7086 /*Get the OleType from the CompObj Stream */
7087 iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7088 iSeekPos.s.HighPart = 0;
7090 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7091 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7092 iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
7093 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7094 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7095 iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
7096 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7098 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7099 if(*dwSize > 0)
7101 IStream_Read(pStream, strProgID, *dwSize, NULL);
7103 IStream_Release(pStream);
7105 else
7107 STATSTG stat;
7108 LPOLESTR wstrProgID;
7110 /* Get the OleType from the registry */
7111 REFCLSID clsid = &(stat.clsid);
7112 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7113 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7114 if(hRes == S_OK)
7116 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7120 return hRes;
7123 /*************************************************************************
7124 * OLECONVERT_GetOle10PresData [Internal]
7126 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7128 * PARAMS
7129 * pStorage [I] Src IStroage
7130 * pOleStream [I] Dest OleStream Mem Struct
7132 * RETURNS
7133 * Nothing
7135 * NOTES
7136 * This function is used by OleConvertIStorageToOLESTREAM only.
7138 * Memory allocated for pData must be freed by the caller
7142 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7145 HRESULT hRes;
7146 IStream *pStream;
7147 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7149 /* Initialize Default data for OLESTREAM */
7150 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7151 pOleStreamData[0].dwTypeID = 2;
7152 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7153 pOleStreamData[1].dwTypeID = 0;
7154 pOleStreamData[0].dwMetaFileWidth = 0;
7155 pOleStreamData[0].dwMetaFileHeight = 0;
7156 pOleStreamData[0].pData = NULL;
7157 pOleStreamData[1].pData = NULL;
7159 /* Open Ole10Native Stream */
7160 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7161 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7162 if(hRes == S_OK)
7165 /* Read Size and Data */
7166 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7167 if(pOleStreamData->dwDataLength > 0)
7169 pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7170 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7172 IStream_Release(pStream);
7178 /*************************************************************************
7179 * OLECONVERT_GetOle20PresData[Internal]
7181 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7183 * PARAMS
7184 * pStorage [I] Src IStroage
7185 * pOleStreamData [I] Dest OleStream Mem Struct
7187 * RETURNS
7188 * Nothing
7190 * NOTES
7191 * This function is used by OleConvertIStorageToOLESTREAM only.
7193 * Memory allocated for pData must be freed by the caller
7195 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7197 HRESULT hRes;
7198 IStream *pStream;
7199 OLECONVERT_ISTORAGE_OLEPRES olePress;
7200 WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7202 /* Initialize Default data for OLESTREAM */
7203 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7204 pOleStreamData[0].dwTypeID = 2;
7205 pOleStreamData[0].dwMetaFileWidth = 0;
7206 pOleStreamData[0].dwMetaFileHeight = 0;
7207 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7208 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7209 pOleStreamData[1].dwTypeID = 0;
7210 pOleStreamData[1].dwOleTypeNameLength = 0;
7211 pOleStreamData[1].strOleTypeName[0] = 0;
7212 pOleStreamData[1].dwMetaFileWidth = 0;
7213 pOleStreamData[1].dwMetaFileHeight = 0;
7214 pOleStreamData[1].pData = NULL;
7215 pOleStreamData[1].dwDataLength = 0;
7218 /* Open OlePress000 stream */
7219 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7220 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7221 if(hRes == S_OK)
7223 LARGE_INTEGER iSeekPos;
7224 METAFILEPICT16 MetaFilePict;
7225 char strMetafilePictName[] = "METAFILEPICT";
7227 /* Set the TypeID for a Metafile */
7228 pOleStreamData[1].dwTypeID = 5;
7230 /* Set the OleTypeName to Metafile */
7231 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7232 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7234 iSeekPos.s.HighPart = 0;
7235 iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
7237 /* Get Presentation Data */
7238 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7239 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7240 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7241 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7243 /*Set width and Height */
7244 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7245 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7246 if(olePress.dwSize > 0)
7248 /* Set Length */
7249 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7251 /* Set MetaFilePict struct */
7252 MetaFilePict.mm = 8;
7253 MetaFilePict.xExt = olePress.dwExtentX;
7254 MetaFilePict.yExt = olePress.dwExtentY;
7255 MetaFilePict.hMF = 0;
7257 /* Get Metafile Data */
7258 pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7259 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7260 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7262 IStream_Release(pStream);
7266 /*************************************************************************
7267 * OleConvertOLESTREAMToIStorage [OLE32.87]
7269 * Read info on MSDN
7271 * TODO
7272 * DVTARGETDEVICE paramenter is not handled
7273 * Still unsure of some mem fields for OLE 10 Stream
7274 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7275 * and "\001OLE" streams
7278 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7279 LPOLESTREAM pOleStream,
7280 LPSTORAGE pstg,
7281 const DVTARGETDEVICE* ptd)
7283 int i;
7284 HRESULT hRes=S_OK;
7285 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7287 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7289 if(ptd != NULL)
7291 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7294 if(pstg == NULL || pOleStream == NULL)
7296 hRes = E_INVALIDARG;
7299 if(hRes == S_OK)
7301 /* Load the OLESTREAM to Memory */
7302 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7305 if(hRes == S_OK)
7307 /* Load the OLESTREAM to Memory (part 2)*/
7308 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7311 if(hRes == S_OK)
7314 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7316 /* Do we have the IStorage Data in the OLESTREAM */
7317 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7319 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7320 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7322 else
7324 /* It must be an original OLE 1.0 source */
7325 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7328 else
7330 /* It must be an original OLE 1.0 source */
7331 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7334 /* Create CompObj Stream if necessary */
7335 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7336 if(hRes == S_OK)
7338 /*Create the Ole Stream if necessary */
7339 OLECONVERT_CreateOleStream(pstg);
7344 /* Free allocated memory */
7345 for(i=0; i < 2; i++)
7347 if(pOleStreamData[i].pData != NULL)
7349 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7351 if(pOleStreamData[i].pstrOleObjFileName != NULL)
7353 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7354 pOleStreamData[i].pstrOleObjFileName = NULL;
7357 return hRes;
7360 /*************************************************************************
7361 * OleConvertIStorageToOLESTREAM [OLE32.85]
7363 * Read info on MSDN
7365 * Read info on MSDN
7367 * TODO
7368 * Still unsure of some mem fields for OLE 10 Stream
7369 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7370 * and "\001OLE" streams.
7373 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7374 LPSTORAGE pstg,
7375 LPOLESTREAM pOleStream)
7377 int i;
7378 HRESULT hRes = S_OK;
7379 IStream *pStream;
7380 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7381 WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7384 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7386 if(pstg == NULL || pOleStream == NULL)
7388 hRes = E_INVALIDARG;
7390 if(hRes == S_OK)
7392 /* Get the ProgID */
7393 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7394 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7396 if(hRes == S_OK)
7398 /* Was it originally Ole10 */
7399 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7400 if(hRes == S_OK)
7402 IStream_Release(pStream);
7403 /* Get Presentation Data for Ole10Native */
7404 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7406 else
7408 /* Get Presentation Data (OLE20) */
7409 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7412 /* Save OLESTREAM */
7413 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7414 if(hRes == S_OK)
7416 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7421 /* Free allocated memory */
7422 for(i=0; i < 2; i++)
7424 if(pOleStreamData[i].pData != NULL)
7426 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7430 return hRes;
7433 /***********************************************************************
7434 * GetConvertStg (OLE32.68)
7436 HRESULT WINAPI GetConvertStg(LPGUID guid) {
7437 FIXME("(%s), unimplemented stub!\n",debugstr_guid(guid));
7438 return E_FAIL;
7441 /******************************************************************************
7442 * StgIsStorageFile [OLE32.146]
7444 HRESULT WINAPI
7445 StgIsStorageFile(LPCOLESTR fn)
7447 HANDLE hf;
7448 BYTE magic[8];
7449 DWORD bytes_read;
7451 TRACE("(\'%s\')\n", debugstr_w(fn));
7452 hf = CreateFileW(fn, GENERIC_READ,
7453 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7454 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7456 if (hf == INVALID_HANDLE_VALUE)
7457 return STG_E_FILENOTFOUND;
7459 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7461 WARN(" unable to read file\n");
7462 CloseHandle(hf);
7463 return S_FALSE;
7466 CloseHandle(hf);
7468 if (bytes_read != 8) {
7469 WARN(" too short\n");
7470 return S_FALSE;
7473 if (!memcmp(magic,STORAGE_magic,8)) {
7474 WARN(" -> YES\n");
7475 return S_OK;
7478 WARN(" -> Invalid header.\n");
7479 return S_FALSE;