ole32: Add DebugInfo to critical sections.
[wine/hacks.git] / dlls / ole32 / storage32.c
blobdc2db11474beaa93e87e353cb8a6bc53914a9269
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
11 * Copyright 2005 Mike McCormack
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * NOTES
28 * The compound file implementation of IStorage used for create
29 * and manage substorages and streams within a storage object
30 * residing in a compound file object.
32 * MSDN
33 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/stg/stg/istorage_compound_file_implementation.asp
36 #include <assert.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
42 #define COBJMACROS
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
46 #include "windef.h"
47 #include "winbase.h"
48 #include "winnls.h"
49 #include "winuser.h"
50 #include "wine/unicode.h"
51 #include "wine/debug.h"
53 #include "storage32.h"
54 #include "ole2.h" /* For Write/ReadClassStm */
56 #include "winreg.h"
57 #include "wine/wingdi16.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(storage);
61 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
62 #define OLESTREAM_ID 0x501
63 #define OLESTREAM_MAX_STR_LEN 255
65 static const char rootPropertyName[] = "Root Entry";
67 /****************************************************************************
68 * Storage32InternalImpl definitions.
70 * Definition of the implementation structure for the IStorage32 interface.
71 * This one implements the IStorage32 interface for storage that are
72 * inside another storage.
74 struct StorageInternalImpl
76 struct StorageBaseImpl base;
78 * There is no specific data for this class.
81 typedef struct StorageInternalImpl StorageInternalImpl;
83 /* Method definitions for the Storage32InternalImpl class. */
84 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
85 DWORD openFlags, ULONG rootTropertyIndex);
86 static void StorageImpl_Destroy(StorageBaseImpl* iface);
87 static void* StorageImpl_GetBigBlock(StorageImpl* This, ULONG blockIndex);
88 static void* StorageImpl_GetROBigBlock(StorageImpl* This, ULONG blockIndex);
89 static void StorageImpl_ReleaseBigBlock(StorageImpl* This, void* pBigBlock);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
92 static void StorageImpl_SetNextBlockInChain(StorageImpl* This, ULONG blockIndex, ULONG nextBlock);
93 static HRESULT StorageImpl_LoadFileHeader(StorageImpl* This);
94 static void StorageImpl_SaveFileHeader(StorageImpl* This);
96 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex);
97 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This);
98 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex);
99 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex);
100 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex);
102 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This);
103 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This);
104 static ULONG BlockChainStream_GetCount(BlockChainStream* This);
106 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This);
109 /* OLESTREAM memory structure to use for Get and Put Routines */
110 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
111 typedef struct
113 DWORD dwOleID;
114 DWORD dwTypeID;
115 DWORD dwOleTypeNameLength;
116 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
117 CHAR *pstrOleObjFileName;
118 DWORD dwOleObjFileNameLength;
119 DWORD dwMetaFileWidth;
120 DWORD dwMetaFileHeight;
121 CHAR strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
122 DWORD dwDataLength;
123 BYTE *pData;
124 }OLECONVERT_OLESTREAM_DATA;
126 /* CompObj Stream structure */
127 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
128 typedef struct
130 BYTE byUnknown1[12];
131 CLSID clsid;
132 DWORD dwCLSIDNameLength;
133 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
134 DWORD dwOleTypeNameLength;
135 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
136 DWORD dwProgIDNameLength;
137 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
138 BYTE byUnknown2[16];
139 }OLECONVERT_ISTORAGE_COMPOBJ;
142 /* Ole Presention Stream structure */
143 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
144 typedef struct
146 BYTE byUnknown1[28];
147 DWORD dwExtentX;
148 DWORD dwExtentY;
149 DWORD dwSize;
150 BYTE *pData;
151 }OLECONVERT_ISTORAGE_OLEPRES;
155 /***********************************************************************
156 * Forward declaration of internal functions used by the method DestroyElement
158 static HRESULT deleteStorageProperty(
159 StorageImpl *parentStorage,
160 ULONG foundPropertyIndexToDelete,
161 StgProperty propertyToDelete);
163 static HRESULT deleteStreamProperty(
164 StorageImpl *parentStorage,
165 ULONG foundPropertyIndexToDelete,
166 StgProperty propertyToDelete);
168 static HRESULT findPlaceholder(
169 StorageImpl *storage,
170 ULONG propertyIndexToStore,
171 ULONG storagePropertyIndex,
172 INT typeOfRelation);
174 static HRESULT adjustPropertyChain(
175 StorageImpl *This,
176 StgProperty propertyToDelete,
177 StgProperty parentProperty,
178 ULONG parentPropertyId,
179 INT typeOfRelation);
181 /***********************************************************************
182 * Declaration of the functions used to manipulate StgProperty
185 static ULONG getFreeProperty(
186 StorageImpl *storage);
188 static void updatePropertyChain(
189 StorageImpl *storage,
190 ULONG newPropertyIndex,
191 StgProperty newProperty);
193 static LONG propertyNameCmp(
194 const OLECHAR *newProperty,
195 const OLECHAR *currentProperty);
198 /***********************************************************************
199 * Declaration of miscellaneous functions...
201 static HRESULT validateSTGM(DWORD stgmValue);
203 static DWORD GetShareModeFromSTGM(DWORD stgm);
204 static DWORD GetAccessModeFromSTGM(DWORD stgm);
205 static DWORD GetCreationModeFromSTGM(DWORD stgm);
207 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
210 /****************************************************************************
211 * IEnumSTATSTGImpl definitions.
213 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
214 * This class allows iterating through the content of a storage and to find
215 * specific items inside it.
217 struct IEnumSTATSTGImpl
219 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
220 * since we want to cast this in an IEnumSTATSTG pointer */
222 LONG ref; /* Reference count */
223 StorageImpl* parentStorage; /* Reference to the parent storage */
224 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
227 * The current implementation of the IEnumSTATSTGImpl class uses a stack
228 * to walk the property sets to get the content of a storage. This stack
229 * is implemented by the following 3 data members
231 ULONG stackSize;
232 ULONG stackMaxSize;
233 ULONG* stackToVisit;
235 #define ENUMSTATSGT_SIZE_INCREMENT 10
239 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
240 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
241 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
242 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
243 static ULONG IEnumSTATSTGImpl_FindProperty(IEnumSTATSTGImpl* This, const OLECHAR* lpszPropName,
244 StgProperty* buffer);
245 static INT IEnumSTATSTGImpl_FindParentProperty(IEnumSTATSTGImpl *This, ULONG childProperty,
246 StgProperty *currentProperty, ULONG *propertyId);
249 /************************************************************************
250 ** Storage32BaseImpl implementatiion
253 /************************************************************************
254 * Storage32BaseImpl_QueryInterface (IUnknown)
256 * This method implements the common QueryInterface for all IStorage32
257 * implementations contained in this file.
259 * See Windows documentation for more details on IUnknown methods.
261 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
262 IStorage* iface,
263 REFIID riid,
264 void** ppvObject)
266 StorageBaseImpl *This = (StorageBaseImpl *)iface;
268 * Perform a sanity check on the parameters.
270 if ( (This==0) || (ppvObject==0) )
271 return E_INVALIDARG;
274 * Initialize the return parameter.
276 *ppvObject = 0;
279 * Compare the riid with the interface IDs implemented by this object.
281 if (IsEqualGUID(&IID_IUnknown, riid) ||
282 IsEqualGUID(&IID_IStorage, riid))
284 *ppvObject = (IStorage*)This;
286 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
288 *ppvObject = (IStorage*)&This->pssVtbl;
292 * Check that we obtained an interface.
294 if ((*ppvObject)==0)
295 return E_NOINTERFACE;
298 * Query Interface always increases the reference count by one when it is
299 * successful
301 IStorage_AddRef(iface);
303 return S_OK;
306 /************************************************************************
307 * Storage32BaseImpl_AddRef (IUnknown)
309 * This method implements the common AddRef for all IStorage32
310 * implementations contained in this file.
312 * See Windows documentation for more details on IUnknown methods.
314 static ULONG WINAPI StorageBaseImpl_AddRef(
315 IStorage* iface)
317 StorageBaseImpl *This = (StorageBaseImpl *)iface;
318 ULONG ref = InterlockedIncrement(&This->ref);
320 TRACE("(%p) AddRef to %d\n", This, ref);
322 return ref;
325 /************************************************************************
326 * Storage32BaseImpl_Release (IUnknown)
328 * This method implements the common Release for all IStorage32
329 * implementations contained in this file.
331 * See Windows documentation for more details on IUnknown methods.
333 static ULONG WINAPI StorageBaseImpl_Release(
334 IStorage* iface)
336 StorageBaseImpl *This = (StorageBaseImpl *)iface;
338 * Decrease the reference count on this object.
340 ULONG ref = InterlockedDecrement(&This->ref);
342 TRACE("(%p) ReleaseRef to %d\n", This, ref);
345 * If the reference count goes down to 0, perform suicide.
347 if (ref == 0)
350 * Since we are using a system of base-classes, we want to call the
351 * destructor of the appropriate derived class. To do this, we are
352 * using virtual functions to implement the destructor.
354 This->v_destructor(This);
357 return ref;
360 /************************************************************************
361 * Storage32BaseImpl_OpenStream (IStorage)
363 * This method will open the specified stream object from the current storage.
365 * See Windows documentation for more details on IStorage methods.
367 static HRESULT WINAPI StorageBaseImpl_OpenStream(
368 IStorage* iface,
369 const OLECHAR* pwcsName, /* [string][in] */
370 void* reserved1, /* [unique][in] */
371 DWORD grfMode, /* [in] */
372 DWORD reserved2, /* [in] */
373 IStream** ppstm) /* [out] */
375 StorageBaseImpl *This = (StorageBaseImpl *)iface;
376 IEnumSTATSTGImpl* propertyEnumeration;
377 StgStreamImpl* newStream;
378 StgProperty currentProperty;
379 ULONG foundPropertyIndex;
380 HRESULT res = STG_E_UNKNOWN;
382 TRACE("(%p, %s, %p, %x, %d, %p)\n",
383 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
386 * Perform a sanity check on the parameters.
388 if ( (pwcsName==NULL) || (ppstm==0) )
390 res = E_INVALIDARG;
391 goto end;
395 * Initialize the out parameter
397 *ppstm = NULL;
400 * Validate the STGM flags
402 if ( FAILED( validateSTGM(grfMode) ) ||
403 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
405 res = STG_E_INVALIDFLAG;
406 goto end;
410 * As documented.
412 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
414 res = STG_E_INVALIDFUNCTION;
415 goto end;
419 * Check that we're compatible with the parent's storage mode, but
420 * only if we are not in transacted mode
422 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
423 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
425 res = STG_E_ACCESSDENIED;
426 goto end;
431 * Create a property enumeration to search the properties
433 propertyEnumeration = IEnumSTATSTGImpl_Construct(
434 This->ancestorStorage,
435 This->rootPropertySetIndex);
438 * Search the enumeration for the property with the given name
440 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
441 propertyEnumeration,
442 pwcsName,
443 &currentProperty);
446 * Delete the property enumeration since we don't need it anymore
448 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
451 * If it was found, construct the stream object and return a pointer to it.
453 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
454 (currentProperty.propertyType==PROPTYPE_STREAM) )
456 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
458 if (newStream!=0)
460 newStream->grfMode = grfMode;
461 *ppstm = (IStream*)newStream;
464 * Since we are returning a pointer to the interface, we have to
465 * nail down the reference.
467 IStream_AddRef(*ppstm);
469 res = S_OK;
470 goto end;
473 res = E_OUTOFMEMORY;
474 goto end;
477 res = STG_E_FILENOTFOUND;
479 end:
480 if (res == S_OK)
481 TRACE("<-- IStream %p\n", *ppstm);
482 TRACE("<-- %08x\n", res);
483 return res;
486 /************************************************************************
487 * Storage32BaseImpl_OpenStorage (IStorage)
489 * This method will open a new storage object from the current storage.
491 * See Windows documentation for more details on IStorage methods.
493 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
494 IStorage* iface,
495 const OLECHAR* pwcsName, /* [string][unique][in] */
496 IStorage* pstgPriority, /* [unique][in] */
497 DWORD grfMode, /* [in] */
498 SNB snbExclude, /* [unique][in] */
499 DWORD reserved, /* [in] */
500 IStorage** ppstg) /* [out] */
502 StorageBaseImpl *This = (StorageBaseImpl *)iface;
503 StorageInternalImpl* newStorage;
504 IEnumSTATSTGImpl* propertyEnumeration;
505 StgProperty currentProperty;
506 ULONG foundPropertyIndex;
507 HRESULT res = STG_E_UNKNOWN;
509 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
510 iface, debugstr_w(pwcsName), pstgPriority,
511 grfMode, snbExclude, reserved, ppstg);
514 * Perform a sanity check on the parameters.
516 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
518 res = E_INVALIDARG;
519 goto end;
522 /* as documented */
523 if (snbExclude != NULL)
525 res = STG_E_INVALIDPARAMETER;
526 goto end;
530 * Validate the STGM flags
532 if ( FAILED( validateSTGM(grfMode) ))
534 res = STG_E_INVALIDFLAG;
535 goto end;
539 * As documented.
541 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
542 (grfMode & STGM_DELETEONRELEASE) ||
543 (grfMode & STGM_PRIORITY) )
545 res = STG_E_INVALIDFUNCTION;
546 goto end;
550 * Check that we're compatible with the parent's storage mode,
551 * but only if we are not transacted
553 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
554 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
556 res = STG_E_ACCESSDENIED;
557 goto end;
562 * Initialize the out parameter
564 *ppstg = NULL;
567 * Create a property enumeration to search the properties
569 propertyEnumeration = IEnumSTATSTGImpl_Construct(
570 This->ancestorStorage,
571 This->rootPropertySetIndex);
574 * Search the enumeration for the property with the given name
576 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
577 propertyEnumeration,
578 pwcsName,
579 &currentProperty);
582 * Delete the property enumeration since we don't need it anymore
584 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
587 * If it was found, construct the stream object and return a pointer to it.
589 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
590 (currentProperty.propertyType==PROPTYPE_STORAGE) )
593 * Construct a new Storage object
595 newStorage = StorageInternalImpl_Construct(
596 This->ancestorStorage,
597 grfMode,
598 foundPropertyIndex);
600 if (newStorage != 0)
602 *ppstg = (IStorage*)newStorage;
605 * Since we are returning a pointer to the interface,
606 * we have to nail down the reference.
608 StorageBaseImpl_AddRef(*ppstg);
610 res = S_OK;
611 goto end;
614 res = STG_E_INSUFFICIENTMEMORY;
615 goto end;
618 res = STG_E_FILENOTFOUND;
620 end:
621 TRACE("<-- %08x\n", res);
622 return res;
625 /************************************************************************
626 * Storage32BaseImpl_EnumElements (IStorage)
628 * This method will create an enumerator object that can be used to
629 * retrieve informatino about all the properties in the storage object.
631 * See Windows documentation for more details on IStorage methods.
633 static HRESULT WINAPI StorageBaseImpl_EnumElements(
634 IStorage* iface,
635 DWORD reserved1, /* [in] */
636 void* reserved2, /* [size_is][unique][in] */
637 DWORD reserved3, /* [in] */
638 IEnumSTATSTG** ppenum) /* [out] */
640 StorageBaseImpl *This = (StorageBaseImpl *)iface;
641 IEnumSTATSTGImpl* newEnum;
643 TRACE("(%p, %d, %p, %d, %p)\n",
644 iface, reserved1, reserved2, reserved3, ppenum);
647 * Perform a sanity check on the parameters.
649 if ( (This==0) || (ppenum==0))
650 return E_INVALIDARG;
653 * Construct the enumerator.
655 newEnum = IEnumSTATSTGImpl_Construct(
656 This->ancestorStorage,
657 This->rootPropertySetIndex);
659 if (newEnum!=0)
661 *ppenum = (IEnumSTATSTG*)newEnum;
664 * Don't forget to nail down a reference to the new object before
665 * returning it.
667 IEnumSTATSTG_AddRef(*ppenum);
669 return S_OK;
672 return E_OUTOFMEMORY;
675 /************************************************************************
676 * Storage32BaseImpl_Stat (IStorage)
678 * This method will retrieve information about this storage object.
680 * See Windows documentation for more details on IStorage methods.
682 static HRESULT WINAPI StorageBaseImpl_Stat(
683 IStorage* iface,
684 STATSTG* pstatstg, /* [out] */
685 DWORD grfStatFlag) /* [in] */
687 StorageBaseImpl *This = (StorageBaseImpl *)iface;
688 StgProperty curProperty;
689 BOOL readSuccessful;
690 HRESULT res = STG_E_UNKNOWN;
692 TRACE("(%p, %p, %x)\n",
693 iface, pstatstg, grfStatFlag);
696 * Perform a sanity check on the parameters.
698 if ( (This==0) || (pstatstg==0))
700 res = E_INVALIDARG;
701 goto end;
705 * Read the information from the property.
707 readSuccessful = StorageImpl_ReadProperty(
708 This->ancestorStorage,
709 This->rootPropertySetIndex,
710 &curProperty);
712 if (readSuccessful)
714 StorageUtl_CopyPropertyToSTATSTG(
715 pstatstg,
716 &curProperty,
717 grfStatFlag);
719 pstatstg->grfMode = This->openFlags;
721 res = S_OK;
722 goto end;
725 res = E_FAIL;
727 end:
728 if (res == S_OK)
730 TRACE("<-- STATSTG: pwcsName: %s, type: %d, cbSize.Low/High: %d/%d, grfMode: %08x, grfLocksSupported: %d, grfStateBits: %08x\n", debugstr_w(pstatstg->pwcsName), pstatstg->type, pstatstg->cbSize.u.LowPart, pstatstg->cbSize.u.HighPart, pstatstg->grfMode, pstatstg->grfLocksSupported, pstatstg->grfStateBits);
732 TRACE("<-- %08x\n", res);
733 return res;
736 /************************************************************************
737 * Storage32BaseImpl_RenameElement (IStorage)
739 * This method will rename the specified element.
741 * See Windows documentation for more details on IStorage methods.
743 * Implementation notes: The method used to rename consists of creating a clone
744 * of the deleted StgProperty object setting it with the new name and to
745 * perform a DestroyElement of the old StgProperty.
747 static HRESULT WINAPI StorageBaseImpl_RenameElement(
748 IStorage* iface,
749 const OLECHAR* pwcsOldName, /* [in] */
750 const OLECHAR* pwcsNewName) /* [in] */
752 StorageBaseImpl *This = (StorageBaseImpl *)iface;
753 IEnumSTATSTGImpl* propertyEnumeration;
754 StgProperty currentProperty;
755 ULONG foundPropertyIndex;
757 TRACE("(%p, %s, %s)\n",
758 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
761 * Create a property enumeration to search the properties
763 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
764 This->rootPropertySetIndex);
767 * Search the enumeration for the new property name
769 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
770 pwcsNewName,
771 &currentProperty);
773 if (foundPropertyIndex != PROPERTY_NULL)
776 * There is already a property with the new name
778 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
779 return STG_E_FILEALREADYEXISTS;
782 IEnumSTATSTG_Reset((IEnumSTATSTG*)propertyEnumeration);
785 * Search the enumeration for the old property name
787 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
788 pwcsOldName,
789 &currentProperty);
792 * Delete the property enumeration since we don't need it anymore
794 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
796 if (foundPropertyIndex != PROPERTY_NULL)
798 StgProperty renamedProperty;
799 ULONG renamedPropertyIndex;
802 * Setup a new property for the renamed property
804 renamedProperty.sizeOfNameString =
805 ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
807 if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
808 return STG_E_INVALIDNAME;
810 strcpyW(renamedProperty.name, pwcsNewName);
812 renamedProperty.propertyType = currentProperty.propertyType;
813 renamedProperty.startingBlock = currentProperty.startingBlock;
814 renamedProperty.size.u.LowPart = currentProperty.size.u.LowPart;
815 renamedProperty.size.u.HighPart = currentProperty.size.u.HighPart;
817 renamedProperty.previousProperty = PROPERTY_NULL;
818 renamedProperty.nextProperty = PROPERTY_NULL;
821 * Bring the dirProperty link in case it is a storage and in which
822 * case the renamed storage elements don't require to be reorganized.
824 renamedProperty.dirProperty = currentProperty.dirProperty;
826 /* call CoFileTime to get the current time
827 renamedProperty.timeStampS1
828 renamedProperty.timeStampD1
829 renamedProperty.timeStampS2
830 renamedProperty.timeStampD2
831 renamedProperty.propertyUniqueID
835 * Obtain a free property in the property chain
837 renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
840 * Save the new property into the new property spot
842 StorageImpl_WriteProperty(
843 This->ancestorStorage,
844 renamedPropertyIndex,
845 &renamedProperty);
848 * Find a spot in the property chain for our newly created property.
850 updatePropertyChain(
851 (StorageImpl*)This,
852 renamedPropertyIndex,
853 renamedProperty);
856 * At this point the renamed property has been inserted in the tree,
857 * now, before Destroying the old property we must zero its dirProperty
858 * otherwise the DestroyProperty below will zap it all and we do not want
859 * this to happen.
860 * Also, we fake that the old property is a storage so the DestroyProperty
861 * will not do a SetSize(0) on the stream data.
863 * This means that we need to tweak the StgProperty if it is a stream or a
864 * non empty storage.
866 StorageImpl_ReadProperty(This->ancestorStorage,
867 foundPropertyIndex,
868 &currentProperty);
870 currentProperty.dirProperty = PROPERTY_NULL;
871 currentProperty.propertyType = PROPTYPE_STORAGE;
872 StorageImpl_WriteProperty(
873 This->ancestorStorage,
874 foundPropertyIndex,
875 &currentProperty);
878 * Invoke Destroy to get rid of the ole property and automatically redo
879 * the linking of its previous and next members...
881 IStorage_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName);
884 else
887 * There is no property with the old name
889 return STG_E_FILENOTFOUND;
892 return S_OK;
895 /************************************************************************
896 * Storage32BaseImpl_CreateStream (IStorage)
898 * This method will create a stream object within this storage
900 * See Windows documentation for more details on IStorage methods.
902 static HRESULT WINAPI StorageBaseImpl_CreateStream(
903 IStorage* iface,
904 const OLECHAR* pwcsName, /* [string][in] */
905 DWORD grfMode, /* [in] */
906 DWORD reserved1, /* [in] */
907 DWORD reserved2, /* [in] */
908 IStream** ppstm) /* [out] */
910 StorageBaseImpl *This = (StorageBaseImpl *)iface;
911 IEnumSTATSTGImpl* propertyEnumeration;
912 StgStreamImpl* newStream;
913 StgProperty currentProperty, newStreamProperty;
914 ULONG foundPropertyIndex, newPropertyIndex;
916 TRACE("(%p, %s, %x, %d, %d, %p)\n",
917 iface, debugstr_w(pwcsName), grfMode,
918 reserved1, reserved2, ppstm);
921 * Validate parameters
923 if (ppstm == 0)
924 return STG_E_INVALIDPOINTER;
926 if (pwcsName == 0)
927 return STG_E_INVALIDNAME;
929 if (reserved1 || reserved2)
930 return STG_E_INVALIDPARAMETER;
933 * Validate the STGM flags
935 if ( FAILED( validateSTGM(grfMode) ))
936 return STG_E_INVALIDFLAG;
938 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
939 return STG_E_INVALIDFLAG;
942 * As documented.
944 if ((grfMode & STGM_DELETEONRELEASE) ||
945 (grfMode & STGM_TRANSACTED))
946 return STG_E_INVALIDFUNCTION;
949 * Check that we're compatible with the parent's storage mode
950 * if not in transacted mode
952 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
953 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
954 return STG_E_ACCESSDENIED;
958 * Initialize the out parameter
960 *ppstm = 0;
963 * Create a property enumeration to search the properties
965 propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
966 This->rootPropertySetIndex);
968 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
969 pwcsName,
970 &currentProperty);
972 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
974 if (foundPropertyIndex != PROPERTY_NULL)
977 * An element with this name already exists
979 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
981 IStorage_DestroyElement(iface, pwcsName);
983 else
984 return STG_E_FILEALREADYEXISTS;
986 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
988 WARN("read-only storage\n");
989 return STG_E_ACCESSDENIED;
993 * memset the empty property
995 memset(&newStreamProperty, 0, sizeof(StgProperty));
997 newStreamProperty.sizeOfNameString =
998 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
1000 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1001 return STG_E_INVALIDNAME;
1003 strcpyW(newStreamProperty.name, pwcsName);
1005 newStreamProperty.propertyType = PROPTYPE_STREAM;
1006 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
1007 newStreamProperty.size.u.LowPart = 0;
1008 newStreamProperty.size.u.HighPart = 0;
1010 newStreamProperty.previousProperty = PROPERTY_NULL;
1011 newStreamProperty.nextProperty = PROPERTY_NULL;
1012 newStreamProperty.dirProperty = PROPERTY_NULL;
1014 /* call CoFileTime to get the current time
1015 newStreamProperty.timeStampS1
1016 newStreamProperty.timeStampD1
1017 newStreamProperty.timeStampS2
1018 newStreamProperty.timeStampD2
1021 /* newStreamProperty.propertyUniqueID */
1024 * Get a free property or create a new one
1026 newPropertyIndex = getFreeProperty(This->ancestorStorage);
1029 * Save the new property into the new property spot
1031 StorageImpl_WriteProperty(
1032 This->ancestorStorage,
1033 newPropertyIndex,
1034 &newStreamProperty);
1037 * Find a spot in the property chain for our newly created property.
1039 updatePropertyChain(
1040 (StorageImpl*)This,
1041 newPropertyIndex,
1042 newStreamProperty);
1045 * Open the stream to return it.
1047 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
1049 if (newStream != 0)
1051 *ppstm = (IStream*)newStream;
1054 * Since we are returning a pointer to the interface, we have to nail down
1055 * the reference.
1057 IStream_AddRef(*ppstm);
1059 else
1061 return STG_E_INSUFFICIENTMEMORY;
1064 return S_OK;
1067 /************************************************************************
1068 * Storage32BaseImpl_SetClass (IStorage)
1070 * This method will write the specified CLSID in the property of this
1071 * storage.
1073 * See Windows documentation for more details on IStorage methods.
1075 static HRESULT WINAPI StorageBaseImpl_SetClass(
1076 IStorage* iface,
1077 REFCLSID clsid) /* [in] */
1079 StorageBaseImpl *This = (StorageBaseImpl *)iface;
1080 HRESULT hRes = E_FAIL;
1081 StgProperty curProperty;
1082 BOOL success;
1084 TRACE("(%p, %p)\n", iface, clsid);
1086 success = StorageImpl_ReadProperty(This->ancestorStorage,
1087 This->rootPropertySetIndex,
1088 &curProperty);
1089 if (success)
1091 curProperty.propertyUniqueID = *clsid;
1093 success = StorageImpl_WriteProperty(This->ancestorStorage,
1094 This->rootPropertySetIndex,
1095 &curProperty);
1096 if (success)
1097 hRes = S_OK;
1100 return hRes;
1103 /************************************************************************
1104 ** Storage32Impl implementation
1107 /************************************************************************
1108 * Storage32Impl_CreateStorage (IStorage)
1110 * This method will create the storage object within the provided storage.
1112 * See Windows documentation for more details on IStorage methods.
1114 static HRESULT WINAPI StorageImpl_CreateStorage(
1115 IStorage* iface,
1116 const OLECHAR *pwcsName, /* [string][in] */
1117 DWORD grfMode, /* [in] */
1118 DWORD reserved1, /* [in] */
1119 DWORD reserved2, /* [in] */
1120 IStorage **ppstg) /* [out] */
1122 StorageImpl* const This=(StorageImpl*)iface;
1124 IEnumSTATSTGImpl *propertyEnumeration;
1125 StgProperty currentProperty;
1126 StgProperty newProperty;
1127 ULONG foundPropertyIndex;
1128 ULONG newPropertyIndex;
1129 HRESULT hr;
1131 TRACE("(%p, %s, %x, %d, %d, %p)\n",
1132 iface, debugstr_w(pwcsName), grfMode,
1133 reserved1, reserved2, ppstg);
1136 * Validate parameters
1138 if (ppstg == 0)
1139 return STG_E_INVALIDPOINTER;
1141 if (pwcsName == 0)
1142 return STG_E_INVALIDNAME;
1145 * Initialize the out parameter
1147 *ppstg = NULL;
1150 * Validate the STGM flags
1152 if ( FAILED( validateSTGM(grfMode) ) ||
1153 (grfMode & STGM_DELETEONRELEASE) )
1155 WARN("bad grfMode: 0x%x\n", grfMode);
1156 return STG_E_INVALIDFLAG;
1160 * Check that we're compatible with the parent's storage mode
1162 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->base.openFlags ) )
1164 WARN("access denied\n");
1165 return STG_E_ACCESSDENIED;
1169 * Create a property enumeration and search the properties
1171 propertyEnumeration = IEnumSTATSTGImpl_Construct( This->base.ancestorStorage,
1172 This->base.rootPropertySetIndex);
1174 foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1175 pwcsName,
1176 &currentProperty);
1177 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1179 if (foundPropertyIndex != PROPERTY_NULL)
1182 * An element with this name already exists
1184 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
1185 IStorage_DestroyElement(iface, pwcsName);
1186 else
1188 WARN("file already exists\n");
1189 return STG_E_FILEALREADYEXISTS;
1192 else if (STGM_ACCESS_MODE(This->base.openFlags) == STGM_READ)
1194 WARN("read-only storage\n");
1195 return STG_E_ACCESSDENIED;
1199 * memset the empty property
1201 memset(&newProperty, 0, sizeof(StgProperty));
1203 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1205 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1207 FIXME("name too long\n");
1208 return STG_E_INVALIDNAME;
1211 strcpyW(newProperty.name, pwcsName);
1213 newProperty.propertyType = PROPTYPE_STORAGE;
1214 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1215 newProperty.size.u.LowPart = 0;
1216 newProperty.size.u.HighPart = 0;
1218 newProperty.previousProperty = PROPERTY_NULL;
1219 newProperty.nextProperty = PROPERTY_NULL;
1220 newProperty.dirProperty = PROPERTY_NULL;
1222 /* call CoFileTime to get the current time
1223 newProperty.timeStampS1
1224 newProperty.timeStampD1
1225 newProperty.timeStampS2
1226 newProperty.timeStampD2
1229 /* newStorageProperty.propertyUniqueID */
1232 * Obtain a free property in the property chain
1234 newPropertyIndex = getFreeProperty(This->base.ancestorStorage);
1237 * Save the new property into the new property spot
1239 StorageImpl_WriteProperty(
1240 This->base.ancestorStorage,
1241 newPropertyIndex,
1242 &newProperty);
1245 * Find a spot in the property chain for our newly created property.
1247 updatePropertyChain(
1248 This,
1249 newPropertyIndex,
1250 newProperty);
1253 * Open it to get a pointer to return.
1255 hr = IStorage_OpenStorage(
1256 iface,
1257 (const OLECHAR*)pwcsName,
1259 grfMode,
1262 ppstg);
1264 if( (hr != S_OK) || (*ppstg == NULL))
1266 return hr;
1270 return S_OK;
1274 /***************************************************************************
1276 * Internal Method
1278 * Get a free property or create a new one.
1280 static ULONG getFreeProperty(
1281 StorageImpl *storage)
1283 ULONG currentPropertyIndex = 0;
1284 ULONG newPropertyIndex = PROPERTY_NULL;
1285 BOOL readSuccessful = TRUE;
1286 StgProperty currentProperty;
1291 * Start by reading the root property
1293 readSuccessful = StorageImpl_ReadProperty(storage->base.ancestorStorage,
1294 currentPropertyIndex,
1295 &currentProperty);
1296 if (readSuccessful)
1298 if (currentProperty.sizeOfNameString == 0)
1301 * The property existis and is available, we found it.
1303 newPropertyIndex = currentPropertyIndex;
1306 else
1309 * We exhausted the property list, we will create more space below
1311 newPropertyIndex = currentPropertyIndex;
1313 currentPropertyIndex++;
1315 } while (newPropertyIndex == PROPERTY_NULL);
1318 * grow the property chain
1320 if (! readSuccessful)
1322 StgProperty emptyProperty;
1323 ULARGE_INTEGER newSize;
1324 ULONG propertyIndex;
1325 ULONG lastProperty = 0;
1326 ULONG blockCount = 0;
1329 * obtain the new count of property blocks
1331 blockCount = BlockChainStream_GetCount(
1332 storage->base.ancestorStorage->rootBlockChain)+1;
1335 * initialize the size used by the property stream
1337 newSize.u.HighPart = 0;
1338 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1341 * add a property block to the property chain
1343 BlockChainStream_SetSize(storage->base.ancestorStorage->rootBlockChain, newSize);
1346 * memset the empty property in order to initialize the unused newly
1347 * created property
1349 memset(&emptyProperty, 0, sizeof(StgProperty));
1352 * initialize them
1354 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1356 for(
1357 propertyIndex = newPropertyIndex;
1358 propertyIndex < lastProperty;
1359 propertyIndex++)
1361 StorageImpl_WriteProperty(
1362 storage->base.ancestorStorage,
1363 propertyIndex,
1364 &emptyProperty);
1368 return newPropertyIndex;
1371 /****************************************************************************
1373 * Internal Method
1375 * Case insensitive comparaison of StgProperty.name by first considering
1376 * their size.
1378 * Returns <0 when newPrpoerty < currentProperty
1379 * >0 when newPrpoerty > currentProperty
1380 * 0 when newPrpoerty == currentProperty
1382 static LONG propertyNameCmp(
1383 const OLECHAR *newProperty,
1384 const OLECHAR *currentProperty)
1386 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1388 if (diff == 0)
1391 * We compare the string themselves only when they are of the same length
1393 diff = lstrcmpiW( newProperty, currentProperty);
1396 return diff;
1399 /****************************************************************************
1401 * Internal Method
1403 * Properly link this new element in the property chain.
1405 static void updatePropertyChain(
1406 StorageImpl *storage,
1407 ULONG newPropertyIndex,
1408 StgProperty newProperty)
1410 StgProperty currentProperty;
1413 * Read the root property
1415 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1416 storage->base.rootPropertySetIndex,
1417 &currentProperty);
1419 if (currentProperty.dirProperty != PROPERTY_NULL)
1422 * The root storage contains some element, therefore, start the research
1423 * for the appropriate location.
1425 BOOL found = 0;
1426 ULONG current, next, previous, currentPropertyId;
1429 * Keep the StgProperty sequence number of the storage first property
1431 currentPropertyId = currentProperty.dirProperty;
1434 * Read
1436 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1437 currentProperty.dirProperty,
1438 &currentProperty);
1440 previous = currentProperty.previousProperty;
1441 next = currentProperty.nextProperty;
1442 current = currentPropertyId;
1444 while (found == 0)
1446 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1448 if (diff < 0)
1450 if (previous != PROPERTY_NULL)
1452 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1453 previous,
1454 &currentProperty);
1455 current = previous;
1457 else
1459 currentProperty.previousProperty = newPropertyIndex;
1460 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1461 current,
1462 &currentProperty);
1463 found = 1;
1466 else if (diff > 0)
1468 if (next != PROPERTY_NULL)
1470 StorageImpl_ReadProperty(storage->base.ancestorStorage,
1471 next,
1472 &currentProperty);
1473 current = next;
1475 else
1477 currentProperty.nextProperty = newPropertyIndex;
1478 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1479 current,
1480 &currentProperty);
1481 found = 1;
1484 else
1487 * Trying to insert an item with the same name in the
1488 * subtree structure.
1490 assert(FALSE);
1493 previous = currentProperty.previousProperty;
1494 next = currentProperty.nextProperty;
1497 else
1500 * The root storage is empty, link the new property to its dir property
1502 currentProperty.dirProperty = newPropertyIndex;
1503 StorageImpl_WriteProperty(storage->base.ancestorStorage,
1504 storage->base.rootPropertySetIndex,
1505 &currentProperty);
1510 /*************************************************************************
1511 * CopyTo (IStorage)
1513 static HRESULT WINAPI StorageImpl_CopyTo(
1514 IStorage* iface,
1515 DWORD ciidExclude, /* [in] */
1516 const IID* rgiidExclude, /* [size_is][unique][in] */
1517 SNB snbExclude, /* [unique][in] */
1518 IStorage* pstgDest) /* [unique][in] */
1520 IEnumSTATSTG *elements = 0;
1521 STATSTG curElement, strStat;
1522 HRESULT hr;
1523 IStorage *pstgTmp, *pstgChild;
1524 IStream *pstrTmp, *pstrChild;
1526 if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1527 FIXME("Exclude option not implemented\n");
1529 TRACE("(%p, %d, %p, %p, %p)\n",
1530 iface, ciidExclude, rgiidExclude,
1531 snbExclude, pstgDest);
1534 * Perform a sanity check
1536 if ( pstgDest == 0 )
1537 return STG_E_INVALIDPOINTER;
1540 * Enumerate the elements
1542 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1544 if ( hr != S_OK )
1545 return hr;
1548 * set the class ID
1550 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1551 IStorage_SetClass( pstgDest, &curElement.clsid );
1556 * Obtain the next element
1558 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1560 if ( hr == S_FALSE )
1562 hr = S_OK; /* done, every element has been copied */
1563 break;
1566 if (curElement.type == STGTY_STORAGE)
1569 * open child source storage
1571 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1572 STGM_READ|STGM_SHARE_EXCLUSIVE,
1573 NULL, 0, &pstgChild );
1575 if (hr != S_OK)
1576 break;
1579 * Check if destination storage is not a child of the source
1580 * storage, which will cause an infinite loop
1582 if (pstgChild == pstgDest)
1584 IEnumSTATSTG_Release(elements);
1586 return STG_E_ACCESSDENIED;
1590 * create a new storage in destination storage
1592 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1593 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1594 0, 0,
1595 &pstgTmp );
1597 * if it already exist, don't create a new one use this one
1599 if (hr == STG_E_FILEALREADYEXISTS)
1601 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1602 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1603 NULL, 0, &pstgTmp );
1606 if (hr != S_OK)
1607 break;
1611 * do the copy recursively
1613 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1614 snbExclude, pstgTmp );
1616 IStorage_Release( pstgTmp );
1617 IStorage_Release( pstgChild );
1619 else if (curElement.type == STGTY_STREAM)
1622 * create a new stream in destination storage. If the stream already
1623 * exist, it will be deleted and a new one will be created.
1625 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1626 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1627 0, 0, &pstrTmp );
1629 if (hr != S_OK)
1630 break;
1633 * open child stream storage
1635 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1636 STGM_READ|STGM_SHARE_EXCLUSIVE,
1637 0, &pstrChild );
1639 if (hr != S_OK)
1640 break;
1643 * Get the size of the source stream
1645 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1648 * Set the size of the destination stream.
1650 IStream_SetSize(pstrTmp, strStat.cbSize);
1653 * do the copy
1655 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1656 NULL, NULL );
1658 IStream_Release( pstrTmp );
1659 IStream_Release( pstrChild );
1661 else
1663 WARN("unknown element type: %d\n", curElement.type);
1666 } while (hr == S_OK);
1669 * Clean-up
1671 IEnumSTATSTG_Release(elements);
1673 return hr;
1676 /*************************************************************************
1677 * MoveElementTo (IStorage)
1679 static HRESULT WINAPI StorageImpl_MoveElementTo(
1680 IStorage* iface,
1681 const OLECHAR *pwcsName, /* [string][in] */
1682 IStorage *pstgDest, /* [unique][in] */
1683 const OLECHAR *pwcsNewName,/* [string][in] */
1684 DWORD grfFlags) /* [in] */
1686 FIXME("(%p %s %p %s %u): stub\n", iface,
1687 debugstr_w(pwcsName), pstgDest,
1688 debugstr_w(pwcsNewName), grfFlags);
1689 return E_NOTIMPL;
1692 /*************************************************************************
1693 * Commit (IStorage)
1695 * Ensures that any changes made to a storage object open in transacted mode
1696 * are reflected in the parent storage
1698 * NOTES
1699 * Wine doesn't implement transacted mode, which seems to be a basic
1700 * optimization, so we can ignore this stub for now.
1702 static HRESULT WINAPI StorageImpl_Commit(
1703 IStorage* iface,
1704 DWORD grfCommitFlags)/* [in] */
1706 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1707 return S_OK;
1710 /*************************************************************************
1711 * Revert (IStorage)
1713 * Discard all changes that have been made since the last commit operation
1715 static HRESULT WINAPI StorageImpl_Revert(
1716 IStorage* iface)
1718 FIXME("(%p): stub\n", iface);
1719 return E_NOTIMPL;
1722 /*************************************************************************
1723 * DestroyElement (IStorage)
1725 * Strategy: This implementation is built this way for simplicity not for speed.
1726 * I always delete the topmost element of the enumeration and adjust
1727 * the deleted element pointer all the time. This takes longer to
1728 * do but allow to reinvoke DestroyElement whenever we encounter a
1729 * storage object. The optimisation resides in the usage of another
1730 * enumeration strategy that would give all the leaves of a storage
1731 * first. (postfix order)
1733 static HRESULT WINAPI StorageImpl_DestroyElement(
1734 IStorage* iface,
1735 const OLECHAR *pwcsName)/* [string][in] */
1737 StorageImpl* const This=(StorageImpl*)iface;
1739 IEnumSTATSTGImpl* propertyEnumeration;
1740 HRESULT hr = S_OK;
1741 BOOL res;
1742 StgProperty propertyToDelete;
1743 StgProperty parentProperty;
1744 ULONG foundPropertyIndexToDelete;
1745 ULONG typeOfRelation;
1746 ULONG parentPropertyId = 0;
1748 TRACE("(%p, %s)\n",
1749 iface, debugstr_w(pwcsName));
1752 * Perform a sanity check on the parameters.
1754 if (pwcsName==NULL)
1755 return STG_E_INVALIDPOINTER;
1758 * Create a property enumeration to search the property with the given name
1760 propertyEnumeration = IEnumSTATSTGImpl_Construct(
1761 This->base.ancestorStorage,
1762 This->base.rootPropertySetIndex);
1764 foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1765 propertyEnumeration,
1766 pwcsName,
1767 &propertyToDelete);
1769 IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1771 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1773 return STG_E_FILENOTFOUND;
1777 * Find the parent property of the property to delete (the one that
1778 * link to it). If This->dirProperty == foundPropertyIndexToDelete,
1779 * the parent is This. Otherwise, the parent is one of its sibling...
1783 * First, read This's StgProperty..
1785 res = StorageImpl_ReadProperty(
1786 This->base.ancestorStorage,
1787 This->base.rootPropertySetIndex,
1788 &parentProperty);
1790 assert(res);
1793 * Second, check to see if by any chance the actual storage (This) is not
1794 * the parent of the property to delete... We never know...
1796 if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1799 * Set data as it would have been done in the else part...
1801 typeOfRelation = PROPERTY_RELATION_DIR;
1802 parentPropertyId = This->base.rootPropertySetIndex;
1804 else
1807 * Create a property enumeration to search the parent properties, and
1808 * delete it once done.
1810 IEnumSTATSTGImpl* propertyEnumeration2;
1812 propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1813 This->base.ancestorStorage,
1814 This->base.rootPropertySetIndex);
1816 typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1817 propertyEnumeration2,
1818 foundPropertyIndexToDelete,
1819 &parentProperty,
1820 &parentPropertyId);
1822 IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1825 if ( propertyToDelete.propertyType == PROPTYPE_STORAGE )
1827 hr = deleteStorageProperty(
1828 This,
1829 foundPropertyIndexToDelete,
1830 propertyToDelete);
1832 else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1834 hr = deleteStreamProperty(
1835 This,
1836 foundPropertyIndexToDelete,
1837 propertyToDelete);
1840 if (hr!=S_OK)
1841 return hr;
1844 * Adjust the property chain
1846 hr = adjustPropertyChain(
1847 This,
1848 propertyToDelete,
1849 parentProperty,
1850 parentPropertyId,
1851 typeOfRelation);
1853 return hr;
1857 /************************************************************************
1858 * StorageImpl_Stat (IStorage)
1860 * This method will retrieve information about this storage object.
1862 * See Windows documentation for more details on IStorage methods.
1864 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1865 STATSTG* pstatstg, /* [out] */
1866 DWORD grfStatFlag) /* [in] */
1868 StorageImpl* const This = (StorageImpl*)iface;
1869 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1871 if ( !FAILED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1873 CoTaskMemFree(pstatstg->pwcsName);
1874 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1875 strcpyW(pstatstg->pwcsName, This->pwcsName);
1878 return result;
1881 /******************************************************************************
1882 * Internal stream list handlers
1885 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1887 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1888 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1891 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1893 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1894 list_remove(&(strm->StrmListEntry));
1897 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1899 struct list *cur, *cur2;
1900 StgStreamImpl *strm=NULL;
1902 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1903 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1904 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1905 strm->parentStorage = NULL;
1906 list_remove(cur);
1911 /*********************************************************************
1913 * Internal Method
1915 * Perform the deletion of a complete storage node
1918 static HRESULT deleteStorageProperty(
1919 StorageImpl *parentStorage,
1920 ULONG indexOfPropertyToDelete,
1921 StgProperty propertyToDelete)
1923 IEnumSTATSTG *elements = 0;
1924 IStorage *childStorage = 0;
1925 STATSTG currentElement;
1926 HRESULT hr;
1927 HRESULT destroyHr = S_OK;
1930 * Open the storage and enumerate it
1932 hr = StorageBaseImpl_OpenStorage(
1933 (IStorage*)parentStorage,
1934 propertyToDelete.name,
1936 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1939 &childStorage);
1941 if (hr != S_OK)
1943 return hr;
1947 * Enumerate the elements
1949 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1954 * Obtain the next element
1956 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1957 if (hr==S_OK)
1959 destroyHr = StorageImpl_DestroyElement(
1960 (IStorage*)childStorage,
1961 (OLECHAR*)currentElement.pwcsName);
1963 CoTaskMemFree(currentElement.pwcsName);
1967 * We need to Reset the enumeration every time because we delete elements
1968 * and the enumeration could be invalid
1970 IEnumSTATSTG_Reset(elements);
1972 } while ((hr == S_OK) && (destroyHr == S_OK));
1975 * Invalidate the property by zeroing its name member.
1977 propertyToDelete.sizeOfNameString = 0;
1979 StorageImpl_WriteProperty(parentStorage->base.ancestorStorage,
1980 indexOfPropertyToDelete,
1981 &propertyToDelete);
1983 IStorage_Release(childStorage);
1984 IEnumSTATSTG_Release(elements);
1986 return destroyHr;
1989 /*********************************************************************
1991 * Internal Method
1993 * Perform the deletion of a stream node
1996 static HRESULT deleteStreamProperty(
1997 StorageImpl *parentStorage,
1998 ULONG indexOfPropertyToDelete,
1999 StgProperty propertyToDelete)
2001 IStream *pis;
2002 HRESULT hr;
2003 ULARGE_INTEGER size;
2005 size.u.HighPart = 0;
2006 size.u.LowPart = 0;
2008 hr = StorageBaseImpl_OpenStream(
2009 (IStorage*)parentStorage,
2010 (OLECHAR*)propertyToDelete.name,
2011 NULL,
2012 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
2014 &pis);
2016 if (hr!=S_OK)
2018 return(hr);
2022 * Zap the stream
2024 hr = IStream_SetSize(pis, size);
2026 if(hr != S_OK)
2028 return hr;
2032 * Release the stream object.
2034 IStream_Release(pis);
2037 * Invalidate the property by zeroing its name member.
2039 propertyToDelete.sizeOfNameString = 0;
2042 * Here we should re-read the property so we get the updated pointer
2043 * but since we are here to zap it, I don't do it...
2045 StorageImpl_WriteProperty(
2046 parentStorage->base.ancestorStorage,
2047 indexOfPropertyToDelete,
2048 &propertyToDelete);
2050 return S_OK;
2053 /*********************************************************************
2055 * Internal Method
2057 * Finds a placeholder for the StgProperty within the Storage
2060 static HRESULT findPlaceholder(
2061 StorageImpl *storage,
2062 ULONG propertyIndexToStore,
2063 ULONG storePropertyIndex,
2064 INT typeOfRelation)
2066 StgProperty storeProperty;
2067 HRESULT hr = S_OK;
2068 BOOL res = TRUE;
2071 * Read the storage property
2073 res = StorageImpl_ReadProperty(
2074 storage->base.ancestorStorage,
2075 storePropertyIndex,
2076 &storeProperty);
2078 if(! res)
2080 return E_FAIL;
2083 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2085 if (storeProperty.previousProperty != PROPERTY_NULL)
2087 return findPlaceholder(
2088 storage,
2089 propertyIndexToStore,
2090 storeProperty.previousProperty,
2091 typeOfRelation);
2093 else
2095 storeProperty.previousProperty = propertyIndexToStore;
2098 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2100 if (storeProperty.nextProperty != PROPERTY_NULL)
2102 return findPlaceholder(
2103 storage,
2104 propertyIndexToStore,
2105 storeProperty.nextProperty,
2106 typeOfRelation);
2108 else
2110 storeProperty.nextProperty = propertyIndexToStore;
2113 else if (typeOfRelation == PROPERTY_RELATION_DIR)
2115 if (storeProperty.dirProperty != PROPERTY_NULL)
2117 return findPlaceholder(
2118 storage,
2119 propertyIndexToStore,
2120 storeProperty.dirProperty,
2121 typeOfRelation);
2123 else
2125 storeProperty.dirProperty = propertyIndexToStore;
2129 hr = StorageImpl_WriteProperty(
2130 storage->base.ancestorStorage,
2131 storePropertyIndex,
2132 &storeProperty);
2134 if(! hr)
2136 return E_FAIL;
2139 return S_OK;
2142 /*************************************************************************
2144 * Internal Method
2146 * This method takes the previous and the next property link of a property
2147 * to be deleted and find them a place in the Storage.
2149 static HRESULT adjustPropertyChain(
2150 StorageImpl *This,
2151 StgProperty propertyToDelete,
2152 StgProperty parentProperty,
2153 ULONG parentPropertyId,
2154 INT typeOfRelation)
2156 ULONG newLinkProperty = PROPERTY_NULL;
2157 BOOL needToFindAPlaceholder = FALSE;
2158 ULONG storeNode = PROPERTY_NULL;
2159 ULONG toStoreNode = PROPERTY_NULL;
2160 INT relationType = 0;
2161 HRESULT hr = S_OK;
2162 BOOL res = TRUE;
2164 if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
2166 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2169 * Set the parent previous to the property to delete previous
2171 newLinkProperty = propertyToDelete.previousProperty;
2173 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2176 * We also need to find a storage for the other link, setup variables
2177 * to do this at the end...
2179 needToFindAPlaceholder = TRUE;
2180 storeNode = propertyToDelete.previousProperty;
2181 toStoreNode = propertyToDelete.nextProperty;
2182 relationType = PROPERTY_RELATION_NEXT;
2185 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2188 * Set the parent previous to the property to delete next
2190 newLinkProperty = propertyToDelete.nextProperty;
2194 * Link it for real...
2196 parentProperty.previousProperty = newLinkProperty;
2199 else if (typeOfRelation == PROPERTY_RELATION_NEXT)
2201 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2204 * Set the parent next to the property to delete next previous
2206 newLinkProperty = propertyToDelete.previousProperty;
2208 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2211 * We also need to find a storage for the other link, setup variables
2212 * to do this at the end...
2214 needToFindAPlaceholder = TRUE;
2215 storeNode = propertyToDelete.previousProperty;
2216 toStoreNode = propertyToDelete.nextProperty;
2217 relationType = PROPERTY_RELATION_NEXT;
2220 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2223 * Set the parent next to the property to delete next
2225 newLinkProperty = propertyToDelete.nextProperty;
2229 * Link it for real...
2231 parentProperty.nextProperty = newLinkProperty;
2233 else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2235 if (propertyToDelete.previousProperty != PROPERTY_NULL)
2238 * Set the parent dir to the property to delete previous
2240 newLinkProperty = propertyToDelete.previousProperty;
2242 if (propertyToDelete.nextProperty != PROPERTY_NULL)
2245 * We also need to find a storage for the other link, setup variables
2246 * to do this at the end...
2248 needToFindAPlaceholder = TRUE;
2249 storeNode = propertyToDelete.previousProperty;
2250 toStoreNode = propertyToDelete.nextProperty;
2251 relationType = PROPERTY_RELATION_NEXT;
2254 else if (propertyToDelete.nextProperty != PROPERTY_NULL)
2257 * Set the parent dir to the property to delete next
2259 newLinkProperty = propertyToDelete.nextProperty;
2263 * Link it for real...
2265 parentProperty.dirProperty = newLinkProperty;
2269 * Write back the parent property
2271 res = StorageImpl_WriteProperty(
2272 This->base.ancestorStorage,
2273 parentPropertyId,
2274 &parentProperty);
2275 if(! res)
2277 return E_FAIL;
2281 * If a placeholder is required for the other link, then, find one and
2282 * get out of here...
2284 if (needToFindAPlaceholder)
2286 hr = findPlaceholder(
2287 This,
2288 toStoreNode,
2289 storeNode,
2290 relationType);
2293 return hr;
2297 /******************************************************************************
2298 * SetElementTimes (IStorage)
2300 static HRESULT WINAPI StorageImpl_SetElementTimes(
2301 IStorage* iface,
2302 const OLECHAR *pwcsName,/* [string][in] */
2303 const FILETIME *pctime, /* [in] */
2304 const FILETIME *patime, /* [in] */
2305 const FILETIME *pmtime) /* [in] */
2307 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2308 return S_OK;
2311 /******************************************************************************
2312 * SetStateBits (IStorage)
2314 static HRESULT WINAPI StorageImpl_SetStateBits(
2315 IStorage* iface,
2316 DWORD grfStateBits,/* [in] */
2317 DWORD grfMask) /* [in] */
2319 FIXME("not implemented!\n");
2320 return E_NOTIMPL;
2324 * Virtual function table for the IStorage32Impl class.
2326 static const IStorageVtbl Storage32Impl_Vtbl =
2328 StorageBaseImpl_QueryInterface,
2329 StorageBaseImpl_AddRef,
2330 StorageBaseImpl_Release,
2331 StorageBaseImpl_CreateStream,
2332 StorageBaseImpl_OpenStream,
2333 StorageImpl_CreateStorage,
2334 StorageBaseImpl_OpenStorage,
2335 StorageImpl_CopyTo,
2336 StorageImpl_MoveElementTo,
2337 StorageImpl_Commit,
2338 StorageImpl_Revert,
2339 StorageBaseImpl_EnumElements,
2340 StorageImpl_DestroyElement,
2341 StorageBaseImpl_RenameElement,
2342 StorageImpl_SetElementTimes,
2343 StorageBaseImpl_SetClass,
2344 StorageImpl_SetStateBits,
2345 StorageImpl_Stat
2348 static HRESULT StorageImpl_Construct(
2349 StorageImpl* This,
2350 HANDLE hFile,
2351 LPCOLESTR pwcsName,
2352 ILockBytes* pLkbyt,
2353 DWORD openFlags,
2354 BOOL fileBased,
2355 BOOL fileCreate)
2357 HRESULT hr = S_OK;
2358 StgProperty currentProperty;
2359 BOOL readSuccessful;
2360 ULONG currentPropertyIndex;
2362 if ( FAILED( validateSTGM(openFlags) ))
2363 return STG_E_INVALIDFLAG;
2365 memset(This, 0, sizeof(StorageImpl));
2368 * Initialize stream list
2371 list_init(&This->base.strmHead);
2374 * Initialize the virtual function table.
2376 This->base.lpVtbl = &Storage32Impl_Vtbl;
2377 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2378 This->base.v_destructor = &StorageImpl_Destroy;
2379 This->base.openFlags = (openFlags & ~STGM_CREATE);
2382 * This is the top-level storage so initialize the ancestor pointer
2383 * to this.
2385 This->base.ancestorStorage = This;
2388 * Initialize the physical support of the storage.
2390 This->hFile = hFile;
2393 * Store copy of file path.
2395 if(pwcsName) {
2396 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2397 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2398 if (!This->pwcsName)
2399 return STG_E_INSUFFICIENTMEMORY;
2400 strcpyW(This->pwcsName, pwcsName);
2404 * Initialize the big block cache.
2406 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2407 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2408 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2409 pLkbyt,
2410 openFlags,
2411 This->bigBlockSize,
2412 fileBased);
2414 if (This->bigBlockFile == 0)
2415 return E_FAIL;
2417 if (fileCreate)
2419 ULARGE_INTEGER size;
2420 BYTE* bigBlockBuffer;
2423 * Initialize all header variables:
2424 * - The big block depot consists of one block and it is at block 0
2425 * - The properties start at block 1
2426 * - There is no small block depot
2428 memset( This->bigBlockDepotStart,
2429 BLOCK_UNUSED,
2430 sizeof(This->bigBlockDepotStart));
2432 This->bigBlockDepotCount = 1;
2433 This->bigBlockDepotStart[0] = 0;
2434 This->rootStartBlock = 1;
2435 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2436 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2437 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2438 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2439 This->extBigBlockDepotCount = 0;
2441 StorageImpl_SaveFileHeader(This);
2444 * Add one block for the big block depot and one block for the properties
2446 size.u.HighPart = 0;
2447 size.u.LowPart = This->bigBlockSize * 3;
2448 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2451 * Initialize the big block depot
2453 bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2454 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2455 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2456 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2457 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2459 else
2462 * Load the header for the file.
2464 hr = StorageImpl_LoadFileHeader(This);
2466 if (FAILED(hr))
2468 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2470 return hr;
2475 * There is no block depot cached yet.
2477 This->indexBlockDepotCached = 0xFFFFFFFF;
2480 * Start searching for free blocks with block 0.
2482 This->prevFreeBlock = 0;
2485 * Create the block chain abstractions.
2487 if(!(This->rootBlockChain =
2488 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2489 return STG_E_READFAULT;
2491 if(!(This->smallBlockDepotChain =
2492 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2493 PROPERTY_NULL)))
2494 return STG_E_READFAULT;
2497 * Write the root property (memory only)
2499 if (fileCreate)
2501 StgProperty rootProp;
2503 * Initialize the property chain
2505 memset(&rootProp, 0, sizeof(rootProp));
2506 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2507 sizeof(rootProp.name)/sizeof(WCHAR) );
2508 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2509 rootProp.propertyType = PROPTYPE_ROOT;
2510 rootProp.previousProperty = PROPERTY_NULL;
2511 rootProp.nextProperty = PROPERTY_NULL;
2512 rootProp.dirProperty = PROPERTY_NULL;
2513 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2514 rootProp.size.u.HighPart = 0;
2515 rootProp.size.u.LowPart = 0;
2517 StorageImpl_WriteProperty(This, 0, &rootProp);
2521 * Find the ID of the root in the property sets.
2523 currentPropertyIndex = 0;
2527 readSuccessful = StorageImpl_ReadProperty(
2528 This,
2529 currentPropertyIndex,
2530 &currentProperty);
2532 if (readSuccessful)
2534 if ( (currentProperty.sizeOfNameString != 0 ) &&
2535 (currentProperty.propertyType == PROPTYPE_ROOT) )
2537 This->base.rootPropertySetIndex = currentPropertyIndex;
2541 currentPropertyIndex++;
2543 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2545 if (!readSuccessful)
2547 /* TODO CLEANUP */
2548 return STG_E_READFAULT;
2552 * Create the block chain abstraction for the small block root chain.
2554 if(!(This->smallBlockRootChain =
2555 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2556 return STG_E_READFAULT;
2558 return hr;
2561 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2563 StorageImpl *This = (StorageImpl*) iface;
2564 TRACE("(%p)\n", This);
2566 StorageBaseImpl_DeleteAll(&This->base);
2568 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2570 BlockChainStream_Destroy(This->smallBlockRootChain);
2571 BlockChainStream_Destroy(This->rootBlockChain);
2572 BlockChainStream_Destroy(This->smallBlockDepotChain);
2574 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2575 HeapFree(GetProcessHeap(), 0, This);
2578 /******************************************************************************
2579 * Storage32Impl_GetNextFreeBigBlock
2581 * Returns the index of the next free big block.
2582 * If the big block depot is filled, this method will enlarge it.
2585 static ULONG StorageImpl_GetNextFreeBigBlock(
2586 StorageImpl* This)
2588 ULONG depotBlockIndexPos;
2589 void *depotBuffer;
2590 ULONG depotBlockOffset;
2591 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2592 ULONG nextBlockIndex = BLOCK_SPECIAL;
2593 int depotIndex = 0;
2594 ULONG freeBlock = BLOCK_UNUSED;
2596 depotIndex = This->prevFreeBlock / blocksPerDepot;
2597 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2600 * Scan the entire big block depot until we find a block marked free
2602 while (nextBlockIndex != BLOCK_UNUSED)
2604 if (depotIndex < COUNT_BBDEPOTINHEADER)
2606 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2609 * Grow the primary depot.
2611 if (depotBlockIndexPos == BLOCK_UNUSED)
2613 depotBlockIndexPos = depotIndex*blocksPerDepot;
2616 * Add a block depot.
2618 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2619 This->bigBlockDepotCount++;
2620 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2623 * Flag it as a block depot.
2625 StorageImpl_SetNextBlockInChain(This,
2626 depotBlockIndexPos,
2627 BLOCK_SPECIAL);
2629 /* Save new header information.
2631 StorageImpl_SaveFileHeader(This);
2634 else
2636 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2638 if (depotBlockIndexPos == BLOCK_UNUSED)
2641 * Grow the extended depot.
2643 ULONG extIndex = BLOCK_UNUSED;
2644 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2645 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2647 if (extBlockOffset == 0)
2649 /* We need an extended block.
2651 extIndex = Storage32Impl_AddExtBlockDepot(This);
2652 This->extBigBlockDepotCount++;
2653 depotBlockIndexPos = extIndex + 1;
2655 else
2656 depotBlockIndexPos = depotIndex * blocksPerDepot;
2659 * Add a block depot and mark it in the extended block.
2661 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2662 This->bigBlockDepotCount++;
2663 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2665 /* Flag the block depot.
2667 StorageImpl_SetNextBlockInChain(This,
2668 depotBlockIndexPos,
2669 BLOCK_SPECIAL);
2671 /* If necessary, flag the extended depot block.
2673 if (extIndex != BLOCK_UNUSED)
2674 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2676 /* Save header information.
2678 StorageImpl_SaveFileHeader(This);
2682 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2684 if (depotBuffer != 0)
2686 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2687 ( nextBlockIndex != BLOCK_UNUSED))
2689 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2691 if (nextBlockIndex == BLOCK_UNUSED)
2693 freeBlock = (depotIndex * blocksPerDepot) +
2694 (depotBlockOffset/sizeof(ULONG));
2697 depotBlockOffset += sizeof(ULONG);
2700 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2703 depotIndex++;
2704 depotBlockOffset = 0;
2708 * make sure that the block physically exists before using it
2710 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2712 This->prevFreeBlock = freeBlock;
2714 return freeBlock;
2717 /******************************************************************************
2718 * Storage32Impl_AddBlockDepot
2720 * This will create a depot block, essentially it is a block initialized
2721 * to BLOCK_UNUSEDs.
2723 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2725 BYTE* blockBuffer;
2727 blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2730 * Initialize blocks as free
2732 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2734 StorageImpl_ReleaseBigBlock(This, blockBuffer);
2737 /******************************************************************************
2738 * Storage32Impl_GetExtDepotBlock
2740 * Returns the index of the block that corresponds to the specified depot
2741 * index. This method is only for depot indexes equal or greater than
2742 * COUNT_BBDEPOTINHEADER.
2744 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2746 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2747 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2748 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2749 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2750 ULONG blockIndex = BLOCK_UNUSED;
2751 ULONG extBlockIndex = This->extBigBlockDepotStart;
2753 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2755 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2756 return BLOCK_UNUSED;
2758 while (extBlockCount > 0)
2760 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2761 extBlockCount--;
2764 if (extBlockIndex != BLOCK_UNUSED)
2766 BYTE* depotBuffer;
2768 depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2770 if (depotBuffer != 0)
2772 StorageUtl_ReadDWord(depotBuffer,
2773 extBlockOffset * sizeof(ULONG),
2774 &blockIndex);
2776 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2780 return blockIndex;
2783 /******************************************************************************
2784 * Storage32Impl_SetExtDepotBlock
2786 * Associates the specified block index to the specified depot index.
2787 * This method is only for depot indexes equal or greater than
2788 * COUNT_BBDEPOTINHEADER.
2790 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2792 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2793 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2794 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2795 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2796 ULONG extBlockIndex = This->extBigBlockDepotStart;
2798 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2800 while (extBlockCount > 0)
2802 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2803 extBlockCount--;
2806 if (extBlockIndex != BLOCK_UNUSED)
2808 BYTE* depotBuffer;
2810 depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2812 if (depotBuffer != 0)
2814 StorageUtl_WriteDWord(depotBuffer,
2815 extBlockOffset * sizeof(ULONG),
2816 blockIndex);
2818 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2823 /******************************************************************************
2824 * Storage32Impl_AddExtBlockDepot
2826 * Creates an extended depot block.
2828 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2830 ULONG numExtBlocks = This->extBigBlockDepotCount;
2831 ULONG nextExtBlock = This->extBigBlockDepotStart;
2832 BYTE* depotBuffer = NULL;
2833 ULONG index = BLOCK_UNUSED;
2834 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2835 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2836 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2838 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2839 blocksPerDepotBlock;
2841 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2844 * The first extended block.
2846 This->extBigBlockDepotStart = index;
2848 else
2850 unsigned int i;
2852 * Follow the chain to the last one.
2854 for (i = 0; i < (numExtBlocks - 1); i++)
2856 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2860 * Add the new extended block to the chain.
2862 depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2863 StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2864 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2868 * Initialize this block.
2870 depotBuffer = StorageImpl_GetBigBlock(This, index);
2871 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2872 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2874 return index;
2877 /******************************************************************************
2878 * Storage32Impl_FreeBigBlock
2880 * This method will flag the specified block as free in the big block depot.
2882 static void StorageImpl_FreeBigBlock(
2883 StorageImpl* This,
2884 ULONG blockIndex)
2886 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2888 if (blockIndex < This->prevFreeBlock)
2889 This->prevFreeBlock = blockIndex;
2892 /************************************************************************
2893 * Storage32Impl_GetNextBlockInChain
2895 * This method will retrieve the block index of the next big block in
2896 * in the chain.
2898 * Params: This - Pointer to the Storage object.
2899 * blockIndex - Index of the block to retrieve the chain
2900 * for.
2901 * nextBlockIndex - receives the return value.
2903 * Returns: This method returns the index of the next block in the chain.
2904 * It will return the constants:
2905 * BLOCK_SPECIAL - If the block given was not part of a
2906 * chain.
2907 * BLOCK_END_OF_CHAIN - If the block given was the last in
2908 * a chain.
2909 * BLOCK_UNUSED - If the block given was not past of a chain
2910 * and is available.
2911 * BLOCK_EXTBBDEPOT - This block is part of the extended
2912 * big block depot.
2914 * See Windows documentation for more details on IStorage methods.
2916 static HRESULT StorageImpl_GetNextBlockInChain(
2917 StorageImpl* This,
2918 ULONG blockIndex,
2919 ULONG* nextBlockIndex)
2921 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2922 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2923 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2924 void* depotBuffer;
2925 ULONG depotBlockIndexPos;
2926 int index;
2928 *nextBlockIndex = BLOCK_SPECIAL;
2930 if(depotBlockCount >= This->bigBlockDepotCount)
2932 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2933 This->bigBlockDepotCount);
2934 return STG_E_READFAULT;
2938 * Cache the currently accessed depot block.
2940 if (depotBlockCount != This->indexBlockDepotCached)
2942 This->indexBlockDepotCached = depotBlockCount;
2944 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2946 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2948 else
2951 * We have to look in the extended depot.
2953 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2956 depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2958 if (!depotBuffer)
2959 return STG_E_READFAULT;
2961 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2963 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2964 This->blockDepotCached[index] = *nextBlockIndex;
2966 StorageImpl_ReleaseBigBlock(This, depotBuffer);
2969 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2971 return S_OK;
2974 /******************************************************************************
2975 * Storage32Impl_GetNextExtendedBlock
2977 * Given an extended block this method will return the next extended block.
2979 * NOTES:
2980 * The last ULONG of an extended block is the block index of the next
2981 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2982 * depot.
2984 * Return values:
2985 * - The index of the next extended block
2986 * - BLOCK_UNUSED: there is no next extended block.
2987 * - Any other return values denotes failure.
2989 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2991 ULONG nextBlockIndex = BLOCK_SPECIAL;
2992 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2993 void* depotBuffer;
2995 depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2997 if (depotBuffer!=0)
2999 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
3001 StorageImpl_ReleaseBigBlock(This, depotBuffer);
3004 return nextBlockIndex;
3007 /******************************************************************************
3008 * Storage32Impl_SetNextBlockInChain
3010 * This method will write the index of the specified block's next block
3011 * in the big block depot.
3013 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
3014 * do the following
3016 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
3017 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
3018 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
3021 static void StorageImpl_SetNextBlockInChain(
3022 StorageImpl* This,
3023 ULONG blockIndex,
3024 ULONG nextBlock)
3026 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
3027 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
3028 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
3029 ULONG depotBlockIndexPos;
3030 void* depotBuffer;
3032 assert(depotBlockCount < This->bigBlockDepotCount);
3033 assert(blockIndex != nextBlock);
3035 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
3037 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
3039 else
3042 * We have to look in the extended depot.
3044 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
3047 depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
3049 if (depotBuffer!=0)
3051 StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
3052 StorageImpl_ReleaseBigBlock(This, depotBuffer);
3056 * Update the cached block depot, if necessary.
3058 if (depotBlockCount == This->indexBlockDepotCached)
3060 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
3064 /******************************************************************************
3065 * Storage32Impl_LoadFileHeader
3067 * This method will read in the file header, i.e. big block index -1.
3069 static HRESULT StorageImpl_LoadFileHeader(
3070 StorageImpl* This)
3072 HRESULT hr = STG_E_FILENOTFOUND;
3073 void* headerBigBlock = NULL;
3074 int index;
3076 TRACE("\n");
3078 * Get a pointer to the big block of data containing the header.
3080 headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
3083 * Extract the information from the header.
3085 if (headerBigBlock!=0)
3088 * Check for the "magic number" signature and return an error if it is not
3089 * found.
3091 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
3093 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3094 return STG_E_OLDFORMAT;
3097 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
3099 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3100 return STG_E_INVALIDHEADER;
3103 StorageUtl_ReadWord(
3104 headerBigBlock,
3105 OFFSET_BIGBLOCKSIZEBITS,
3106 &This->bigBlockSizeBits);
3108 StorageUtl_ReadWord(
3109 headerBigBlock,
3110 OFFSET_SMALLBLOCKSIZEBITS,
3111 &This->smallBlockSizeBits);
3113 StorageUtl_ReadDWord(
3114 headerBigBlock,
3115 OFFSET_BBDEPOTCOUNT,
3116 &This->bigBlockDepotCount);
3118 StorageUtl_ReadDWord(
3119 headerBigBlock,
3120 OFFSET_ROOTSTARTBLOCK,
3121 &This->rootStartBlock);
3123 StorageUtl_ReadDWord(
3124 headerBigBlock,
3125 OFFSET_SBDEPOTSTART,
3126 &This->smallBlockDepotStart);
3128 StorageUtl_ReadDWord(
3129 headerBigBlock,
3130 OFFSET_EXTBBDEPOTSTART,
3131 &This->extBigBlockDepotStart);
3133 StorageUtl_ReadDWord(
3134 headerBigBlock,
3135 OFFSET_EXTBBDEPOTCOUNT,
3136 &This->extBigBlockDepotCount);
3138 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3140 StorageUtl_ReadDWord(
3141 headerBigBlock,
3142 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3143 &(This->bigBlockDepotStart[index]));
3147 * Make the bitwise arithmetic to get the size of the blocks in bytes.
3149 if ((1 << 2) == 4)
3151 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
3152 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
3154 else
3156 This->bigBlockSize = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
3157 This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
3161 * Right now, the code is making some assumptions about the size of the
3162 * blocks, just make sure they are what we're expecting.
3164 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
3165 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
3167 WARN("Broken OLE storage file\n");
3168 hr = STG_E_INVALIDHEADER;
3170 else
3171 hr = S_OK;
3174 * Release the block.
3176 StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3179 return hr;
3182 /******************************************************************************
3183 * Storage32Impl_SaveFileHeader
3185 * This method will save to the file the header, i.e. big block -1.
3187 static void StorageImpl_SaveFileHeader(
3188 StorageImpl* This)
3190 BYTE headerBigBlock[BIG_BLOCK_SIZE];
3191 int index;
3192 BOOL success;
3195 * Get a pointer to the big block of data containing the header.
3197 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3200 * If the block read failed, the file is probably new.
3202 if (!success)
3205 * Initialize for all unknown fields.
3207 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3210 * Initialize the magic number.
3212 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3215 * And a bunch of things we don't know what they mean
3217 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
3218 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
3219 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
3220 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3224 * Write the information to the header.
3226 StorageUtl_WriteWord(
3227 headerBigBlock,
3228 OFFSET_BIGBLOCKSIZEBITS,
3229 This->bigBlockSizeBits);
3231 StorageUtl_WriteWord(
3232 headerBigBlock,
3233 OFFSET_SMALLBLOCKSIZEBITS,
3234 This->smallBlockSizeBits);
3236 StorageUtl_WriteDWord(
3237 headerBigBlock,
3238 OFFSET_BBDEPOTCOUNT,
3239 This->bigBlockDepotCount);
3241 StorageUtl_WriteDWord(
3242 headerBigBlock,
3243 OFFSET_ROOTSTARTBLOCK,
3244 This->rootStartBlock);
3246 StorageUtl_WriteDWord(
3247 headerBigBlock,
3248 OFFSET_SBDEPOTSTART,
3249 This->smallBlockDepotStart);
3251 StorageUtl_WriteDWord(
3252 headerBigBlock,
3253 OFFSET_SBDEPOTCOUNT,
3254 This->smallBlockDepotChain ?
3255 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3257 StorageUtl_WriteDWord(
3258 headerBigBlock,
3259 OFFSET_EXTBBDEPOTSTART,
3260 This->extBigBlockDepotStart);
3262 StorageUtl_WriteDWord(
3263 headerBigBlock,
3264 OFFSET_EXTBBDEPOTCOUNT,
3265 This->extBigBlockDepotCount);
3267 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3269 StorageUtl_WriteDWord(
3270 headerBigBlock,
3271 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3272 (This->bigBlockDepotStart[index]));
3276 * Write the big block back to the file.
3278 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3281 /******************************************************************************
3282 * Storage32Impl_ReadProperty
3284 * This method will read the specified property from the property chain.
3286 BOOL StorageImpl_ReadProperty(
3287 StorageImpl* This,
3288 ULONG index,
3289 StgProperty* buffer)
3291 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3292 ULARGE_INTEGER offsetInPropSet;
3293 HRESULT readRes;
3294 ULONG bytesRead;
3296 offsetInPropSet.u.HighPart = 0;
3297 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3299 readRes = BlockChainStream_ReadAt(
3300 This->rootBlockChain,
3301 offsetInPropSet,
3302 PROPSET_BLOCK_SIZE,
3303 currentProperty,
3304 &bytesRead);
3306 if (SUCCEEDED(readRes))
3308 /* replace the name of root entry (often "Root Entry") by the file name */
3309 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3310 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3312 memset(buffer->name, 0, sizeof(buffer->name));
3313 memcpy(
3314 buffer->name,
3315 propName,
3316 PROPERTY_NAME_BUFFER_LEN );
3317 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3319 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3321 StorageUtl_ReadWord(
3322 currentProperty,
3323 OFFSET_PS_NAMELENGTH,
3324 &buffer->sizeOfNameString);
3326 StorageUtl_ReadDWord(
3327 currentProperty,
3328 OFFSET_PS_PREVIOUSPROP,
3329 &buffer->previousProperty);
3331 StorageUtl_ReadDWord(
3332 currentProperty,
3333 OFFSET_PS_NEXTPROP,
3334 &buffer->nextProperty);
3336 StorageUtl_ReadDWord(
3337 currentProperty,
3338 OFFSET_PS_DIRPROP,
3339 &buffer->dirProperty);
3341 StorageUtl_ReadGUID(
3342 currentProperty,
3343 OFFSET_PS_GUID,
3344 &buffer->propertyUniqueID);
3346 StorageUtl_ReadDWord(
3347 currentProperty,
3348 OFFSET_PS_TSS1,
3349 &buffer->timeStampS1);
3351 StorageUtl_ReadDWord(
3352 currentProperty,
3353 OFFSET_PS_TSD1,
3354 &buffer->timeStampD1);
3356 StorageUtl_ReadDWord(
3357 currentProperty,
3358 OFFSET_PS_TSS2,
3359 &buffer->timeStampS2);
3361 StorageUtl_ReadDWord(
3362 currentProperty,
3363 OFFSET_PS_TSD2,
3364 &buffer->timeStampD2);
3366 StorageUtl_ReadDWord(
3367 currentProperty,
3368 OFFSET_PS_STARTBLOCK,
3369 &buffer->startingBlock);
3371 StorageUtl_ReadDWord(
3372 currentProperty,
3373 OFFSET_PS_SIZE,
3374 &buffer->size.u.LowPart);
3376 buffer->size.u.HighPart = 0;
3379 return SUCCEEDED(readRes) ? TRUE : FALSE;
3382 /*********************************************************************
3383 * Write the specified property into the property chain
3385 BOOL StorageImpl_WriteProperty(
3386 StorageImpl* This,
3387 ULONG index,
3388 StgProperty* buffer)
3390 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3391 ULARGE_INTEGER offsetInPropSet;
3392 HRESULT writeRes;
3393 ULONG bytesWritten;
3395 offsetInPropSet.u.HighPart = 0;
3396 offsetInPropSet.u.LowPart = index * PROPSET_BLOCK_SIZE;
3398 memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3400 memcpy(
3401 currentProperty + OFFSET_PS_NAME,
3402 buffer->name,
3403 PROPERTY_NAME_BUFFER_LEN );
3405 memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3407 StorageUtl_WriteWord(
3408 currentProperty,
3409 OFFSET_PS_NAMELENGTH,
3410 buffer->sizeOfNameString);
3412 StorageUtl_WriteDWord(
3413 currentProperty,
3414 OFFSET_PS_PREVIOUSPROP,
3415 buffer->previousProperty);
3417 StorageUtl_WriteDWord(
3418 currentProperty,
3419 OFFSET_PS_NEXTPROP,
3420 buffer->nextProperty);
3422 StorageUtl_WriteDWord(
3423 currentProperty,
3424 OFFSET_PS_DIRPROP,
3425 buffer->dirProperty);
3427 StorageUtl_WriteGUID(
3428 currentProperty,
3429 OFFSET_PS_GUID,
3430 &buffer->propertyUniqueID);
3432 StorageUtl_WriteDWord(
3433 currentProperty,
3434 OFFSET_PS_TSS1,
3435 buffer->timeStampS1);
3437 StorageUtl_WriteDWord(
3438 currentProperty,
3439 OFFSET_PS_TSD1,
3440 buffer->timeStampD1);
3442 StorageUtl_WriteDWord(
3443 currentProperty,
3444 OFFSET_PS_TSS2,
3445 buffer->timeStampS2);
3447 StorageUtl_WriteDWord(
3448 currentProperty,
3449 OFFSET_PS_TSD2,
3450 buffer->timeStampD2);
3452 StorageUtl_WriteDWord(
3453 currentProperty,
3454 OFFSET_PS_STARTBLOCK,
3455 buffer->startingBlock);
3457 StorageUtl_WriteDWord(
3458 currentProperty,
3459 OFFSET_PS_SIZE,
3460 buffer->size.u.LowPart);
3462 writeRes = BlockChainStream_WriteAt(This->rootBlockChain,
3463 offsetInPropSet,
3464 PROPSET_BLOCK_SIZE,
3465 currentProperty,
3466 &bytesWritten);
3467 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3470 static BOOL StorageImpl_ReadBigBlock(
3471 StorageImpl* This,
3472 ULONG blockIndex,
3473 void* buffer)
3475 void* bigBlockBuffer;
3477 bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3479 if (bigBlockBuffer!=0)
3481 memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3483 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3485 return TRUE;
3488 return FALSE;
3491 static BOOL StorageImpl_WriteBigBlock(
3492 StorageImpl* This,
3493 ULONG blockIndex,
3494 void* buffer)
3496 void* bigBlockBuffer;
3498 bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3500 if (bigBlockBuffer!=0)
3502 memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3504 StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3506 return TRUE;
3509 return FALSE;
3512 static void* StorageImpl_GetROBigBlock(
3513 StorageImpl* This,
3514 ULONG blockIndex)
3516 return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3519 static void* StorageImpl_GetBigBlock(
3520 StorageImpl* This,
3521 ULONG blockIndex)
3523 return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3526 static void StorageImpl_ReleaseBigBlock(
3527 StorageImpl* This,
3528 void* pBigBlock)
3530 BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3533 /******************************************************************************
3534 * Storage32Impl_SmallBlocksToBigBlocks
3536 * This method will convert a small block chain to a big block chain.
3537 * The small block chain will be destroyed.
3539 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3540 StorageImpl* This,
3541 SmallBlockChainStream** ppsbChain)
3543 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3544 ULARGE_INTEGER size, offset;
3545 ULONG cbRead, cbWritten;
3546 ULARGE_INTEGER cbTotalRead;
3547 ULONG propertyIndex;
3548 HRESULT resWrite = S_OK;
3549 HRESULT resRead;
3550 StgProperty chainProperty;
3551 BYTE *buffer;
3552 BlockChainStream *bbTempChain = NULL;
3553 BlockChainStream *bigBlockChain = NULL;
3556 * Create a temporary big block chain that doesn't have
3557 * an associated property. This temporary chain will be
3558 * used to copy data from small blocks to big blocks.
3560 bbTempChain = BlockChainStream_Construct(This,
3561 &bbHeadOfChain,
3562 PROPERTY_NULL);
3563 if(!bbTempChain) return NULL;
3565 * Grow the big block chain.
3567 size = SmallBlockChainStream_GetSize(*ppsbChain);
3568 BlockChainStream_SetSize(bbTempChain, size);
3571 * Copy the contents of the small block chain to the big block chain
3572 * by small block size increments.
3574 offset.u.LowPart = 0;
3575 offset.u.HighPart = 0;
3576 cbTotalRead.QuadPart = 0;
3578 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3581 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3582 offset,
3583 This->smallBlockSize,
3584 buffer,
3585 &cbRead);
3586 if (FAILED(resRead))
3587 break;
3589 if (cbRead > 0)
3591 cbTotalRead.QuadPart += cbRead;
3593 resWrite = BlockChainStream_WriteAt(bbTempChain,
3594 offset,
3595 cbRead,
3596 buffer,
3597 &cbWritten);
3599 if (FAILED(resWrite))
3600 break;
3602 offset.u.LowPart += This->smallBlockSize;
3604 } while (cbTotalRead.QuadPart < size.QuadPart);
3605 HeapFree(GetProcessHeap(),0,buffer);
3607 if (FAILED(resRead) || FAILED(resWrite))
3609 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3610 BlockChainStream_Destroy(bbTempChain);
3611 return NULL;
3615 * Destroy the small block chain.
3617 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3618 size.u.HighPart = 0;
3619 size.u.LowPart = 0;
3620 SmallBlockChainStream_SetSize(*ppsbChain, size);
3621 SmallBlockChainStream_Destroy(*ppsbChain);
3622 *ppsbChain = 0;
3625 * Change the property information. This chain is now a big block chain
3626 * and it doesn't reside in the small blocks chain anymore.
3628 StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3630 chainProperty.startingBlock = bbHeadOfChain;
3632 StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3635 * Destroy the temporary propertyless big block chain.
3636 * Create a new big block chain associated with this property.
3638 BlockChainStream_Destroy(bbTempChain);
3639 bigBlockChain = BlockChainStream_Construct(This,
3640 NULL,
3641 propertyIndex);
3643 return bigBlockChain;
3646 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3648 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3650 StorageBaseImpl_Release((IStorage*)This->base.ancestorStorage);
3651 HeapFree(GetProcessHeap(), 0, This);
3654 /******************************************************************************
3656 ** Storage32InternalImpl_Commit
3658 ** The non-root storages cannot be opened in transacted mode thus this function
3659 ** does nothing.
3661 static HRESULT WINAPI StorageInternalImpl_Commit(
3662 IStorage* iface,
3663 DWORD grfCommitFlags) /* [in] */
3665 return S_OK;
3668 /******************************************************************************
3670 ** Storage32InternalImpl_Revert
3672 ** The non-root storages cannot be opened in transacted mode thus this function
3673 ** does nothing.
3675 static HRESULT WINAPI StorageInternalImpl_Revert(
3676 IStorage* iface)
3678 return S_OK;
3681 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3683 IStorage_Release((IStorage*)This->parentStorage);
3684 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3685 HeapFree(GetProcessHeap(), 0, This);
3688 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3689 IEnumSTATSTG* iface,
3690 REFIID riid,
3691 void** ppvObject)
3693 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3696 * Perform a sanity check on the parameters.
3698 if (ppvObject==0)
3699 return E_INVALIDARG;
3702 * Initialize the return parameter.
3704 *ppvObject = 0;
3707 * Compare the riid with the interface IDs implemented by this object.
3709 if (IsEqualGUID(&IID_IUnknown, riid) ||
3710 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3712 *ppvObject = (IEnumSTATSTG*)This;
3713 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3714 return S_OK;
3717 return E_NOINTERFACE;
3720 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3721 IEnumSTATSTG* iface)
3723 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3724 return InterlockedIncrement(&This->ref);
3727 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3728 IEnumSTATSTG* iface)
3730 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3732 ULONG newRef;
3734 newRef = InterlockedDecrement(&This->ref);
3737 * If the reference count goes down to 0, perform suicide.
3739 if (newRef==0)
3741 IEnumSTATSTGImpl_Destroy(This);
3744 return newRef;
3747 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3748 IEnumSTATSTG* iface,
3749 ULONG celt,
3750 STATSTG* rgelt,
3751 ULONG* pceltFetched)
3753 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3755 StgProperty currentProperty;
3756 STATSTG* currentReturnStruct = rgelt;
3757 ULONG objectFetched = 0;
3758 ULONG currentSearchNode;
3761 * Perform a sanity check on the parameters.
3763 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3764 return E_INVALIDARG;
3767 * To avoid the special case, get another pointer to a ULONG value if
3768 * the caller didn't supply one.
3770 if (pceltFetched==0)
3771 pceltFetched = &objectFetched;
3774 * Start the iteration, we will iterate until we hit the end of the
3775 * linked list or until we hit the number of items to iterate through
3777 *pceltFetched = 0;
3780 * Start with the node at the top of the stack.
3782 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3784 while ( ( *pceltFetched < celt) &&
3785 ( currentSearchNode!=PROPERTY_NULL) )
3788 * Remove the top node from the stack
3790 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3793 * Read the property from the storage.
3795 StorageImpl_ReadProperty(This->parentStorage,
3796 currentSearchNode,
3797 &currentProperty);
3800 * Copy the information to the return buffer.
3802 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3803 &currentProperty,
3804 STATFLAG_DEFAULT);
3807 * Step to the next item in the iteration
3809 (*pceltFetched)++;
3810 currentReturnStruct++;
3813 * Push the next search node in the search stack.
3815 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3818 * continue the iteration.
3820 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3823 if (*pceltFetched == celt)
3824 return S_OK;
3826 return S_FALSE;
3830 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3831 IEnumSTATSTG* iface,
3832 ULONG celt)
3834 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3836 StgProperty currentProperty;
3837 ULONG objectFetched = 0;
3838 ULONG currentSearchNode;
3841 * Start with the node at the top of the stack.
3843 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3845 while ( (objectFetched < celt) &&
3846 (currentSearchNode!=PROPERTY_NULL) )
3849 * Remove the top node from the stack
3851 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3854 * Read the property from the storage.
3856 StorageImpl_ReadProperty(This->parentStorage,
3857 currentSearchNode,
3858 &currentProperty);
3861 * Step to the next item in the iteration
3863 objectFetched++;
3866 * Push the next search node in the search stack.
3868 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3871 * continue the iteration.
3873 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3876 if (objectFetched == celt)
3877 return S_OK;
3879 return S_FALSE;
3882 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3883 IEnumSTATSTG* iface)
3885 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3887 StgProperty rootProperty;
3888 BOOL readSuccessful;
3891 * Re-initialize the search stack to an empty stack
3893 This->stackSize = 0;
3896 * Read the root property from the storage.
3898 readSuccessful = StorageImpl_ReadProperty(
3899 This->parentStorage,
3900 This->firstPropertyNode,
3901 &rootProperty);
3903 if (readSuccessful)
3905 assert(rootProperty.sizeOfNameString!=0);
3908 * Push the search node in the search stack.
3910 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3913 return S_OK;
3916 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3917 IEnumSTATSTG* iface,
3918 IEnumSTATSTG** ppenum)
3920 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3922 IEnumSTATSTGImpl* newClone;
3925 * Perform a sanity check on the parameters.
3927 if (ppenum==0)
3928 return E_INVALIDARG;
3930 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3931 This->firstPropertyNode);
3935 * The new clone enumeration must point to the same current node as
3936 * the ole one.
3938 newClone->stackSize = This->stackSize ;
3939 newClone->stackMaxSize = This->stackMaxSize ;
3940 newClone->stackToVisit =
3941 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3943 memcpy(
3944 newClone->stackToVisit,
3945 This->stackToVisit,
3946 sizeof(ULONG) * newClone->stackSize);
3948 *ppenum = (IEnumSTATSTG*)newClone;
3951 * Don't forget to nail down a reference to the clone before
3952 * returning it.
3954 IEnumSTATSTGImpl_AddRef(*ppenum);
3956 return S_OK;
3959 static INT IEnumSTATSTGImpl_FindParentProperty(
3960 IEnumSTATSTGImpl *This,
3961 ULONG childProperty,
3962 StgProperty *currentProperty,
3963 ULONG *thisNodeId)
3965 ULONG currentSearchNode;
3966 ULONG foundNode;
3969 * To avoid the special case, get another pointer to a ULONG value if
3970 * the caller didn't supply one.
3972 if (thisNodeId==0)
3973 thisNodeId = &foundNode;
3976 * Start with the node at the top of the stack.
3978 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3981 while (currentSearchNode!=PROPERTY_NULL)
3984 * Store the current node in the returned parameters
3986 *thisNodeId = currentSearchNode;
3989 * Remove the top node from the stack
3991 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3994 * Read the property from the storage.
3996 StorageImpl_ReadProperty(
3997 This->parentStorage,
3998 currentSearchNode,
3999 currentProperty);
4001 if (currentProperty->previousProperty == childProperty)
4002 return PROPERTY_RELATION_PREVIOUS;
4004 else if (currentProperty->nextProperty == childProperty)
4005 return PROPERTY_RELATION_NEXT;
4007 else if (currentProperty->dirProperty == childProperty)
4008 return PROPERTY_RELATION_DIR;
4011 * Push the next search node in the search stack.
4013 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4016 * continue the iteration.
4018 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4021 return PROPERTY_NULL;
4024 static ULONG IEnumSTATSTGImpl_FindProperty(
4025 IEnumSTATSTGImpl* This,
4026 const OLECHAR* lpszPropName,
4027 StgProperty* currentProperty)
4029 ULONG currentSearchNode;
4032 * Start with the node at the top of the stack.
4034 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4036 while (currentSearchNode!=PROPERTY_NULL)
4039 * Remove the top node from the stack
4041 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
4044 * Read the property from the storage.
4046 StorageImpl_ReadProperty(This->parentStorage,
4047 currentSearchNode,
4048 currentProperty);
4050 if ( propertyNameCmp(
4051 (const OLECHAR*)currentProperty->name,
4052 (const OLECHAR*)lpszPropName) == 0)
4053 return currentSearchNode;
4056 * Push the next search node in the search stack.
4058 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
4061 * continue the iteration.
4063 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
4066 return PROPERTY_NULL;
4069 static void IEnumSTATSTGImpl_PushSearchNode(
4070 IEnumSTATSTGImpl* This,
4071 ULONG nodeToPush)
4073 StgProperty rootProperty;
4074 BOOL readSuccessful;
4077 * First, make sure we're not trying to push an unexisting node.
4079 if (nodeToPush==PROPERTY_NULL)
4080 return;
4083 * First push the node to the stack
4085 if (This->stackSize == This->stackMaxSize)
4087 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4089 This->stackToVisit = HeapReAlloc(
4090 GetProcessHeap(),
4092 This->stackToVisit,
4093 sizeof(ULONG) * This->stackMaxSize);
4096 This->stackToVisit[This->stackSize] = nodeToPush;
4097 This->stackSize++;
4100 * Read the root property from the storage.
4102 readSuccessful = StorageImpl_ReadProperty(
4103 This->parentStorage,
4104 nodeToPush,
4105 &rootProperty);
4107 if (readSuccessful)
4109 assert(rootProperty.sizeOfNameString!=0);
4112 * Push the previous search node in the search stack.
4114 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4118 static ULONG IEnumSTATSTGImpl_PopSearchNode(
4119 IEnumSTATSTGImpl* This,
4120 BOOL remove)
4122 ULONG topNode;
4124 if (This->stackSize == 0)
4125 return PROPERTY_NULL;
4127 topNode = This->stackToVisit[This->stackSize-1];
4129 if (remove)
4130 This->stackSize--;
4132 return topNode;
4136 * Virtual function table for the IEnumSTATSTGImpl class.
4138 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
4140 IEnumSTATSTGImpl_QueryInterface,
4141 IEnumSTATSTGImpl_AddRef,
4142 IEnumSTATSTGImpl_Release,
4143 IEnumSTATSTGImpl_Next,
4144 IEnumSTATSTGImpl_Skip,
4145 IEnumSTATSTGImpl_Reset,
4146 IEnumSTATSTGImpl_Clone
4149 /******************************************************************************
4150 ** IEnumSTATSTGImpl implementation
4153 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
4154 StorageImpl* parentStorage,
4155 ULONG firstPropertyNode)
4157 IEnumSTATSTGImpl* newEnumeration;
4159 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
4161 if (newEnumeration!=0)
4164 * Set-up the virtual function table and reference count.
4166 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
4167 newEnumeration->ref = 0;
4170 * We want to nail-down the reference to the storage in case the
4171 * enumeration out-lives the storage in the client application.
4173 newEnumeration->parentStorage = parentStorage;
4174 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
4176 newEnumeration->firstPropertyNode = firstPropertyNode;
4179 * Initialize the search stack
4181 newEnumeration->stackSize = 0;
4182 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
4183 newEnumeration->stackToVisit =
4184 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
4187 * Make sure the current node of the iterator is the first one.
4189 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
4192 return newEnumeration;
4196 * Virtual function table for the Storage32InternalImpl class.
4198 static const IStorageVtbl Storage32InternalImpl_Vtbl =
4200 StorageBaseImpl_QueryInterface,
4201 StorageBaseImpl_AddRef,
4202 StorageBaseImpl_Release,
4203 StorageBaseImpl_CreateStream,
4204 StorageBaseImpl_OpenStream,
4205 StorageImpl_CreateStorage,
4206 StorageBaseImpl_OpenStorage,
4207 StorageImpl_CopyTo,
4208 StorageImpl_MoveElementTo,
4209 StorageInternalImpl_Commit,
4210 StorageInternalImpl_Revert,
4211 StorageBaseImpl_EnumElements,
4212 StorageImpl_DestroyElement,
4213 StorageBaseImpl_RenameElement,
4214 StorageImpl_SetElementTimes,
4215 StorageBaseImpl_SetClass,
4216 StorageImpl_SetStateBits,
4217 StorageBaseImpl_Stat
4220 /******************************************************************************
4221 ** Storage32InternalImpl implementation
4224 static StorageInternalImpl* StorageInternalImpl_Construct(
4225 StorageImpl* ancestorStorage,
4226 DWORD openFlags,
4227 ULONG rootPropertyIndex)
4229 StorageInternalImpl* newStorage;
4232 * Allocate space for the new storage object
4234 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
4236 if (newStorage!=0)
4238 memset(newStorage, 0, sizeof(StorageInternalImpl));
4241 * Initialize the stream list
4244 list_init(&newStorage->base.strmHead);
4247 * Initialize the virtual function table.
4249 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
4250 newStorage->base.v_destructor = &StorageInternalImpl_Destroy;
4251 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
4254 * Keep the ancestor storage pointer and nail a reference to it.
4256 newStorage->base.ancestorStorage = ancestorStorage;
4257 StorageBaseImpl_AddRef((IStorage*)(newStorage->base.ancestorStorage));
4260 * Keep the index of the root property set for this storage,
4262 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
4264 return newStorage;
4267 return 0;
4270 /******************************************************************************
4271 ** StorageUtl implementation
4274 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
4276 WORD tmp;
4278 memcpy(&tmp, buffer+offset, sizeof(WORD));
4279 *value = le16toh(tmp);
4282 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
4284 value = htole16(value);
4285 memcpy(buffer+offset, &value, sizeof(WORD));
4288 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
4290 DWORD tmp;
4292 memcpy(&tmp, buffer+offset, sizeof(DWORD));
4293 *value = le32toh(tmp);
4296 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4298 value = htole32(value);
4299 memcpy(buffer+offset, &value, sizeof(DWORD));
4302 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4303 ULARGE_INTEGER* value)
4305 #ifdef WORDS_BIGENDIAN
4306 ULARGE_INTEGER tmp;
4308 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4309 value->u.LowPart = htole32(tmp.u.HighPart);
4310 value->u.HighPart = htole32(tmp.u.LowPart);
4311 #else
4312 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4313 #endif
4316 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4317 const ULARGE_INTEGER *value)
4319 #ifdef WORDS_BIGENDIAN
4320 ULARGE_INTEGER tmp;
4322 tmp.u.LowPart = htole32(value->u.HighPart);
4323 tmp.u.HighPart = htole32(value->u.LowPart);
4324 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4325 #else
4326 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4327 #endif
4330 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4332 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4333 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4334 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4336 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4339 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4341 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4342 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4343 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4345 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4348 void StorageUtl_CopyPropertyToSTATSTG(
4349 STATSTG* destination,
4350 StgProperty* source,
4351 int statFlags)
4354 * The copy of the string occurs only when the flag is not set
4356 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4357 (source->name == NULL) ||
4358 (source->name[0] == 0) )
4360 destination->pwcsName = 0;
4362 else
4364 destination->pwcsName =
4365 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4367 strcpyW((LPWSTR)destination->pwcsName, source->name);
4370 switch (source->propertyType)
4372 case PROPTYPE_STORAGE:
4373 case PROPTYPE_ROOT:
4374 destination->type = STGTY_STORAGE;
4375 break;
4376 case PROPTYPE_STREAM:
4377 destination->type = STGTY_STREAM;
4378 break;
4379 default:
4380 destination->type = STGTY_STREAM;
4381 break;
4384 destination->cbSize = source->size;
4386 currentReturnStruct->mtime = {0}; TODO
4387 currentReturnStruct->ctime = {0};
4388 currentReturnStruct->atime = {0};
4390 destination->grfMode = 0;
4391 destination->grfLocksSupported = 0;
4392 destination->clsid = source->propertyUniqueID;
4393 destination->grfStateBits = 0;
4394 destination->reserved = 0;
4397 /******************************************************************************
4398 ** BlockChainStream implementation
4401 BlockChainStream* BlockChainStream_Construct(
4402 StorageImpl* parentStorage,
4403 ULONG* headOfStreamPlaceHolder,
4404 ULONG propertyIndex)
4406 BlockChainStream* newStream;
4407 ULONG blockIndex;
4409 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4411 newStream->parentStorage = parentStorage;
4412 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4413 newStream->ownerPropertyIndex = propertyIndex;
4414 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4415 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4416 newStream->numBlocks = 0;
4418 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4420 while (blockIndex != BLOCK_END_OF_CHAIN)
4422 newStream->numBlocks++;
4423 newStream->tailIndex = blockIndex;
4425 if(FAILED(StorageImpl_GetNextBlockInChain(
4426 parentStorage,
4427 blockIndex,
4428 &blockIndex)))
4430 HeapFree(GetProcessHeap(), 0, newStream);
4431 return NULL;
4435 return newStream;
4438 void BlockChainStream_Destroy(BlockChainStream* This)
4440 HeapFree(GetProcessHeap(), 0, This);
4443 /******************************************************************************
4444 * BlockChainStream_GetHeadOfChain
4446 * Returns the head of this stream chain.
4447 * Some special chains don't have properties, their heads are kept in
4448 * This->headOfStreamPlaceHolder.
4451 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4453 StgProperty chainProperty;
4454 BOOL readSuccessful;
4456 if (This->headOfStreamPlaceHolder != 0)
4457 return *(This->headOfStreamPlaceHolder);
4459 if (This->ownerPropertyIndex != PROPERTY_NULL)
4461 readSuccessful = StorageImpl_ReadProperty(
4462 This->parentStorage,
4463 This->ownerPropertyIndex,
4464 &chainProperty);
4466 if (readSuccessful)
4468 return chainProperty.startingBlock;
4472 return BLOCK_END_OF_CHAIN;
4475 /******************************************************************************
4476 * BlockChainStream_GetCount
4478 * Returns the number of blocks that comprises this chain.
4479 * This is not the size of the stream as the last block may not be full!
4482 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4484 ULONG blockIndex;
4485 ULONG count = 0;
4487 blockIndex = BlockChainStream_GetHeadOfChain(This);
4489 while (blockIndex != BLOCK_END_OF_CHAIN)
4491 count++;
4493 if(FAILED(StorageImpl_GetNextBlockInChain(
4494 This->parentStorage,
4495 blockIndex,
4496 &blockIndex)))
4497 return 0;
4500 return count;
4503 /******************************************************************************
4504 * BlockChainStream_ReadAt
4506 * Reads a specified number of bytes from this chain at the specified offset.
4507 * bytesRead may be NULL.
4508 * Failure will be returned if the specified number of bytes has not been read.
4510 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4511 ULARGE_INTEGER offset,
4512 ULONG size,
4513 void* buffer,
4514 ULONG* bytesRead)
4516 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4517 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4518 ULONG bytesToReadInBuffer;
4519 ULONG blockIndex;
4520 BYTE* bufferWalker;
4521 BYTE* bigBlockBuffer;
4524 * Find the first block in the stream that contains part of the buffer.
4526 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4527 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4528 (blockNoInSequence < This->lastBlockNoInSequence) )
4530 blockIndex = BlockChainStream_GetHeadOfChain(This);
4531 This->lastBlockNoInSequence = blockNoInSequence;
4533 else
4535 ULONG temp = blockNoInSequence;
4537 blockIndex = This->lastBlockNoInSequenceIndex;
4538 blockNoInSequence -= This->lastBlockNoInSequence;
4539 This->lastBlockNoInSequence = temp;
4542 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4544 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4545 return STG_E_DOCFILECORRUPT;
4546 blockNoInSequence--;
4549 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4550 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4552 This->lastBlockNoInSequenceIndex = blockIndex;
4555 * Start reading the buffer.
4557 *bytesRead = 0;
4558 bufferWalker = buffer;
4560 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4563 * Calculate how many bytes we can copy from this big block.
4565 bytesToReadInBuffer =
4566 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4569 * Copy those bytes to the buffer
4571 bigBlockBuffer =
4572 StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4573 if (!bigBlockBuffer)
4574 return STG_E_READFAULT;
4576 memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4578 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4581 * Step to the next big block.
4583 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4584 return STG_E_DOCFILECORRUPT;
4586 bufferWalker += bytesToReadInBuffer;
4587 size -= bytesToReadInBuffer;
4588 *bytesRead += bytesToReadInBuffer;
4589 offsetInBlock = 0; /* There is no offset on the next block */
4593 return (size == 0) ? S_OK : STG_E_READFAULT;
4596 /******************************************************************************
4597 * BlockChainStream_WriteAt
4599 * Writes the specified number of bytes to this chain at the specified offset.
4600 * bytesWritten may be NULL.
4601 * Will fail if not all specified number of bytes have been written.
4603 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4604 ULARGE_INTEGER offset,
4605 ULONG size,
4606 const void* buffer,
4607 ULONG* bytesWritten)
4609 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4610 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4611 ULONG bytesToWrite;
4612 ULONG blockIndex;
4613 const BYTE* bufferWalker;
4614 BYTE* bigBlockBuffer;
4617 * Find the first block in the stream that contains part of the buffer.
4619 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4620 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4621 (blockNoInSequence < This->lastBlockNoInSequence) )
4623 blockIndex = BlockChainStream_GetHeadOfChain(This);
4624 This->lastBlockNoInSequence = blockNoInSequence;
4626 else
4628 ULONG temp = blockNoInSequence;
4630 blockIndex = This->lastBlockNoInSequenceIndex;
4631 blockNoInSequence -= This->lastBlockNoInSequence;
4632 This->lastBlockNoInSequence = temp;
4635 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4637 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4638 &blockIndex)))
4639 return STG_E_DOCFILECORRUPT;
4640 blockNoInSequence--;
4643 This->lastBlockNoInSequenceIndex = blockIndex;
4645 /* BlockChainStream_SetSize should have already been called to ensure we have
4646 * enough blocks in the chain to write into */
4647 if (blockIndex == BLOCK_END_OF_CHAIN)
4649 ERR("not enough blocks in chain to write data\n");
4650 return STG_E_DOCFILECORRUPT;
4654 * Here, I'm casting away the constness on the buffer variable
4655 * This is OK since we don't intend to modify that buffer.
4657 *bytesWritten = 0;
4658 bufferWalker = (const BYTE*)buffer;
4660 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4663 * Calculate how many bytes we can copy from this big block.
4665 bytesToWrite =
4666 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4669 * Copy those bytes to the buffer
4671 bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4673 memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4675 StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4678 * Step to the next big block.
4680 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4681 &blockIndex)))
4682 return STG_E_DOCFILECORRUPT;
4683 bufferWalker += bytesToWrite;
4684 size -= bytesToWrite;
4685 *bytesWritten += bytesToWrite;
4686 offsetInBlock = 0; /* There is no offset on the next block */
4689 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4692 /******************************************************************************
4693 * BlockChainStream_Shrink
4695 * Shrinks this chain in the big block depot.
4697 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4698 ULARGE_INTEGER newSize)
4700 ULONG blockIndex, extraBlock;
4701 ULONG numBlocks;
4702 ULONG count = 1;
4705 * Reset the last accessed block cache.
4707 This->lastBlockNoInSequence = 0xFFFFFFFF;
4708 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4711 * Figure out how many blocks are needed to contain the new size
4713 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4715 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4716 numBlocks++;
4718 blockIndex = BlockChainStream_GetHeadOfChain(This);
4721 * Go to the new end of chain
4723 while (count < numBlocks)
4725 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4726 &blockIndex)))
4727 return FALSE;
4728 count++;
4731 /* Get the next block before marking the new end */
4732 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4733 &extraBlock)))
4734 return FALSE;
4736 /* Mark the new end of chain */
4737 StorageImpl_SetNextBlockInChain(
4738 This->parentStorage,
4739 blockIndex,
4740 BLOCK_END_OF_CHAIN);
4742 This->tailIndex = blockIndex;
4743 This->numBlocks = numBlocks;
4746 * Mark the extra blocks as free
4748 while (extraBlock != BLOCK_END_OF_CHAIN)
4750 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4751 &blockIndex)))
4752 return FALSE;
4753 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4754 extraBlock = blockIndex;
4757 return TRUE;
4760 /******************************************************************************
4761 * BlockChainStream_Enlarge
4763 * Grows this chain in the big block depot.
4765 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4766 ULARGE_INTEGER newSize)
4768 ULONG blockIndex, currentBlock;
4769 ULONG newNumBlocks;
4770 ULONG oldNumBlocks = 0;
4772 blockIndex = BlockChainStream_GetHeadOfChain(This);
4775 * Empty chain. Create the head.
4777 if (blockIndex == BLOCK_END_OF_CHAIN)
4779 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4780 StorageImpl_SetNextBlockInChain(This->parentStorage,
4781 blockIndex,
4782 BLOCK_END_OF_CHAIN);
4784 if (This->headOfStreamPlaceHolder != 0)
4786 *(This->headOfStreamPlaceHolder) = blockIndex;
4788 else
4790 StgProperty chainProp;
4791 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4793 StorageImpl_ReadProperty(
4794 This->parentStorage,
4795 This->ownerPropertyIndex,
4796 &chainProp);
4798 chainProp.startingBlock = blockIndex;
4800 StorageImpl_WriteProperty(
4801 This->parentStorage,
4802 This->ownerPropertyIndex,
4803 &chainProp);
4806 This->tailIndex = blockIndex;
4807 This->numBlocks = 1;
4811 * Figure out how many blocks are needed to contain this stream
4813 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4815 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4816 newNumBlocks++;
4819 * Go to the current end of chain
4821 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4823 currentBlock = blockIndex;
4825 while (blockIndex != BLOCK_END_OF_CHAIN)
4827 This->numBlocks++;
4828 currentBlock = blockIndex;
4830 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4831 &blockIndex)))
4832 return FALSE;
4835 This->tailIndex = currentBlock;
4838 currentBlock = This->tailIndex;
4839 oldNumBlocks = This->numBlocks;
4842 * Add new blocks to the chain
4844 if (oldNumBlocks < newNumBlocks)
4846 while (oldNumBlocks < newNumBlocks)
4848 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4850 StorageImpl_SetNextBlockInChain(
4851 This->parentStorage,
4852 currentBlock,
4853 blockIndex);
4855 StorageImpl_SetNextBlockInChain(
4856 This->parentStorage,
4857 blockIndex,
4858 BLOCK_END_OF_CHAIN);
4860 currentBlock = blockIndex;
4861 oldNumBlocks++;
4864 This->tailIndex = blockIndex;
4865 This->numBlocks = newNumBlocks;
4868 return TRUE;
4871 /******************************************************************************
4872 * BlockChainStream_SetSize
4874 * Sets the size of this stream. The big block depot will be updated.
4875 * The file will grow if we grow the chain.
4877 * TODO: Free the actual blocks in the file when we shrink the chain.
4878 * Currently, the blocks are still in the file. So the file size
4879 * doesn't shrink even if we shrink streams.
4881 BOOL BlockChainStream_SetSize(
4882 BlockChainStream* This,
4883 ULARGE_INTEGER newSize)
4885 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4887 if (newSize.u.LowPart == size.u.LowPart)
4888 return TRUE;
4890 if (newSize.u.LowPart < size.u.LowPart)
4892 BlockChainStream_Shrink(This, newSize);
4894 else
4896 BlockChainStream_Enlarge(This, newSize);
4899 return TRUE;
4902 /******************************************************************************
4903 * BlockChainStream_GetSize
4905 * Returns the size of this chain.
4906 * Will return the block count if this chain doesn't have a property.
4908 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4910 StgProperty chainProperty;
4912 if(This->headOfStreamPlaceHolder == NULL)
4915 * This chain is a data stream read the property and return
4916 * the appropriate size
4918 StorageImpl_ReadProperty(
4919 This->parentStorage,
4920 This->ownerPropertyIndex,
4921 &chainProperty);
4923 return chainProperty.size;
4925 else
4928 * this chain is a chain that does not have a property, figure out the
4929 * size by making the product number of used blocks times the
4930 * size of them
4932 ULARGE_INTEGER result;
4933 result.u.HighPart = 0;
4935 result.u.LowPart =
4936 BlockChainStream_GetCount(This) *
4937 This->parentStorage->bigBlockSize;
4939 return result;
4943 /******************************************************************************
4944 ** SmallBlockChainStream implementation
4947 SmallBlockChainStream* SmallBlockChainStream_Construct(
4948 StorageImpl* parentStorage,
4949 ULONG propertyIndex)
4951 SmallBlockChainStream* newStream;
4953 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4955 newStream->parentStorage = parentStorage;
4956 newStream->ownerPropertyIndex = propertyIndex;
4958 return newStream;
4961 void SmallBlockChainStream_Destroy(
4962 SmallBlockChainStream* This)
4964 HeapFree(GetProcessHeap(), 0, This);
4967 /******************************************************************************
4968 * SmallBlockChainStream_GetHeadOfChain
4970 * Returns the head of this chain of small blocks.
4972 static ULONG SmallBlockChainStream_GetHeadOfChain(
4973 SmallBlockChainStream* This)
4975 StgProperty chainProperty;
4976 BOOL readSuccessful;
4978 if (This->ownerPropertyIndex)
4980 readSuccessful = StorageImpl_ReadProperty(
4981 This->parentStorage,
4982 This->ownerPropertyIndex,
4983 &chainProperty);
4985 if (readSuccessful)
4987 return chainProperty.startingBlock;
4992 return BLOCK_END_OF_CHAIN;
4995 /******************************************************************************
4996 * SmallBlockChainStream_GetNextBlockInChain
4998 * Returns the index of the next small block in this chain.
5000 * Return Values:
5001 * - BLOCK_END_OF_CHAIN: end of this chain
5002 * - BLOCK_UNUSED: small block 'blockIndex' is free
5004 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
5005 SmallBlockChainStream* This,
5006 ULONG blockIndex,
5007 ULONG* nextBlockInChain)
5009 ULARGE_INTEGER offsetOfBlockInDepot;
5010 DWORD buffer;
5011 ULONG bytesRead;
5012 HRESULT res;
5014 *nextBlockInChain = BLOCK_END_OF_CHAIN;
5016 offsetOfBlockInDepot.u.HighPart = 0;
5017 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5020 * Read those bytes in the buffer from the small block file.
5022 res = BlockChainStream_ReadAt(
5023 This->parentStorage->smallBlockDepotChain,
5024 offsetOfBlockInDepot,
5025 sizeof(DWORD),
5026 &buffer,
5027 &bytesRead);
5029 if (SUCCEEDED(res))
5031 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
5032 return S_OK;
5035 return res;
5038 /******************************************************************************
5039 * SmallBlockChainStream_SetNextBlockInChain
5041 * Writes the index of the next block of the specified block in the small
5042 * block depot.
5043 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
5044 * To flag a block as free use BLOCK_UNUSED as nextBlock.
5046 static void SmallBlockChainStream_SetNextBlockInChain(
5047 SmallBlockChainStream* This,
5048 ULONG blockIndex,
5049 ULONG nextBlock)
5051 ULARGE_INTEGER offsetOfBlockInDepot;
5052 DWORD buffer;
5053 ULONG bytesWritten;
5055 offsetOfBlockInDepot.u.HighPart = 0;
5056 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5058 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
5061 * Read those bytes in the buffer from the small block file.
5063 BlockChainStream_WriteAt(
5064 This->parentStorage->smallBlockDepotChain,
5065 offsetOfBlockInDepot,
5066 sizeof(DWORD),
5067 &buffer,
5068 &bytesWritten);
5071 /******************************************************************************
5072 * SmallBlockChainStream_FreeBlock
5074 * Flag small block 'blockIndex' as free in the small block depot.
5076 static void SmallBlockChainStream_FreeBlock(
5077 SmallBlockChainStream* This,
5078 ULONG blockIndex)
5080 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
5083 /******************************************************************************
5084 * SmallBlockChainStream_GetNextFreeBlock
5086 * Returns the index of a free small block. The small block depot will be
5087 * enlarged if necessary. The small block chain will also be enlarged if
5088 * necessary.
5090 static ULONG SmallBlockChainStream_GetNextFreeBlock(
5091 SmallBlockChainStream* This)
5093 ULARGE_INTEGER offsetOfBlockInDepot;
5094 DWORD buffer;
5095 ULONG bytesRead;
5096 ULONG blockIndex = 0;
5097 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
5098 HRESULT res = S_OK;
5099 ULONG smallBlocksPerBigBlock;
5101 offsetOfBlockInDepot.u.HighPart = 0;
5104 * Scan the small block depot for a free block
5106 while (nextBlockIndex != BLOCK_UNUSED)
5108 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
5110 res = BlockChainStream_ReadAt(
5111 This->parentStorage->smallBlockDepotChain,
5112 offsetOfBlockInDepot,
5113 sizeof(DWORD),
5114 &buffer,
5115 &bytesRead);
5118 * If we run out of space for the small block depot, enlarge it
5120 if (SUCCEEDED(res))
5122 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
5124 if (nextBlockIndex != BLOCK_UNUSED)
5125 blockIndex++;
5127 else
5129 ULONG count =
5130 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
5132 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
5133 ULONG nextBlock, newsbdIndex;
5134 BYTE* smallBlockDepot;
5136 nextBlock = sbdIndex;
5137 while (nextBlock != BLOCK_END_OF_CHAIN)
5139 sbdIndex = nextBlock;
5140 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
5143 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5144 if (sbdIndex != BLOCK_END_OF_CHAIN)
5145 StorageImpl_SetNextBlockInChain(
5146 This->parentStorage,
5147 sbdIndex,
5148 newsbdIndex);
5150 StorageImpl_SetNextBlockInChain(
5151 This->parentStorage,
5152 newsbdIndex,
5153 BLOCK_END_OF_CHAIN);
5156 * Initialize all the small blocks to free
5158 smallBlockDepot =
5159 StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
5161 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
5162 StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
5164 if (count == 0)
5167 * We have just created the small block depot.
5169 StgProperty rootProp;
5170 ULONG sbStartIndex;
5173 * Save it in the header
5175 This->parentStorage->smallBlockDepotStart = newsbdIndex;
5176 StorageImpl_SaveFileHeader(This->parentStorage);
5179 * And allocate the first big block that will contain small blocks
5181 sbStartIndex =
5182 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
5184 StorageImpl_SetNextBlockInChain(
5185 This->parentStorage,
5186 sbStartIndex,
5187 BLOCK_END_OF_CHAIN);
5189 StorageImpl_ReadProperty(
5190 This->parentStorage,
5191 This->parentStorage->base.rootPropertySetIndex,
5192 &rootProp);
5194 rootProp.startingBlock = sbStartIndex;
5195 rootProp.size.u.HighPart = 0;
5196 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
5198 StorageImpl_WriteProperty(
5199 This->parentStorage,
5200 This->parentStorage->base.rootPropertySetIndex,
5201 &rootProp);
5206 smallBlocksPerBigBlock =
5207 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
5210 * Verify if we have to allocate big blocks to contain small blocks
5212 if (blockIndex % smallBlocksPerBigBlock == 0)
5214 StgProperty rootProp;
5215 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
5217 StorageImpl_ReadProperty(
5218 This->parentStorage,
5219 This->parentStorage->base.rootPropertySetIndex,
5220 &rootProp);
5222 if (rootProp.size.u.LowPart <
5223 (blocksRequired * This->parentStorage->bigBlockSize))
5225 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
5227 BlockChainStream_SetSize(
5228 This->parentStorage->smallBlockRootChain,
5229 rootProp.size);
5231 StorageImpl_WriteProperty(
5232 This->parentStorage,
5233 This->parentStorage->base.rootPropertySetIndex,
5234 &rootProp);
5238 return blockIndex;
5241 /******************************************************************************
5242 * SmallBlockChainStream_ReadAt
5244 * Reads a specified number of bytes from this chain at the specified offset.
5245 * bytesRead may be NULL.
5246 * Failure will be returned if the specified number of bytes has not been read.
5248 HRESULT SmallBlockChainStream_ReadAt(
5249 SmallBlockChainStream* This,
5250 ULARGE_INTEGER offset,
5251 ULONG size,
5252 void* buffer,
5253 ULONG* bytesRead)
5255 HRESULT rc = S_OK;
5256 ULARGE_INTEGER offsetInBigBlockFile;
5257 ULONG blockNoInSequence =
5258 offset.u.LowPart / This->parentStorage->smallBlockSize;
5260 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5261 ULONG bytesToReadInBuffer;
5262 ULONG blockIndex;
5263 ULONG bytesReadFromBigBlockFile;
5264 BYTE* bufferWalker;
5267 * This should never happen on a small block file.
5269 assert(offset.u.HighPart==0);
5272 * Find the first block in the stream that contains part of the buffer.
5274 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5276 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5278 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5279 if(FAILED(rc))
5280 return rc;
5281 blockNoInSequence--;
5285 * Start reading the buffer.
5287 *bytesRead = 0;
5288 bufferWalker = buffer;
5290 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5293 * Calculate how many bytes we can copy from this small block.
5295 bytesToReadInBuffer =
5296 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5299 * Calculate the offset of the small block in the small block file.
5301 offsetInBigBlockFile.u.HighPart = 0;
5302 offsetInBigBlockFile.u.LowPart =
5303 blockIndex * This->parentStorage->smallBlockSize;
5305 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5308 * Read those bytes in the buffer from the small block file.
5309 * The small block has already been identified so it shouldn't fail
5310 * unless the file is corrupt.
5312 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5313 offsetInBigBlockFile,
5314 bytesToReadInBuffer,
5315 bufferWalker,
5316 &bytesReadFromBigBlockFile);
5318 if (FAILED(rc))
5319 return rc;
5322 * Step to the next big block.
5324 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5325 if(FAILED(rc))
5326 return STG_E_DOCFILECORRUPT;
5328 bufferWalker += bytesReadFromBigBlockFile;
5329 size -= bytesReadFromBigBlockFile;
5330 *bytesRead += bytesReadFromBigBlockFile;
5331 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5334 return (size == 0) ? S_OK : STG_E_READFAULT;
5337 /******************************************************************************
5338 * SmallBlockChainStream_WriteAt
5340 * Writes the specified number of bytes to this chain at the specified offset.
5341 * bytesWritten may be NULL.
5342 * Will fail if not all specified number of bytes have been written.
5344 HRESULT SmallBlockChainStream_WriteAt(
5345 SmallBlockChainStream* This,
5346 ULARGE_INTEGER offset,
5347 ULONG size,
5348 const void* buffer,
5349 ULONG* bytesWritten)
5351 ULARGE_INTEGER offsetInBigBlockFile;
5352 ULONG blockNoInSequence =
5353 offset.u.LowPart / This->parentStorage->smallBlockSize;
5355 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5356 ULONG bytesToWriteInBuffer;
5357 ULONG blockIndex;
5358 ULONG bytesWrittenToBigBlockFile;
5359 const BYTE* bufferWalker;
5360 HRESULT res;
5363 * This should never happen on a small block file.
5365 assert(offset.u.HighPart==0);
5368 * Find the first block in the stream that contains part of the buffer.
5370 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5372 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5374 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5375 return STG_E_DOCFILECORRUPT;
5376 blockNoInSequence--;
5380 * Start writing the buffer.
5382 * Here, I'm casting away the constness on the buffer variable
5383 * This is OK since we don't intend to modify that buffer.
5385 *bytesWritten = 0;
5386 bufferWalker = (const BYTE*)buffer;
5387 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5390 * Calculate how many bytes we can copy to this small block.
5392 bytesToWriteInBuffer =
5393 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5396 * Calculate the offset of the small block in the small block file.
5398 offsetInBigBlockFile.u.HighPart = 0;
5399 offsetInBigBlockFile.u.LowPart =
5400 blockIndex * This->parentStorage->smallBlockSize;
5402 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5405 * Write those bytes in the buffer to the small block file.
5407 res = BlockChainStream_WriteAt(
5408 This->parentStorage->smallBlockRootChain,
5409 offsetInBigBlockFile,
5410 bytesToWriteInBuffer,
5411 bufferWalker,
5412 &bytesWrittenToBigBlockFile);
5413 if (FAILED(res))
5414 return res;
5417 * Step to the next big block.
5419 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5420 &blockIndex)))
5421 return FALSE;
5422 bufferWalker += bytesWrittenToBigBlockFile;
5423 size -= bytesWrittenToBigBlockFile;
5424 *bytesWritten += bytesWrittenToBigBlockFile;
5425 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5428 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5431 /******************************************************************************
5432 * SmallBlockChainStream_Shrink
5434 * Shrinks this chain in the small block depot.
5436 static BOOL SmallBlockChainStream_Shrink(
5437 SmallBlockChainStream* This,
5438 ULARGE_INTEGER newSize)
5440 ULONG blockIndex, extraBlock;
5441 ULONG numBlocks;
5442 ULONG count = 0;
5444 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5446 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5447 numBlocks++;
5449 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5452 * Go to the new end of chain
5454 while (count < numBlocks)
5456 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5457 &blockIndex)))
5458 return FALSE;
5459 count++;
5463 * If the count is 0, we have a special case, the head of the chain was
5464 * just freed.
5466 if (count == 0)
5468 StgProperty chainProp;
5470 StorageImpl_ReadProperty(This->parentStorage,
5471 This->ownerPropertyIndex,
5472 &chainProp);
5474 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5476 StorageImpl_WriteProperty(This->parentStorage,
5477 This->ownerPropertyIndex,
5478 &chainProp);
5481 * We start freeing the chain at the head block.
5483 extraBlock = blockIndex;
5485 else
5487 /* Get the next block before marking the new end */
5488 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5489 &extraBlock)))
5490 return FALSE;
5492 /* Mark the new end of chain */
5493 SmallBlockChainStream_SetNextBlockInChain(
5494 This,
5495 blockIndex,
5496 BLOCK_END_OF_CHAIN);
5500 * Mark the extra blocks as free
5502 while (extraBlock != BLOCK_END_OF_CHAIN)
5504 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5505 &blockIndex)))
5506 return FALSE;
5507 SmallBlockChainStream_FreeBlock(This, extraBlock);
5508 extraBlock = blockIndex;
5511 return TRUE;
5514 /******************************************************************************
5515 * SmallBlockChainStream_Enlarge
5517 * Grows this chain in the small block depot.
5519 static BOOL SmallBlockChainStream_Enlarge(
5520 SmallBlockChainStream* This,
5521 ULARGE_INTEGER newSize)
5523 ULONG blockIndex, currentBlock;
5524 ULONG newNumBlocks;
5525 ULONG oldNumBlocks = 0;
5527 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5530 * Empty chain
5532 if (blockIndex == BLOCK_END_OF_CHAIN)
5535 StgProperty chainProp;
5537 StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5538 &chainProp);
5540 chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5542 StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5543 &chainProp);
5545 blockIndex = chainProp.startingBlock;
5546 SmallBlockChainStream_SetNextBlockInChain(
5547 This,
5548 blockIndex,
5549 BLOCK_END_OF_CHAIN);
5552 currentBlock = blockIndex;
5555 * Figure out how many blocks are needed to contain this stream
5557 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5559 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5560 newNumBlocks++;
5563 * Go to the current end of chain
5565 while (blockIndex != BLOCK_END_OF_CHAIN)
5567 oldNumBlocks++;
5568 currentBlock = blockIndex;
5569 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5570 return FALSE;
5574 * Add new blocks to the chain
5576 while (oldNumBlocks < newNumBlocks)
5578 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5579 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5581 SmallBlockChainStream_SetNextBlockInChain(
5582 This,
5583 blockIndex,
5584 BLOCK_END_OF_CHAIN);
5586 currentBlock = blockIndex;
5587 oldNumBlocks++;
5590 return TRUE;
5593 /******************************************************************************
5594 * SmallBlockChainStream_SetSize
5596 * Sets the size of this stream.
5597 * The file will grow if we grow the chain.
5599 * TODO: Free the actual blocks in the file when we shrink the chain.
5600 * Currently, the blocks are still in the file. So the file size
5601 * doesn't shrink even if we shrink streams.
5603 BOOL SmallBlockChainStream_SetSize(
5604 SmallBlockChainStream* This,
5605 ULARGE_INTEGER newSize)
5607 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5609 if (newSize.u.LowPart == size.u.LowPart)
5610 return TRUE;
5612 if (newSize.u.LowPart < size.u.LowPart)
5614 SmallBlockChainStream_Shrink(This, newSize);
5616 else
5618 SmallBlockChainStream_Enlarge(This, newSize);
5621 return TRUE;
5624 /******************************************************************************
5625 * SmallBlockChainStream_GetSize
5627 * Returns the size of this chain.
5629 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5631 StgProperty chainProperty;
5633 StorageImpl_ReadProperty(
5634 This->parentStorage,
5635 This->ownerPropertyIndex,
5636 &chainProperty);
5638 return chainProperty.size;
5641 /******************************************************************************
5642 * StgCreateDocfile [OLE32.@]
5643 * Creates a new compound file storage object
5645 * PARAMS
5646 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5647 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5648 * reserved [ ?] unused?, usually 0
5649 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5651 * RETURNS
5652 * S_OK if the file was successfully created
5653 * some STG_E_ value if error
5654 * NOTES
5655 * if pwcsName is NULL, create file with new unique name
5656 * the function can returns
5657 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5658 * (unrealized now)
5660 HRESULT WINAPI StgCreateDocfile(
5661 LPCOLESTR pwcsName,
5662 DWORD grfMode,
5663 DWORD reserved,
5664 IStorage **ppstgOpen)
5666 StorageImpl* newStorage = 0;
5667 HANDLE hFile = INVALID_HANDLE_VALUE;
5668 HRESULT hr = STG_E_INVALIDFLAG;
5669 DWORD shareMode;
5670 DWORD accessMode;
5671 DWORD creationMode;
5672 DWORD fileAttributes;
5673 WCHAR tempFileName[MAX_PATH];
5675 TRACE("(%s, %x, %d, %p)\n",
5676 debugstr_w(pwcsName), grfMode,
5677 reserved, ppstgOpen);
5680 * Validate the parameters
5682 if (ppstgOpen == 0)
5683 return STG_E_INVALIDPOINTER;
5684 if (reserved != 0)
5685 return STG_E_INVALIDPARAMETER;
5688 * Validate the STGM flags
5690 if ( FAILED( validateSTGM(grfMode) ))
5691 goto end;
5693 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5694 switch(STGM_ACCESS_MODE(grfMode))
5696 case STGM_WRITE:
5697 case STGM_READWRITE:
5698 break;
5699 default:
5700 goto end;
5703 /* if no share mode given then DENY_NONE is the default */
5704 if (STGM_SHARE_MODE(grfMode) == 0)
5705 grfMode |= STGM_SHARE_DENY_NONE;
5707 /* must have at least one access mode */
5708 if (STGM_ACCESS_MODE(grfMode) == 0)
5709 goto end;
5711 /* in direct mode, can only use SHARE_EXCLUSIVE */
5712 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5713 goto end;
5715 /* but in transacted mode, any share mode is valid */
5718 * Generate a unique name.
5720 if (pwcsName == 0)
5722 WCHAR tempPath[MAX_PATH];
5723 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5725 memset(tempPath, 0, sizeof(tempPath));
5726 memset(tempFileName, 0, sizeof(tempFileName));
5728 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5729 tempPath[0] = '.';
5731 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5732 pwcsName = tempFileName;
5733 else
5735 hr = STG_E_INSUFFICIENTMEMORY;
5736 goto end;
5739 creationMode = TRUNCATE_EXISTING;
5741 else
5743 creationMode = GetCreationModeFromSTGM(grfMode);
5747 * Interpret the STGM value grfMode
5749 shareMode = GetShareModeFromSTGM(grfMode);
5750 accessMode = GetAccessModeFromSTGM(grfMode);
5752 if (grfMode & STGM_DELETEONRELEASE)
5753 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5754 else
5755 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5757 if (grfMode & STGM_TRANSACTED)
5758 FIXME("Transacted mode not implemented.\n");
5761 * Initialize the "out" parameter.
5763 *ppstgOpen = 0;
5765 hFile = CreateFileW(pwcsName,
5766 accessMode,
5767 shareMode,
5768 NULL,
5769 creationMode,
5770 fileAttributes,
5773 if (hFile == INVALID_HANDLE_VALUE)
5775 if(GetLastError() == ERROR_FILE_EXISTS)
5776 hr = STG_E_FILEALREADYEXISTS;
5777 else
5778 hr = E_FAIL;
5779 goto end;
5783 * Allocate and initialize the new IStorage32object.
5785 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5787 if (newStorage == 0)
5789 hr = STG_E_INSUFFICIENTMEMORY;
5790 goto end;
5793 hr = StorageImpl_Construct(
5794 newStorage,
5795 hFile,
5796 pwcsName,
5797 NULL,
5798 grfMode,
5799 TRUE,
5800 TRUE);
5802 if (FAILED(hr))
5804 HeapFree(GetProcessHeap(), 0, newStorage);
5805 goto end;
5809 * Get an "out" pointer for the caller.
5811 hr = StorageBaseImpl_QueryInterface(
5812 (IStorage*)newStorage,
5813 (REFIID)&IID_IStorage,
5814 (void**)ppstgOpen);
5815 end:
5816 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5818 return hr;
5821 /******************************************************************************
5822 * StgCreateStorageEx [OLE32.@]
5824 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5826 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5827 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5829 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5831 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5832 return STG_E_INVALIDPARAMETER;
5835 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5837 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5838 return STG_E_INVALIDPARAMETER;
5841 if (stgfmt == STGFMT_FILE)
5843 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5844 return STG_E_INVALIDPARAMETER;
5847 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5849 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5850 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5853 ERR("Invalid stgfmt argument\n");
5854 return STG_E_INVALIDPARAMETER;
5857 /******************************************************************************
5858 * StgCreatePropSetStg [OLE32.@]
5860 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5861 IPropertySetStorage **ppPropSetStg)
5863 HRESULT hr;
5865 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5866 if (reserved)
5867 hr = STG_E_INVALIDPARAMETER;
5868 else
5869 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5870 (void**)ppPropSetStg);
5871 return hr;
5874 /******************************************************************************
5875 * StgOpenStorageEx [OLE32.@]
5877 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5879 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5880 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5882 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5884 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5885 return STG_E_INVALIDPARAMETER;
5888 switch (stgfmt)
5890 case STGFMT_FILE:
5891 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5892 return STG_E_INVALIDPARAMETER;
5894 case STGFMT_STORAGE:
5895 break;
5897 case STGFMT_DOCFILE:
5898 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5900 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5901 return STG_E_INVALIDPARAMETER;
5903 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5904 break;
5906 case STGFMT_ANY:
5907 WARN("STGFMT_ANY assuming storage\n");
5908 break;
5910 default:
5911 return STG_E_INVALIDPARAMETER;
5914 return StgOpenStorage(pwcsName, NULL, grfMode, (SNB)NULL, 0, (IStorage **)ppObjectOpen);
5918 /******************************************************************************
5919 * StgOpenStorage [OLE32.@]
5921 HRESULT WINAPI StgOpenStorage(
5922 const OLECHAR *pwcsName,
5923 IStorage *pstgPriority,
5924 DWORD grfMode,
5925 SNB snbExclude,
5926 DWORD reserved,
5927 IStorage **ppstgOpen)
5929 StorageImpl* newStorage = 0;
5930 HRESULT hr = S_OK;
5931 HANDLE hFile = 0;
5932 DWORD shareMode;
5933 DWORD accessMode;
5934 WCHAR fullname[MAX_PATH];
5936 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5937 debugstr_w(pwcsName), pstgPriority, grfMode,
5938 snbExclude, reserved, ppstgOpen);
5941 * Perform sanity checks
5943 if (pwcsName == 0)
5945 hr = STG_E_INVALIDNAME;
5946 goto end;
5949 if (ppstgOpen == 0)
5951 hr = STG_E_INVALIDPOINTER;
5952 goto end;
5955 if (reserved)
5957 hr = STG_E_INVALIDPARAMETER;
5958 goto end;
5961 if (grfMode & STGM_PRIORITY)
5963 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5964 return STG_E_INVALIDFLAG;
5965 if (grfMode & STGM_DELETEONRELEASE)
5966 return STG_E_INVALIDFUNCTION;
5967 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5968 return STG_E_INVALIDFLAG;
5969 grfMode &= ~0xf0; /* remove the existing sharing mode */
5970 grfMode |= STGM_SHARE_DENY_NONE;
5972 /* STGM_PRIORITY stops other IStorage objects on the same file from
5973 * committing until the STGM_PRIORITY IStorage is closed. it also
5974 * stops non-transacted mode StgOpenStorage calls with write access from
5975 * succeeding. obviously, both of these cannot be achieved through just
5976 * file share flags */
5977 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5981 * Validate the sharing mode
5983 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5984 switch(STGM_SHARE_MODE(grfMode))
5986 case STGM_SHARE_EXCLUSIVE:
5987 case STGM_SHARE_DENY_WRITE:
5988 break;
5989 default:
5990 hr = STG_E_INVALIDFLAG;
5991 goto end;
5995 * Validate the STGM flags
5997 if ( FAILED( validateSTGM(grfMode) ) ||
5998 (grfMode&STGM_CREATE))
6000 hr = STG_E_INVALIDFLAG;
6001 goto end;
6004 /* shared reading requires transacted mode */
6005 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
6006 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
6007 !(grfMode&STGM_TRANSACTED) )
6009 hr = STG_E_INVALIDFLAG;
6010 goto end;
6014 * Interpret the STGM value grfMode
6016 shareMode = GetShareModeFromSTGM(grfMode);
6017 accessMode = GetAccessModeFromSTGM(grfMode);
6020 * Initialize the "out" parameter.
6022 *ppstgOpen = 0;
6024 hFile = CreateFileW( pwcsName,
6025 accessMode,
6026 shareMode,
6027 NULL,
6028 OPEN_EXISTING,
6029 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
6032 if (hFile==INVALID_HANDLE_VALUE)
6034 DWORD last_error = GetLastError();
6036 hr = E_FAIL;
6038 switch (last_error)
6040 case ERROR_FILE_NOT_FOUND:
6041 hr = STG_E_FILENOTFOUND;
6042 break;
6044 case ERROR_PATH_NOT_FOUND:
6045 hr = STG_E_PATHNOTFOUND;
6046 break;
6048 case ERROR_ACCESS_DENIED:
6049 case ERROR_WRITE_PROTECT:
6050 hr = STG_E_ACCESSDENIED;
6051 break;
6053 case ERROR_SHARING_VIOLATION:
6054 hr = STG_E_SHAREVIOLATION;
6055 break;
6057 default:
6058 hr = E_FAIL;
6061 goto end;
6065 * Refuse to open the file if it's too small to be a structured storage file
6066 * FIXME: verify the file when reading instead of here
6068 if (GetFileSize(hFile, NULL) < 0x100)
6070 CloseHandle(hFile);
6071 hr = STG_E_FILEALREADYEXISTS;
6072 goto end;
6076 * Allocate and initialize the new IStorage32object.
6078 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6080 if (newStorage == 0)
6082 hr = STG_E_INSUFFICIENTMEMORY;
6083 goto end;
6086 /* Initialize the storage */
6087 hr = StorageImpl_Construct(
6088 newStorage,
6089 hFile,
6090 pwcsName,
6091 NULL,
6092 grfMode,
6093 TRUE,
6094 FALSE );
6096 if (FAILED(hr))
6098 HeapFree(GetProcessHeap(), 0, newStorage);
6100 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
6102 if(hr == STG_E_INVALIDHEADER)
6103 hr = STG_E_FILEALREADYEXISTS;
6104 goto end;
6107 /* prepare the file name string given in lieu of the root property name */
6108 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
6109 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
6110 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
6113 * Get an "out" pointer for the caller.
6115 hr = StorageBaseImpl_QueryInterface(
6116 (IStorage*)newStorage,
6117 (REFIID)&IID_IStorage,
6118 (void**)ppstgOpen);
6120 end:
6121 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
6122 return hr;
6125 /******************************************************************************
6126 * StgCreateDocfileOnILockBytes [OLE32.@]
6128 HRESULT WINAPI StgCreateDocfileOnILockBytes(
6129 ILockBytes *plkbyt,
6130 DWORD grfMode,
6131 DWORD reserved,
6132 IStorage** ppstgOpen)
6134 StorageImpl* newStorage = 0;
6135 HRESULT hr = S_OK;
6138 * Validate the parameters
6140 if ((ppstgOpen == 0) || (plkbyt == 0))
6141 return STG_E_INVALIDPOINTER;
6144 * Allocate and initialize the new IStorage object.
6146 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6148 if (newStorage == 0)
6149 return STG_E_INSUFFICIENTMEMORY;
6151 hr = StorageImpl_Construct(
6152 newStorage,
6155 plkbyt,
6156 grfMode,
6157 FALSE,
6158 TRUE);
6160 if (FAILED(hr))
6162 HeapFree(GetProcessHeap(), 0, newStorage);
6163 return hr;
6167 * Get an "out" pointer for the caller.
6169 hr = StorageBaseImpl_QueryInterface(
6170 (IStorage*)newStorage,
6171 (REFIID)&IID_IStorage,
6172 (void**)ppstgOpen);
6174 return hr;
6177 /******************************************************************************
6178 * StgOpenStorageOnILockBytes [OLE32.@]
6180 HRESULT WINAPI StgOpenStorageOnILockBytes(
6181 ILockBytes *plkbyt,
6182 IStorage *pstgPriority,
6183 DWORD grfMode,
6184 SNB snbExclude,
6185 DWORD reserved,
6186 IStorage **ppstgOpen)
6188 StorageImpl* newStorage = 0;
6189 HRESULT hr = S_OK;
6192 * Perform a sanity check
6194 if ((plkbyt == 0) || (ppstgOpen == 0))
6195 return STG_E_INVALIDPOINTER;
6198 * Validate the STGM flags
6200 if ( FAILED( validateSTGM(grfMode) ))
6201 return STG_E_INVALIDFLAG;
6204 * Initialize the "out" parameter.
6206 *ppstgOpen = 0;
6209 * Allocate and initialize the new IStorage object.
6211 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
6213 if (newStorage == 0)
6214 return STG_E_INSUFFICIENTMEMORY;
6216 hr = StorageImpl_Construct(
6217 newStorage,
6220 plkbyt,
6221 grfMode,
6222 FALSE,
6223 FALSE);
6225 if (FAILED(hr))
6227 HeapFree(GetProcessHeap(), 0, newStorage);
6228 return hr;
6232 * Get an "out" pointer for the caller.
6234 hr = StorageBaseImpl_QueryInterface(
6235 (IStorage*)newStorage,
6236 (REFIID)&IID_IStorage,
6237 (void**)ppstgOpen);
6239 return hr;
6242 /******************************************************************************
6243 * StgSetTimes [ole32.@]
6244 * StgSetTimes [OLE32.@]
6248 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
6249 FILETIME const *patime, FILETIME const *pmtime)
6251 IStorage *stg = NULL;
6252 HRESULT r;
6254 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
6256 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
6257 0, 0, &stg);
6258 if( SUCCEEDED(r) )
6260 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
6261 IStorage_Release(stg);
6264 return r;
6267 /******************************************************************************
6268 * StgIsStorageILockBytes [OLE32.@]
6270 * Determines if the ILockBytes contains a storage object.
6272 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
6274 BYTE sig[8];
6275 ULARGE_INTEGER offset;
6277 offset.u.HighPart = 0;
6278 offset.u.LowPart = 0;
6280 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6282 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6283 return S_OK;
6285 return S_FALSE;
6288 /******************************************************************************
6289 * WriteClassStg [OLE32.@]
6291 * This method will store the specified CLSID in the specified storage object
6293 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6295 HRESULT hRes;
6297 if(!pStg)
6298 return E_INVALIDARG;
6300 hRes = IStorage_SetClass(pStg, rclsid);
6302 return hRes;
6305 /***********************************************************************
6306 * ReadClassStg (OLE32.@)
6308 * This method reads the CLSID previously written to a storage object with
6309 * the WriteClassStg.
6311 * PARAMS
6312 * pstg [I] IStorage pointer
6313 * pclsid [O] Pointer to where the CLSID is written
6315 * RETURNS
6316 * Success: S_OK.
6317 * Failure: HRESULT code.
6319 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6321 STATSTG pstatstg;
6322 HRESULT hRes;
6324 TRACE("(%p, %p)\n", pstg, pclsid);
6326 if(!pstg || !pclsid)
6327 return E_INVALIDARG;
6330 * read a STATSTG structure (contains the clsid) from the storage
6332 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
6334 if(SUCCEEDED(hRes))
6335 *pclsid=pstatstg.clsid;
6337 return hRes;
6340 /***********************************************************************
6341 * OleLoadFromStream (OLE32.@)
6343 * This function loads an object from stream
6345 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6347 CLSID clsid;
6348 HRESULT res;
6349 LPPERSISTSTREAM xstm;
6351 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6353 res=ReadClassStm(pStm,&clsid);
6354 if (!SUCCEEDED(res))
6355 return res;
6356 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6357 if (!SUCCEEDED(res))
6358 return res;
6359 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6360 if (!SUCCEEDED(res)) {
6361 IUnknown_Release((IUnknown*)*ppvObj);
6362 return res;
6364 res=IPersistStream_Load(xstm,pStm);
6365 IPersistStream_Release(xstm);
6366 /* FIXME: all refcounts ok at this point? I think they should be:
6367 * pStm : unchanged
6368 * ppvObj : 1
6369 * xstm : 0 (released)
6371 return res;
6374 /***********************************************************************
6375 * OleSaveToStream (OLE32.@)
6377 * This function saves an object with the IPersistStream interface on it
6378 * to the specified stream.
6380 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6383 CLSID clsid;
6384 HRESULT res;
6386 TRACE("(%p,%p)\n",pPStm,pStm);
6388 res=IPersistStream_GetClassID(pPStm,&clsid);
6390 if (SUCCEEDED(res)){
6392 res=WriteClassStm(pStm,&clsid);
6394 if (SUCCEEDED(res))
6396 res=IPersistStream_Save(pPStm,pStm,TRUE);
6399 TRACE("Finished Save\n");
6400 return res;
6403 /****************************************************************************
6404 * This method validate a STGM parameter that can contain the values below
6406 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6407 * The stgm values contained in 0xffff0000 are bitmasks.
6409 * STGM_DIRECT 0x00000000
6410 * STGM_TRANSACTED 0x00010000
6411 * STGM_SIMPLE 0x08000000
6413 * STGM_READ 0x00000000
6414 * STGM_WRITE 0x00000001
6415 * STGM_READWRITE 0x00000002
6417 * STGM_SHARE_DENY_NONE 0x00000040
6418 * STGM_SHARE_DENY_READ 0x00000030
6419 * STGM_SHARE_DENY_WRITE 0x00000020
6420 * STGM_SHARE_EXCLUSIVE 0x00000010
6422 * STGM_PRIORITY 0x00040000
6423 * STGM_DELETEONRELEASE 0x04000000
6425 * STGM_CREATE 0x00001000
6426 * STGM_CONVERT 0x00020000
6427 * STGM_FAILIFTHERE 0x00000000
6429 * STGM_NOSCRATCH 0x00100000
6430 * STGM_NOSNAPSHOT 0x00200000
6432 static HRESULT validateSTGM(DWORD stgm)
6434 DWORD access = STGM_ACCESS_MODE(stgm);
6435 DWORD share = STGM_SHARE_MODE(stgm);
6436 DWORD create = STGM_CREATE_MODE(stgm);
6438 if (stgm&~STGM_KNOWN_FLAGS)
6440 ERR("unknown flags %08x\n", stgm);
6441 return E_FAIL;
6444 switch (access)
6446 case STGM_READ:
6447 case STGM_WRITE:
6448 case STGM_READWRITE:
6449 break;
6450 default:
6451 return E_FAIL;
6454 switch (share)
6456 case STGM_SHARE_DENY_NONE:
6457 case STGM_SHARE_DENY_READ:
6458 case STGM_SHARE_DENY_WRITE:
6459 case STGM_SHARE_EXCLUSIVE:
6460 break;
6461 default:
6462 return E_FAIL;
6465 switch (create)
6467 case STGM_CREATE:
6468 case STGM_FAILIFTHERE:
6469 break;
6470 default:
6471 return E_FAIL;
6475 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6477 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6478 return E_FAIL;
6481 * STGM_CREATE | STGM_CONVERT
6482 * if both are false, STGM_FAILIFTHERE is set to TRUE
6484 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6485 return E_FAIL;
6488 * STGM_NOSCRATCH requires STGM_TRANSACTED
6490 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6491 return E_FAIL;
6494 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6495 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6497 if ( (stgm & STGM_NOSNAPSHOT) &&
6498 (!(stgm & STGM_TRANSACTED) ||
6499 share == STGM_SHARE_EXCLUSIVE ||
6500 share == STGM_SHARE_DENY_WRITE) )
6501 return E_FAIL;
6503 return S_OK;
6506 /****************************************************************************
6507 * GetShareModeFromSTGM
6509 * This method will return a share mode flag from a STGM value.
6510 * The STGM value is assumed valid.
6512 static DWORD GetShareModeFromSTGM(DWORD stgm)
6514 switch (STGM_SHARE_MODE(stgm))
6516 case STGM_SHARE_DENY_NONE:
6517 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6518 case STGM_SHARE_DENY_READ:
6519 return FILE_SHARE_WRITE;
6520 case STGM_SHARE_DENY_WRITE:
6521 return FILE_SHARE_READ;
6522 case STGM_SHARE_EXCLUSIVE:
6523 return 0;
6525 ERR("Invalid share mode!\n");
6526 assert(0);
6527 return 0;
6530 /****************************************************************************
6531 * GetAccessModeFromSTGM
6533 * This method will return an access mode flag from a STGM value.
6534 * The STGM value is assumed valid.
6536 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6538 switch (STGM_ACCESS_MODE(stgm))
6540 case STGM_READ:
6541 return GENERIC_READ;
6542 case STGM_WRITE:
6543 case STGM_READWRITE:
6544 return GENERIC_READ | GENERIC_WRITE;
6546 ERR("Invalid access mode!\n");
6547 assert(0);
6548 return 0;
6551 /****************************************************************************
6552 * GetCreationModeFromSTGM
6554 * This method will return a creation mode flag from a STGM value.
6555 * The STGM value is assumed valid.
6557 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6559 switch(STGM_CREATE_MODE(stgm))
6561 case STGM_CREATE:
6562 return CREATE_ALWAYS;
6563 case STGM_CONVERT:
6564 FIXME("STGM_CONVERT not implemented!\n");
6565 return CREATE_NEW;
6566 case STGM_FAILIFTHERE:
6567 return CREATE_NEW;
6569 ERR("Invalid create mode!\n");
6570 assert(0);
6571 return 0;
6575 /*************************************************************************
6576 * OLECONVERT_LoadOLE10 [Internal]
6578 * Loads the OLE10 STREAM to memory
6580 * PARAMS
6581 * pOleStream [I] The OLESTREAM
6582 * pData [I] Data Structure for the OLESTREAM Data
6584 * RETURNS
6585 * Success: S_OK
6586 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6587 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6589 * NOTES
6590 * This function is used by OleConvertOLESTREAMToIStorage only.
6592 * Memory allocated for pData must be freed by the caller
6594 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6596 DWORD dwSize;
6597 HRESULT hRes = S_OK;
6598 int nTryCnt=0;
6599 int max_try = 6;
6601 pData->pData = NULL;
6602 pData->pstrOleObjFileName = (CHAR *) NULL;
6604 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6606 /* Get the OleID */
6607 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6608 if(dwSize != sizeof(pData->dwOleID))
6610 hRes = CONVERT10_E_OLESTREAM_GET;
6612 else if(pData->dwOleID != OLESTREAM_ID)
6614 hRes = CONVERT10_E_OLESTREAM_FMT;
6616 else
6618 hRes = S_OK;
6619 break;
6623 if(hRes == S_OK)
6625 /* Get the TypeID...more info needed for this field */
6626 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6627 if(dwSize != sizeof(pData->dwTypeID))
6629 hRes = CONVERT10_E_OLESTREAM_GET;
6632 if(hRes == S_OK)
6634 if(pData->dwTypeID != 0)
6636 /* Get the length of the OleTypeName */
6637 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6638 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6640 hRes = CONVERT10_E_OLESTREAM_GET;
6643 if(hRes == S_OK)
6645 if(pData->dwOleTypeNameLength > 0)
6647 /* Get the OleTypeName */
6648 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6649 if(dwSize != pData->dwOleTypeNameLength)
6651 hRes = CONVERT10_E_OLESTREAM_GET;
6655 if(bStrem1)
6657 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6658 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6660 hRes = CONVERT10_E_OLESTREAM_GET;
6662 if(hRes == S_OK)
6664 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6665 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6666 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6667 if(pData->pstrOleObjFileName)
6669 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6670 if(dwSize != pData->dwOleObjFileNameLength)
6672 hRes = CONVERT10_E_OLESTREAM_GET;
6675 else
6676 hRes = CONVERT10_E_OLESTREAM_GET;
6679 else
6681 /* Get the Width of the Metafile */
6682 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6683 if(dwSize != sizeof(pData->dwMetaFileWidth))
6685 hRes = CONVERT10_E_OLESTREAM_GET;
6687 if(hRes == S_OK)
6689 /* Get the Height of the Metafile */
6690 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6691 if(dwSize != sizeof(pData->dwMetaFileHeight))
6693 hRes = CONVERT10_E_OLESTREAM_GET;
6697 if(hRes == S_OK)
6699 /* Get the Length of the Data */
6700 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6701 if(dwSize != sizeof(pData->dwDataLength))
6703 hRes = CONVERT10_E_OLESTREAM_GET;
6707 if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6709 if(!bStrem1) /* if it is a second OLE stream data */
6711 pData->dwDataLength -= 8;
6712 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6713 if(dwSize != sizeof(pData->strUnknown))
6715 hRes = CONVERT10_E_OLESTREAM_GET;
6719 if(hRes == S_OK)
6721 if(pData->dwDataLength > 0)
6723 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6725 /* Get Data (ex. IStorage, Metafile, or BMP) */
6726 if(pData->pData)
6728 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6729 if(dwSize != pData->dwDataLength)
6731 hRes = CONVERT10_E_OLESTREAM_GET;
6734 else
6736 hRes = CONVERT10_E_OLESTREAM_GET;
6742 return hRes;
6745 /*************************************************************************
6746 * OLECONVERT_SaveOLE10 [Internal]
6748 * Saves the OLE10 STREAM From memory
6750 * PARAMS
6751 * pData [I] Data Structure for the OLESTREAM Data
6752 * pOleStream [I] The OLESTREAM to save
6754 * RETURNS
6755 * Success: S_OK
6756 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6758 * NOTES
6759 * This function is used by OleConvertIStorageToOLESTREAM only.
6762 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6764 DWORD dwSize;
6765 HRESULT hRes = S_OK;
6768 /* Set the OleID */
6769 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6770 if(dwSize != sizeof(pData->dwOleID))
6772 hRes = CONVERT10_E_OLESTREAM_PUT;
6775 if(hRes == S_OK)
6777 /* Set the TypeID */
6778 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6779 if(dwSize != sizeof(pData->dwTypeID))
6781 hRes = CONVERT10_E_OLESTREAM_PUT;
6785 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6787 /* Set the Length of the OleTypeName */
6788 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6789 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6791 hRes = CONVERT10_E_OLESTREAM_PUT;
6794 if(hRes == S_OK)
6796 if(pData->dwOleTypeNameLength > 0)
6798 /* Set the OleTypeName */
6799 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->strOleTypeName, pData->dwOleTypeNameLength);
6800 if(dwSize != pData->dwOleTypeNameLength)
6802 hRes = CONVERT10_E_OLESTREAM_PUT;
6807 if(hRes == S_OK)
6809 /* Set the width of the Metafile */
6810 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6811 if(dwSize != sizeof(pData->dwMetaFileWidth))
6813 hRes = CONVERT10_E_OLESTREAM_PUT;
6817 if(hRes == S_OK)
6819 /* Set the height of the Metafile */
6820 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6821 if(dwSize != sizeof(pData->dwMetaFileHeight))
6823 hRes = CONVERT10_E_OLESTREAM_PUT;
6827 if(hRes == S_OK)
6829 /* Set the length of the Data */
6830 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6831 if(dwSize != sizeof(pData->dwDataLength))
6833 hRes = CONVERT10_E_OLESTREAM_PUT;
6837 if(hRes == S_OK)
6839 if(pData->dwDataLength > 0)
6841 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6842 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6843 if(dwSize != pData->dwDataLength)
6845 hRes = CONVERT10_E_OLESTREAM_PUT;
6850 return hRes;
6853 /*************************************************************************
6854 * OLECONVERT_GetOLE20FromOLE10[Internal]
6856 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6857 * opens it, and copies the content to the dest IStorage for
6858 * OleConvertOLESTREAMToIStorage
6861 * PARAMS
6862 * pDestStorage [I] The IStorage to copy the data to
6863 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6864 * nBufferLength [I] The size of the buffer
6866 * RETURNS
6867 * Nothing
6869 * NOTES
6873 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6875 HRESULT hRes;
6876 HANDLE hFile;
6877 IStorage *pTempStorage;
6878 DWORD dwNumOfBytesWritten;
6879 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6880 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6882 /* Create a temp File */
6883 GetTempPathW(MAX_PATH, wstrTempDir);
6884 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6885 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6887 if(hFile != INVALID_HANDLE_VALUE)
6889 /* Write IStorage Data to File */
6890 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6891 CloseHandle(hFile);
6893 /* Open and copy temp storage to the Dest Storage */
6894 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6895 if(hRes == S_OK)
6897 hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6898 StorageBaseImpl_Release(pTempStorage);
6900 DeleteFileW(wstrTempFile);
6905 /*************************************************************************
6906 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6908 * Saves the OLE10 STREAM From memory
6910 * PARAMS
6911 * pStorage [I] The Src IStorage to copy
6912 * pData [I] The Dest Memory to write to.
6914 * RETURNS
6915 * The size in bytes allocated for pData
6917 * NOTES
6918 * Memory allocated for pData must be freed by the caller
6920 * Used by OleConvertIStorageToOLESTREAM only.
6923 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6925 HANDLE hFile;
6926 HRESULT hRes;
6927 DWORD nDataLength = 0;
6928 IStorage *pTempStorage;
6929 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6930 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6932 *pData = NULL;
6934 /* Create temp Storage */
6935 GetTempPathW(MAX_PATH, wstrTempDir);
6936 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6937 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6939 if(hRes == S_OK)
6941 /* Copy Src Storage to the Temp Storage */
6942 StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6943 StorageBaseImpl_Release(pTempStorage);
6945 /* Open Temp Storage as a file and copy to memory */
6946 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6947 if(hFile != INVALID_HANDLE_VALUE)
6949 nDataLength = GetFileSize(hFile, NULL);
6950 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6951 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6952 CloseHandle(hFile);
6954 DeleteFileW(wstrTempFile);
6956 return nDataLength;
6959 /*************************************************************************
6960 * OLECONVERT_CreateOleStream [Internal]
6962 * Creates the "\001OLE" stream in the IStorage if necessary.
6964 * PARAMS
6965 * pStorage [I] Dest storage to create the stream in
6967 * RETURNS
6968 * Nothing
6970 * NOTES
6971 * This function is used by OleConvertOLESTREAMToIStorage only.
6973 * This stream is still unknown, MS Word seems to have extra data
6974 * but since the data is stored in the OLESTREAM there should be
6975 * no need to recreate the stream. If the stream is manually
6976 * deleted it will create it with this default data.
6979 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6981 HRESULT hRes;
6982 IStream *pStream;
6983 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6984 BYTE pOleStreamHeader [] =
6986 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6987 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6988 0x00, 0x00, 0x00, 0x00
6991 /* Create stream if not present */
6992 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6993 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6995 if(hRes == S_OK)
6997 /* Write default Data */
6998 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6999 IStream_Release(pStream);
7003 /* write a string to a stream, preceded by its length */
7004 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
7006 HRESULT r;
7007 LPSTR str;
7008 DWORD len = 0;
7010 if( string )
7011 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
7012 r = IStream_Write( stm, &len, sizeof(len), NULL);
7013 if( FAILED( r ) )
7014 return r;
7015 if(len == 0)
7016 return r;
7017 str = CoTaskMemAlloc( len );
7018 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
7019 r = IStream_Write( stm, str, len, NULL);
7020 CoTaskMemFree( str );
7021 return r;
7024 /* read a string preceded by its length from a stream */
7025 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
7027 HRESULT r;
7028 DWORD len, count = 0;
7029 LPSTR str;
7030 LPWSTR wstr;
7032 r = IStream_Read( stm, &len, sizeof(len), &count );
7033 if( FAILED( r ) )
7034 return r;
7035 if( count != sizeof(len) )
7036 return E_OUTOFMEMORY;
7038 TRACE("%d bytes\n",len);
7040 str = CoTaskMemAlloc( len );
7041 if( !str )
7042 return E_OUTOFMEMORY;
7043 count = 0;
7044 r = IStream_Read( stm, str, len, &count );
7045 if( FAILED( r ) )
7046 return r;
7047 if( count != len )
7049 CoTaskMemFree( str );
7050 return E_OUTOFMEMORY;
7053 TRACE("Read string %s\n",debugstr_an(str,len));
7055 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
7056 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
7057 if( wstr )
7058 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
7059 CoTaskMemFree( str );
7061 *string = wstr;
7063 return r;
7067 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
7068 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
7070 IStream *pstm;
7071 HRESULT r = S_OK;
7072 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7074 static const BYTE unknown1[12] =
7075 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
7076 0xFF, 0xFF, 0xFF, 0xFF};
7077 static const BYTE unknown2[16] =
7078 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
7079 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
7081 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
7082 debugstr_w(lpszUserType), debugstr_w(szClipName),
7083 debugstr_w(szProgIDName));
7085 /* Create a CompObj stream if it doesn't exist */
7086 r = IStorage_CreateStream(pstg, szwStreamName,
7087 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
7088 if( FAILED (r) )
7089 return r;
7091 /* Write CompObj Structure to stream */
7092 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
7094 if( SUCCEEDED( r ) )
7095 r = WriteClassStm( pstm, clsid );
7097 if( SUCCEEDED( r ) )
7098 r = STREAM_WriteString( pstm, lpszUserType );
7099 if( SUCCEEDED( r ) )
7100 r = STREAM_WriteString( pstm, szClipName );
7101 if( SUCCEEDED( r ) )
7102 r = STREAM_WriteString( pstm, szProgIDName );
7103 if( SUCCEEDED( r ) )
7104 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
7106 IStream_Release( pstm );
7108 return r;
7111 /***********************************************************************
7112 * WriteFmtUserTypeStg (OLE32.@)
7114 HRESULT WINAPI WriteFmtUserTypeStg(
7115 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
7117 HRESULT r;
7118 WCHAR szwClipName[0x40];
7119 CLSID clsid = CLSID_NULL;
7120 LPWSTR wstrProgID = NULL;
7121 DWORD n;
7123 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
7125 /* get the clipboard format name */
7126 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
7127 szwClipName[n]=0;
7129 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
7131 /* FIXME: There's room to save a CLSID and its ProgID, but
7132 the CLSID is not looked up in the registry and in all the
7133 tests I wrote it was CLSID_NULL. Where does it come from?
7136 /* get the real program ID. This may fail, but that's fine */
7137 ProgIDFromCLSID(&clsid, &wstrProgID);
7139 TRACE("progid is %s\n",debugstr_w(wstrProgID));
7141 r = STORAGE_WriteCompObj( pstg, &clsid,
7142 lpszUserType, szwClipName, wstrProgID );
7144 CoTaskMemFree(wstrProgID);
7146 return r;
7150 /******************************************************************************
7151 * ReadFmtUserTypeStg [OLE32.@]
7153 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
7155 HRESULT r;
7156 IStream *stm = 0;
7157 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
7158 unsigned char unknown1[12];
7159 unsigned char unknown2[16];
7160 DWORD count;
7161 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
7162 CLSID clsid;
7164 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
7166 r = IStorage_OpenStream( pstg, szCompObj, NULL,
7167 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
7168 if( FAILED ( r ) )
7170 WARN("Failed to open stream r = %08x\n", r);
7171 return r;
7174 /* read the various parts of the structure */
7175 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
7176 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
7177 goto end;
7178 r = ReadClassStm( stm, &clsid );
7179 if( FAILED( r ) )
7180 goto end;
7182 r = STREAM_ReadString( stm, &szCLSIDName );
7183 if( FAILED( r ) )
7184 goto end;
7186 r = STREAM_ReadString( stm, &szOleTypeName );
7187 if( FAILED( r ) )
7188 goto end;
7190 r = STREAM_ReadString( stm, &szProgIDName );
7191 if( FAILED( r ) )
7192 goto end;
7194 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
7195 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
7196 goto end;
7198 /* ok, success... now we just need to store what we found */
7199 if( pcf )
7200 *pcf = RegisterClipboardFormatW( szOleTypeName );
7201 CoTaskMemFree( szOleTypeName );
7203 if( lplpszUserType )
7204 *lplpszUserType = szCLSIDName;
7205 CoTaskMemFree( szProgIDName );
7207 end:
7208 IStream_Release( stm );
7210 return r;
7214 /*************************************************************************
7215 * OLECONVERT_CreateCompObjStream [Internal]
7217 * Creates a "\001CompObj" is the destination IStorage if necessary.
7219 * PARAMS
7220 * pStorage [I] The dest IStorage to create the CompObj Stream
7221 * if necessary.
7222 * strOleTypeName [I] The ProgID
7224 * RETURNS
7225 * Success: S_OK
7226 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7228 * NOTES
7229 * This function is used by OleConvertOLESTREAMToIStorage only.
7231 * The stream data is stored in the OLESTREAM and there should be
7232 * no need to recreate the stream. If the stream is manually
7233 * deleted it will attempt to create it by querying the registry.
7237 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
7239 IStream *pStream;
7240 HRESULT hStorageRes, hRes = S_OK;
7241 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
7242 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7243 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
7245 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
7246 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
7248 /* Initialize the CompObj structure */
7249 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
7250 memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
7251 memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
7254 /* Create a CompObj stream if it doesn't exist */
7255 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
7256 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7257 if(hStorageRes == S_OK)
7259 /* copy the OleTypeName to the compobj struct */
7260 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
7261 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
7263 /* copy the OleTypeName to the compobj struct */
7264 /* Note: in the test made, these were Identical */
7265 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
7266 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
7268 /* Get the CLSID */
7269 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
7270 bufferW, OLESTREAM_MAX_STR_LEN );
7271 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
7273 if(hRes == S_OK)
7275 HKEY hKey;
7276 LONG hErr;
7277 /* Get the CLSID Default Name from the Registry */
7278 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7279 if(hErr == ERROR_SUCCESS)
7281 char strTemp[OLESTREAM_MAX_STR_LEN];
7282 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7283 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7284 if(hErr == ERROR_SUCCESS)
7286 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7288 RegCloseKey(hKey);
7292 /* Write CompObj Structure to stream */
7293 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7295 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7297 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7298 if(IStorageCompObj.dwCLSIDNameLength > 0)
7300 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7302 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7303 if(IStorageCompObj.dwOleTypeNameLength > 0)
7305 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7307 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7308 if(IStorageCompObj.dwProgIDNameLength > 0)
7310 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7312 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7313 IStream_Release(pStream);
7315 return hRes;
7319 /*************************************************************************
7320 * OLECONVERT_CreateOlePresStream[Internal]
7322 * Creates the "\002OlePres000" Stream with the Metafile data
7324 * PARAMS
7325 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7326 * dwExtentX [I] Width of the Metafile
7327 * dwExtentY [I] Height of the Metafile
7328 * pData [I] Metafile data
7329 * dwDataLength [I] Size of the Metafile data
7331 * RETURNS
7332 * Success: S_OK
7333 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7335 * NOTES
7336 * This function is used by OleConvertOLESTREAMToIStorage only.
7339 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7341 HRESULT hRes;
7342 IStream *pStream;
7343 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7344 BYTE pOlePresStreamHeader [] =
7346 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7347 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7348 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7349 0x00, 0x00, 0x00, 0x00
7352 BYTE pOlePresStreamHeaderEmpty [] =
7354 0x00, 0x00, 0x00, 0x00,
7355 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7356 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7357 0x00, 0x00, 0x00, 0x00
7360 /* Create the OlePres000 Stream */
7361 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7362 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7364 if(hRes == S_OK)
7366 DWORD nHeaderSize;
7367 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7369 memset(&OlePres, 0, sizeof(OlePres));
7370 /* Do we have any metafile data to save */
7371 if(dwDataLength > 0)
7373 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7374 nHeaderSize = sizeof(pOlePresStreamHeader);
7376 else
7378 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7379 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7381 /* Set width and height of the metafile */
7382 OlePres.dwExtentX = dwExtentX;
7383 OlePres.dwExtentY = -dwExtentY;
7385 /* Set Data and Length */
7386 if(dwDataLength > sizeof(METAFILEPICT16))
7388 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7389 OlePres.pData = &(pData[8]);
7391 /* Save OlePres000 Data to Stream */
7392 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7393 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7394 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7395 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7396 if(OlePres.dwSize > 0)
7398 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7400 IStream_Release(pStream);
7404 /*************************************************************************
7405 * OLECONVERT_CreateOle10NativeStream [Internal]
7407 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7409 * PARAMS
7410 * pStorage [I] Dest storage to create the stream in
7411 * pData [I] Ole10 Native Data (ex. bmp)
7412 * dwDataLength [I] Size of the Ole10 Native Data
7414 * RETURNS
7415 * Nothing
7417 * NOTES
7418 * This function is used by OleConvertOLESTREAMToIStorage only.
7420 * Might need to verify the data and return appropriate error message
7423 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7425 HRESULT hRes;
7426 IStream *pStream;
7427 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7429 /* Create the Ole10Native Stream */
7430 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7431 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7433 if(hRes == S_OK)
7435 /* Write info to stream */
7436 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7437 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7438 IStream_Release(pStream);
7443 /*************************************************************************
7444 * OLECONVERT_GetOLE10ProgID [Internal]
7446 * Finds the ProgID (or OleTypeID) from the IStorage
7448 * PARAMS
7449 * pStorage [I] The Src IStorage to get the ProgID
7450 * strProgID [I] the ProgID string to get
7451 * dwSize [I] the size of the string
7453 * RETURNS
7454 * Success: S_OK
7455 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7457 * NOTES
7458 * This function is used by OleConvertIStorageToOLESTREAM only.
7462 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7464 HRESULT hRes;
7465 IStream *pStream;
7466 LARGE_INTEGER iSeekPos;
7467 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7468 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7470 /* Open the CompObj Stream */
7471 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7472 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7473 if(hRes == S_OK)
7476 /*Get the OleType from the CompObj Stream */
7477 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7478 iSeekPos.u.HighPart = 0;
7480 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7481 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7482 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7483 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7484 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7485 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7486 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7488 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7489 if(*dwSize > 0)
7491 IStream_Read(pStream, strProgID, *dwSize, NULL);
7493 IStream_Release(pStream);
7495 else
7497 STATSTG stat;
7498 LPOLESTR wstrProgID;
7500 /* Get the OleType from the registry */
7501 REFCLSID clsid = &(stat.clsid);
7502 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7503 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7504 if(hRes == S_OK)
7506 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7510 return hRes;
7513 /*************************************************************************
7514 * OLECONVERT_GetOle10PresData [Internal]
7516 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7518 * PARAMS
7519 * pStorage [I] Src IStroage
7520 * pOleStream [I] Dest OleStream Mem Struct
7522 * RETURNS
7523 * Nothing
7525 * NOTES
7526 * This function is used by OleConvertIStorageToOLESTREAM only.
7528 * Memory allocated for pData must be freed by the caller
7532 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7535 HRESULT hRes;
7536 IStream *pStream;
7537 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7539 /* Initialize Default data for OLESTREAM */
7540 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7541 pOleStreamData[0].dwTypeID = 2;
7542 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7543 pOleStreamData[1].dwTypeID = 0;
7544 pOleStreamData[0].dwMetaFileWidth = 0;
7545 pOleStreamData[0].dwMetaFileHeight = 0;
7546 pOleStreamData[0].pData = NULL;
7547 pOleStreamData[1].pData = NULL;
7549 /* Open Ole10Native Stream */
7550 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7551 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7552 if(hRes == S_OK)
7555 /* Read Size and Data */
7556 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7557 if(pOleStreamData->dwDataLength > 0)
7559 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7560 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7562 IStream_Release(pStream);
7568 /*************************************************************************
7569 * OLECONVERT_GetOle20PresData[Internal]
7571 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7573 * PARAMS
7574 * pStorage [I] Src IStroage
7575 * pOleStreamData [I] Dest OleStream Mem Struct
7577 * RETURNS
7578 * Nothing
7580 * NOTES
7581 * This function is used by OleConvertIStorageToOLESTREAM only.
7583 * Memory allocated for pData must be freed by the caller
7585 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7587 HRESULT hRes;
7588 IStream *pStream;
7589 OLECONVERT_ISTORAGE_OLEPRES olePress;
7590 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7592 /* Initialize Default data for OLESTREAM */
7593 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7594 pOleStreamData[0].dwTypeID = 2;
7595 pOleStreamData[0].dwMetaFileWidth = 0;
7596 pOleStreamData[0].dwMetaFileHeight = 0;
7597 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7598 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7599 pOleStreamData[1].dwTypeID = 0;
7600 pOleStreamData[1].dwOleTypeNameLength = 0;
7601 pOleStreamData[1].strOleTypeName[0] = 0;
7602 pOleStreamData[1].dwMetaFileWidth = 0;
7603 pOleStreamData[1].dwMetaFileHeight = 0;
7604 pOleStreamData[1].pData = NULL;
7605 pOleStreamData[1].dwDataLength = 0;
7608 /* Open OlePress000 stream */
7609 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7610 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7611 if(hRes == S_OK)
7613 LARGE_INTEGER iSeekPos;
7614 METAFILEPICT16 MetaFilePict;
7615 static const char strMetafilePictName[] = "METAFILEPICT";
7617 /* Set the TypeID for a Metafile */
7618 pOleStreamData[1].dwTypeID = 5;
7620 /* Set the OleTypeName to Metafile */
7621 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7622 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7624 iSeekPos.u.HighPart = 0;
7625 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7627 /* Get Presentation Data */
7628 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7629 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7630 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7631 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7633 /*Set width and Height */
7634 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7635 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7636 if(olePress.dwSize > 0)
7638 /* Set Length */
7639 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7641 /* Set MetaFilePict struct */
7642 MetaFilePict.mm = 8;
7643 MetaFilePict.xExt = olePress.dwExtentX;
7644 MetaFilePict.yExt = olePress.dwExtentY;
7645 MetaFilePict.hMF = 0;
7647 /* Get Metafile Data */
7648 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7649 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7650 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7652 IStream_Release(pStream);
7656 /*************************************************************************
7657 * OleConvertOLESTREAMToIStorage [OLE32.@]
7659 * Read info on MSDN
7661 * TODO
7662 * DVTARGETDEVICE paramenter is not handled
7663 * Still unsure of some mem fields for OLE 10 Stream
7664 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7665 * and "\001OLE" streams
7668 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7669 LPOLESTREAM pOleStream,
7670 LPSTORAGE pstg,
7671 const DVTARGETDEVICE* ptd)
7673 int i;
7674 HRESULT hRes=S_OK;
7675 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7677 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7679 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7681 if(ptd != NULL)
7683 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7686 if(pstg == NULL || pOleStream == NULL)
7688 hRes = E_INVALIDARG;
7691 if(hRes == S_OK)
7693 /* Load the OLESTREAM to Memory */
7694 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7697 if(hRes == S_OK)
7699 /* Load the OLESTREAM to Memory (part 2)*/
7700 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7703 if(hRes == S_OK)
7706 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7708 /* Do we have the IStorage Data in the OLESTREAM */
7709 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7711 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7712 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7714 else
7716 /* It must be an original OLE 1.0 source */
7717 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7720 else
7722 /* It must be an original OLE 1.0 source */
7723 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7726 /* Create CompObj Stream if necessary */
7727 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7728 if(hRes == S_OK)
7730 /*Create the Ole Stream if necessary */
7731 OLECONVERT_CreateOleStream(pstg);
7736 /* Free allocated memory */
7737 for(i=0; i < 2; i++)
7739 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7740 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7741 pOleStreamData[i].pstrOleObjFileName = NULL;
7743 return hRes;
7746 /*************************************************************************
7747 * OleConvertIStorageToOLESTREAM [OLE32.@]
7749 * Read info on MSDN
7751 * Read info on MSDN
7753 * TODO
7754 * Still unsure of some mem fields for OLE 10 Stream
7755 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7756 * and "\001OLE" streams.
7759 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7760 LPSTORAGE pstg,
7761 LPOLESTREAM pOleStream)
7763 int i;
7764 HRESULT hRes = S_OK;
7765 IStream *pStream;
7766 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7767 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7769 TRACE("%p %p\n", pstg, pOleStream);
7771 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7773 if(pstg == NULL || pOleStream == NULL)
7775 hRes = E_INVALIDARG;
7777 if(hRes == S_OK)
7779 /* Get the ProgID */
7780 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7781 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7783 if(hRes == S_OK)
7785 /* Was it originally Ole10 */
7786 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7787 if(hRes == S_OK)
7789 IStream_Release(pStream);
7790 /* Get Presentation Data for Ole10Native */
7791 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7793 else
7795 /* Get Presentation Data (OLE20) */
7796 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7799 /* Save OLESTREAM */
7800 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7801 if(hRes == S_OK)
7803 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7808 /* Free allocated memory */
7809 for(i=0; i < 2; i++)
7811 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7814 return hRes;
7817 /***********************************************************************
7818 * GetConvertStg (OLE32.@)
7820 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7821 FIXME("unimplemented stub!\n");
7822 return E_FAIL;
7825 /******************************************************************************
7826 * StgIsStorageFile [OLE32.@]
7827 * Verify if the file contains a storage object
7829 * PARAMS
7830 * fn [ I] Filename
7832 * RETURNS
7833 * S_OK if file has magic bytes as a storage object
7834 * S_FALSE if file is not storage
7836 HRESULT WINAPI
7837 StgIsStorageFile(LPCOLESTR fn)
7839 HANDLE hf;
7840 BYTE magic[8];
7841 DWORD bytes_read;
7843 TRACE("%s\n", debugstr_w(fn));
7844 hf = CreateFileW(fn, GENERIC_READ,
7845 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7846 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7848 if (hf == INVALID_HANDLE_VALUE)
7849 return STG_E_FILENOTFOUND;
7851 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7853 WARN(" unable to read file\n");
7854 CloseHandle(hf);
7855 return S_FALSE;
7858 CloseHandle(hf);
7860 if (bytes_read != 8) {
7861 WARN(" too short\n");
7862 return S_FALSE;
7865 if (!memcmp(magic,STORAGE_magic,8)) {
7866 WARN(" -> YES\n");
7867 return S_OK;
7870 WARN(" -> Invalid header.\n");
7871 return S_FALSE;
7874 /***********************************************************************
7875 * WriteClassStm (OLE32.@)
7877 * Writes a CLSID to a stream.
7879 * PARAMS
7880 * pStm [I] Stream to write to.
7881 * rclsid [I] CLSID to write.
7883 * RETURNS
7884 * Success: S_OK.
7885 * Failure: HRESULT code.
7887 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7889 TRACE("(%p,%p)\n",pStm,rclsid);
7891 if (!pStm || !rclsid)
7892 return E_INVALIDARG;
7894 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7897 /***********************************************************************
7898 * ReadClassStm (OLE32.@)
7900 * Reads a CLSID from a stream.
7902 * PARAMS
7903 * pStm [I] Stream to read from.
7904 * rclsid [O] CLSID to read.
7906 * RETURNS
7907 * Success: S_OK.
7908 * Failure: HRESULT code.
7910 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7912 ULONG nbByte;
7913 HRESULT res;
7915 TRACE("(%p,%p)\n",pStm,pclsid);
7917 if (!pStm || !pclsid)
7918 return E_INVALIDARG;
7920 /* clear the output args */
7921 memcpy(pclsid, &CLSID_NULL, sizeof(*pclsid));
7923 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7925 if (FAILED(res))
7926 return res;
7928 if (nbByte != sizeof(CLSID))
7929 return STG_E_READFAULT;
7930 else
7931 return S_OK;