ole32: Rename StorageImpl_WriteProperty to StorageImpl_WriteDirEntry.
[wine/multimedia.git] / dlls / ole32 / storage32.c
blob0639599aeeeb2c81feec879693abb8b23e010b6f
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.
33 #include <assert.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #define COBJMACROS
40 #define NONAMELESSUNION
41 #define NONAMELESSSTRUCT
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winuser.h"
47 #include "wine/unicode.h"
48 #include "wine/debug.h"
50 #include "storage32.h"
51 #include "ole2.h" /* For Write/ReadClassStm */
53 #include "winreg.h"
54 #include "wine/wingdi16.h"
56 WINE_DEFAULT_DEBUG_CHANNEL(storage);
58 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
59 #define OLESTREAM_ID 0x501
60 #define OLESTREAM_MAX_STR_LEN 255
63 * These are signatures to detect the type of Document file.
65 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
66 static const BYTE STORAGE_oldmagic[8] ={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d};
68 static const char rootPropertyName[] = "Root Entry";
70 /****************************************************************************
71 * Storage32InternalImpl definitions.
73 * Definition of the implementation structure for the IStorage32 interface.
74 * This one implements the IStorage32 interface for storage that are
75 * inside another storage.
77 struct StorageInternalImpl
79 struct StorageBaseImpl base;
81 * There is no specific data for this class.
84 typedef struct StorageInternalImpl StorageInternalImpl;
86 /* Method definitions for the Storage32InternalImpl class. */
87 static StorageInternalImpl* StorageInternalImpl_Construct(StorageImpl* ancestorStorage,
88 DWORD openFlags, ULONG rootTropertyIndex);
89 static void StorageImpl_Destroy(StorageBaseImpl* iface);
90 static BOOL StorageImpl_ReadBigBlock(StorageImpl* This, ULONG blockIndex, void* buffer);
91 static BOOL StorageImpl_WriteBigBlock(StorageImpl* This, ULONG blockIndex, const 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);
107 static ULONG SmallBlockChainStream_GetHeadOfChain(SmallBlockChainStream* This);
108 static BOOL StorageImpl_WriteDWordToBigBlock( StorageImpl* This,
109 ULONG blockIndex, ULONG offset, DWORD value);
110 static BOOL StorageImpl_ReadDWordFromBigBlock( StorageImpl* This,
111 ULONG blockIndex, ULONG offset, DWORD* value);
113 /* OLESTREAM memory structure to use for Get and Put Routines */
114 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
115 typedef struct
117 DWORD dwOleID;
118 DWORD dwTypeID;
119 DWORD dwOleTypeNameLength;
120 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
121 CHAR *pstrOleObjFileName;
122 DWORD dwOleObjFileNameLength;
123 DWORD dwMetaFileWidth;
124 DWORD dwMetaFileHeight;
125 CHAR strUnknown[8]; /* don't know what this 8 byte information in OLE stream is. */
126 DWORD dwDataLength;
127 BYTE *pData;
128 }OLECONVERT_OLESTREAM_DATA;
130 /* CompObj Stream structure */
131 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
132 typedef struct
134 BYTE byUnknown1[12];
135 CLSID clsid;
136 DWORD dwCLSIDNameLength;
137 CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
138 DWORD dwOleTypeNameLength;
139 CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
140 DWORD dwProgIDNameLength;
141 CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
142 BYTE byUnknown2[16];
143 }OLECONVERT_ISTORAGE_COMPOBJ;
146 /* Ole Presentation Stream structure */
147 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
148 typedef struct
150 BYTE byUnknown1[28];
151 DWORD dwExtentX;
152 DWORD dwExtentY;
153 DWORD dwSize;
154 BYTE *pData;
155 }OLECONVERT_ISTORAGE_OLEPRES;
159 /***********************************************************************
160 * Forward declaration of internal functions used by the method DestroyElement
162 static HRESULT deleteStorageProperty(
163 StorageBaseImpl *parentStorage,
164 ULONG foundPropertyIndexToDelete,
165 DirEntry propertyToDelete);
167 static HRESULT deleteStreamProperty(
168 StorageBaseImpl *parentStorage,
169 ULONG foundPropertyIndexToDelete,
170 DirEntry propertyToDelete);
172 static HRESULT removeFromTree(
173 StorageImpl *This,
174 ULONG parentStorageIndex,
175 ULONG deletedIndex);
177 /***********************************************************************
178 * Declaration of the functions used to manipulate DirEntry
181 static HRESULT createDirEntry(
182 StorageImpl *storage,
183 const DirEntry *newData,
184 ULONG *index);
186 static HRESULT destroyDirEntry(
187 StorageImpl *storage,
188 ULONG index);
190 static HRESULT insertIntoTree(
191 StorageImpl *This,
192 ULONG parentStorageIndex,
193 ULONG newPropertyIndex);
195 static LONG propertyNameCmp(
196 const OLECHAR *newProperty,
197 const OLECHAR *currentProperty);
199 static ULONG findElement(
200 StorageImpl *storage,
201 ULONG storageEntry,
202 const OLECHAR *name,
203 DirEntry *data);
205 static HRESULT findTreeParent(
206 StorageImpl *storage,
207 ULONG storageEntry,
208 const OLECHAR *childName,
209 DirEntry *parentData,
210 ULONG *parentEntry,
211 ULONG *relation);
213 /***********************************************************************
214 * Declaration of miscellaneous functions...
216 static HRESULT validateSTGM(DWORD stgmValue);
218 static DWORD GetShareModeFromSTGM(DWORD stgm);
219 static DWORD GetAccessModeFromSTGM(DWORD stgm);
220 static DWORD GetCreationModeFromSTGM(DWORD stgm);
222 extern const IPropertySetStorageVtbl IPropertySetStorage_Vtbl;
225 /****************************************************************************
226 * IEnumSTATSTGImpl definitions.
228 * Definition of the implementation structure for the IEnumSTATSTGImpl interface.
229 * This class allows iterating through the content of a storage and to find
230 * specific items inside it.
232 struct IEnumSTATSTGImpl
234 const IEnumSTATSTGVtbl *lpVtbl; /* Needs to be the first item in the struct
235 * since we want to cast this in an IEnumSTATSTG pointer */
237 LONG ref; /* Reference count */
238 StorageImpl* parentStorage; /* Reference to the parent storage */
239 ULONG firstPropertyNode; /* Index of the root of the storage to enumerate */
242 * The current implementation of the IEnumSTATSTGImpl class uses a stack
243 * to walk the property sets to get the content of a storage. This stack
244 * is implemented by the following 3 data members
246 ULONG stackSize;
247 ULONG stackMaxSize;
248 ULONG* stackToVisit;
250 #define ENUMSTATSGT_SIZE_INCREMENT 10
254 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(StorageImpl* This, ULONG firstPropertyNode);
255 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This);
256 static void IEnumSTATSTGImpl_PushSearchNode(IEnumSTATSTGImpl* This, ULONG nodeToPush);
257 static ULONG IEnumSTATSTGImpl_PopSearchNode(IEnumSTATSTGImpl* This, BOOL remove);
259 /************************************************************************
260 ** Block Functions
263 static ULONG BLOCK_GetBigBlockOffset(ULONG index)
265 if (index == 0xffffffff)
266 index = 0;
267 else
268 index ++;
270 return index * BIG_BLOCK_SIZE;
273 /************************************************************************
274 ** Storage32BaseImpl implementation
276 static HRESULT StorageImpl_ReadAt(StorageImpl* This,
277 ULARGE_INTEGER offset,
278 void* buffer,
279 ULONG size,
280 ULONG* bytesRead)
282 return BIGBLOCKFILE_ReadAt(This->bigBlockFile,offset,buffer,size,bytesRead);
285 static HRESULT StorageImpl_WriteAt(StorageImpl* This,
286 ULARGE_INTEGER offset,
287 const void* buffer,
288 const ULONG size,
289 ULONG* bytesWritten)
291 return BIGBLOCKFILE_WriteAt(This->bigBlockFile,offset,buffer,size,bytesWritten);
294 /************************************************************************
295 * Storage32BaseImpl_QueryInterface (IUnknown)
297 * This method implements the common QueryInterface for all IStorage32
298 * implementations contained in this file.
300 * See Windows documentation for more details on IUnknown methods.
302 static HRESULT WINAPI StorageBaseImpl_QueryInterface(
303 IStorage* iface,
304 REFIID riid,
305 void** ppvObject)
307 StorageBaseImpl *This = (StorageBaseImpl *)iface;
309 if ( (This==0) || (ppvObject==0) )
310 return E_INVALIDARG;
312 *ppvObject = 0;
314 if (IsEqualGUID(&IID_IUnknown, riid) ||
315 IsEqualGUID(&IID_IStorage, riid))
317 *ppvObject = This;
319 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
321 *ppvObject = &This->pssVtbl;
324 if ((*ppvObject)==0)
325 return E_NOINTERFACE;
327 IStorage_AddRef(iface);
329 return S_OK;
332 /************************************************************************
333 * Storage32BaseImpl_AddRef (IUnknown)
335 * This method implements the common AddRef for all IStorage32
336 * implementations contained in this file.
338 * See Windows documentation for more details on IUnknown methods.
340 static ULONG WINAPI StorageBaseImpl_AddRef(
341 IStorage* iface)
343 StorageBaseImpl *This = (StorageBaseImpl *)iface;
344 ULONG ref = InterlockedIncrement(&This->ref);
346 TRACE("(%p) AddRef to %d\n", This, ref);
348 return ref;
351 /************************************************************************
352 * Storage32BaseImpl_Release (IUnknown)
354 * This method implements the common Release for all IStorage32
355 * implementations contained in this file.
357 * See Windows documentation for more details on IUnknown methods.
359 static ULONG WINAPI StorageBaseImpl_Release(
360 IStorage* iface)
362 StorageBaseImpl *This = (StorageBaseImpl *)iface;
364 ULONG ref = InterlockedDecrement(&This->ref);
366 TRACE("(%p) ReleaseRef to %d\n", This, ref);
368 if (ref == 0)
371 * Since we are using a system of base-classes, we want to call the
372 * destructor of the appropriate derived class. To do this, we are
373 * using virtual functions to implement the destructor.
375 This->v_destructor(This);
378 return ref;
381 /************************************************************************
382 * Storage32BaseImpl_OpenStream (IStorage)
384 * This method will open the specified stream object from the current storage.
386 * See Windows documentation for more details on IStorage methods.
388 static HRESULT WINAPI StorageBaseImpl_OpenStream(
389 IStorage* iface,
390 const OLECHAR* pwcsName, /* [string][in] */
391 void* reserved1, /* [unique][in] */
392 DWORD grfMode, /* [in] */
393 DWORD reserved2, /* [in] */
394 IStream** ppstm) /* [out] */
396 StorageBaseImpl *This = (StorageBaseImpl *)iface;
397 StgStreamImpl* newStream;
398 DirEntry currentProperty;
399 ULONG foundPropertyIndex;
400 HRESULT res = STG_E_UNKNOWN;
402 TRACE("(%p, %s, %p, %x, %d, %p)\n",
403 iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
405 if ( (pwcsName==NULL) || (ppstm==0) )
407 res = E_INVALIDARG;
408 goto end;
411 *ppstm = NULL;
413 if ( FAILED( validateSTGM(grfMode) ) ||
414 STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
416 res = STG_E_INVALIDFLAG;
417 goto end;
421 * As documented.
423 if ( (grfMode & STGM_DELETEONRELEASE) || (grfMode & STGM_TRANSACTED) )
425 res = STG_E_INVALIDFUNCTION;
426 goto end;
430 * Check that we're compatible with the parent's storage mode, but
431 * only if we are not in transacted mode
433 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
434 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
436 res = STG_E_ACCESSDENIED;
437 goto end;
442 * Search for the element with the given name
444 foundPropertyIndex = findElement(
445 This->ancestorStorage,
446 This->rootPropertySetIndex,
447 pwcsName,
448 &currentProperty);
451 * If it was found, construct the stream object and return a pointer to it.
453 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
454 (currentProperty.propertyType==STGTY_STREAM) )
456 newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
458 if (newStream!=0)
460 newStream->grfMode = grfMode;
461 *ppstm = (IStream*)newStream;
463 IStream_AddRef(*ppstm);
465 res = S_OK;
466 goto end;
469 res = E_OUTOFMEMORY;
470 goto end;
473 res = STG_E_FILENOTFOUND;
475 end:
476 if (res == S_OK)
477 TRACE("<-- IStream %p\n", *ppstm);
478 TRACE("<-- %08x\n", res);
479 return res;
482 /************************************************************************
483 * Storage32BaseImpl_OpenStorage (IStorage)
485 * This method will open a new storage object from the current storage.
487 * See Windows documentation for more details on IStorage methods.
489 static HRESULT WINAPI StorageBaseImpl_OpenStorage(
490 IStorage* iface,
491 const OLECHAR* pwcsName, /* [string][unique][in] */
492 IStorage* pstgPriority, /* [unique][in] */
493 DWORD grfMode, /* [in] */
494 SNB snbExclude, /* [unique][in] */
495 DWORD reserved, /* [in] */
496 IStorage** ppstg) /* [out] */
498 StorageBaseImpl *This = (StorageBaseImpl *)iface;
499 StorageInternalImpl* newStorage;
500 DirEntry currentProperty;
501 ULONG foundPropertyIndex;
502 HRESULT res = STG_E_UNKNOWN;
504 TRACE("(%p, %s, %p, %x, %p, %d, %p)\n",
505 iface, debugstr_w(pwcsName), pstgPriority,
506 grfMode, snbExclude, reserved, ppstg);
508 if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
510 res = E_INVALIDARG;
511 goto end;
514 /* as documented */
515 if (snbExclude != NULL)
517 res = STG_E_INVALIDPARAMETER;
518 goto end;
521 if ( FAILED( validateSTGM(grfMode) ))
523 res = STG_E_INVALIDFLAG;
524 goto end;
528 * As documented.
530 if ( STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE ||
531 (grfMode & STGM_DELETEONRELEASE) ||
532 (grfMode & STGM_PRIORITY) )
534 res = STG_E_INVALIDFUNCTION;
535 goto end;
539 * Check that we're compatible with the parent's storage mode,
540 * but only if we are not transacted
542 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
543 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
545 res = STG_E_ACCESSDENIED;
546 goto end;
550 *ppstg = NULL;
552 foundPropertyIndex = findElement(
553 This->ancestorStorage,
554 This->rootPropertySetIndex,
555 pwcsName,
556 &currentProperty);
558 if ( (foundPropertyIndex!=PROPERTY_NULL) &&
559 (currentProperty.propertyType==STGTY_STORAGE) )
561 newStorage = StorageInternalImpl_Construct(
562 This->ancestorStorage,
563 grfMode,
564 foundPropertyIndex);
566 if (newStorage != 0)
568 *ppstg = (IStorage*)newStorage;
570 StorageBaseImpl_AddRef(*ppstg);
572 res = S_OK;
573 goto end;
576 res = STG_E_INSUFFICIENTMEMORY;
577 goto end;
580 res = STG_E_FILENOTFOUND;
582 end:
583 TRACE("<-- %08x\n", res);
584 return res;
587 /************************************************************************
588 * Storage32BaseImpl_EnumElements (IStorage)
590 * This method will create an enumerator object that can be used to
591 * retrieve information about all the properties in the storage object.
593 * See Windows documentation for more details on IStorage methods.
595 static HRESULT WINAPI StorageBaseImpl_EnumElements(
596 IStorage* iface,
597 DWORD reserved1, /* [in] */
598 void* reserved2, /* [size_is][unique][in] */
599 DWORD reserved3, /* [in] */
600 IEnumSTATSTG** ppenum) /* [out] */
602 StorageBaseImpl *This = (StorageBaseImpl *)iface;
603 IEnumSTATSTGImpl* newEnum;
605 TRACE("(%p, %d, %p, %d, %p)\n",
606 iface, reserved1, reserved2, reserved3, ppenum);
608 if ( (This==0) || (ppenum==0))
609 return E_INVALIDARG;
611 newEnum = IEnumSTATSTGImpl_Construct(
612 This->ancestorStorage,
613 This->rootPropertySetIndex);
615 if (newEnum!=0)
617 *ppenum = (IEnumSTATSTG*)newEnum;
619 IEnumSTATSTG_AddRef(*ppenum);
621 return S_OK;
624 return E_OUTOFMEMORY;
627 /************************************************************************
628 * Storage32BaseImpl_Stat (IStorage)
630 * This method will retrieve information about this storage object.
632 * See Windows documentation for more details on IStorage methods.
634 static HRESULT WINAPI StorageBaseImpl_Stat(
635 IStorage* iface,
636 STATSTG* pstatstg, /* [out] */
637 DWORD grfStatFlag) /* [in] */
639 StorageBaseImpl *This = (StorageBaseImpl *)iface;
640 DirEntry curProperty;
641 BOOL readSuccessful;
642 HRESULT res = STG_E_UNKNOWN;
644 TRACE("(%p, %p, %x)\n",
645 iface, pstatstg, grfStatFlag);
647 if ( (This==0) || (pstatstg==0))
649 res = E_INVALIDARG;
650 goto end;
653 readSuccessful = StorageImpl_ReadDirEntry(
654 This->ancestorStorage,
655 This->rootPropertySetIndex,
656 &curProperty);
658 if (readSuccessful)
660 StorageUtl_CopyPropertyToSTATSTG(
661 pstatstg,
662 &curProperty,
663 grfStatFlag);
665 pstatstg->grfMode = This->openFlags;
666 pstatstg->grfStateBits = This->stateBits;
668 res = S_OK;
669 goto end;
672 res = E_FAIL;
674 end:
675 if (res == S_OK)
677 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);
679 TRACE("<-- %08x\n", res);
680 return res;
683 /************************************************************************
684 * Storage32BaseImpl_RenameElement (IStorage)
686 * This method will rename the specified element.
688 * See Windows documentation for more details on IStorage methods.
690 static HRESULT WINAPI StorageBaseImpl_RenameElement(
691 IStorage* iface,
692 const OLECHAR* pwcsOldName, /* [in] */
693 const OLECHAR* pwcsNewName) /* [in] */
695 StorageBaseImpl *This = (StorageBaseImpl *)iface;
696 DirEntry currentProperty;
697 ULONG foundPropertyIndex;
699 TRACE("(%p, %s, %s)\n",
700 iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
702 foundPropertyIndex = findElement(This->ancestorStorage,
703 This->rootPropertySetIndex,
704 pwcsNewName,
705 &currentProperty);
707 if (foundPropertyIndex != PROPERTY_NULL)
710 * There is already a property with the new name
712 return STG_E_FILEALREADYEXISTS;
716 * Search for the old element name
718 foundPropertyIndex = findElement(This->ancestorStorage,
719 This->rootPropertySetIndex,
720 pwcsOldName,
721 &currentProperty);
723 if (foundPropertyIndex != PROPERTY_NULL)
725 /* Remove the element from its current position in the tree */
726 removeFromTree(This->ancestorStorage, This->rootPropertySetIndex,
727 foundPropertyIndex);
729 /* Change the name of the element */
730 strcpyW(currentProperty.name, pwcsNewName);
732 StorageImpl_WriteDirEntry(This->ancestorStorage, foundPropertyIndex,
733 &currentProperty);
735 /* Insert the element in a new position in the tree */
736 insertIntoTree(This->ancestorStorage, This->rootPropertySetIndex,
737 foundPropertyIndex);
739 else
742 * There is no property with the old name
744 return STG_E_FILENOTFOUND;
747 return S_OK;
750 /************************************************************************
751 * Storage32BaseImpl_CreateStream (IStorage)
753 * This method will create a stream object within this storage
755 * See Windows documentation for more details on IStorage methods.
757 static HRESULT WINAPI StorageBaseImpl_CreateStream(
758 IStorage* iface,
759 const OLECHAR* pwcsName, /* [string][in] */
760 DWORD grfMode, /* [in] */
761 DWORD reserved1, /* [in] */
762 DWORD reserved2, /* [in] */
763 IStream** ppstm) /* [out] */
765 StorageBaseImpl *This = (StorageBaseImpl *)iface;
766 StgStreamImpl* newStream;
767 DirEntry currentProperty, newStreamProperty;
768 ULONG foundPropertyIndex, newPropertyIndex;
770 TRACE("(%p, %s, %x, %d, %d, %p)\n",
771 iface, debugstr_w(pwcsName), grfMode,
772 reserved1, reserved2, ppstm);
774 if (ppstm == 0)
775 return STG_E_INVALIDPOINTER;
777 if (pwcsName == 0)
778 return STG_E_INVALIDNAME;
780 if (reserved1 || reserved2)
781 return STG_E_INVALIDPARAMETER;
783 if ( FAILED( validateSTGM(grfMode) ))
784 return STG_E_INVALIDFLAG;
786 if (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE)
787 return STG_E_INVALIDFLAG;
790 * As documented.
792 if ((grfMode & STGM_DELETEONRELEASE) ||
793 (grfMode & STGM_TRANSACTED))
794 return STG_E_INVALIDFUNCTION;
796 /* Can't create a stream on read-only storage */
797 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
798 return STG_E_ACCESSDENIED;
801 * Check that we're compatible with the parent's storage mode
802 * if not in transacted mode
804 if(!(This->ancestorStorage->base.openFlags & STGM_TRANSACTED)) {
805 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
806 return STG_E_ACCESSDENIED;
809 if(This->ancestorStorage->base.openFlags & STGM_SIMPLE)
810 if(grfMode & STGM_CREATE) return STG_E_INVALIDFLAG;
812 *ppstm = 0;
814 foundPropertyIndex = findElement(This->ancestorStorage,
815 This->rootPropertySetIndex,
816 pwcsName,
817 &currentProperty);
819 if (foundPropertyIndex != PROPERTY_NULL)
822 * An element with this name already exists
824 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE)
826 StgStreamImpl *strm;
828 LIST_FOR_EACH_ENTRY(strm, &This->strmHead, StgStreamImpl, StrmListEntry)
830 if (strm->ownerProperty == foundPropertyIndex)
832 TRACE("Stream deleted %p\n", strm);
833 strm->parentStorage = NULL;
834 list_remove(&strm->StrmListEntry);
837 IStorage_DestroyElement(iface, pwcsName);
839 else
840 return STG_E_FILEALREADYEXISTS;
842 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
844 WARN("read-only storage\n");
845 return STG_E_ACCESSDENIED;
849 * memset the empty property
851 memset(&newStreamProperty, 0, sizeof(DirEntry));
853 newStreamProperty.sizeOfNameString =
854 ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
856 if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
857 return STG_E_INVALIDNAME;
859 strcpyW(newStreamProperty.name, pwcsName);
861 newStreamProperty.propertyType = STGTY_STREAM;
862 newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
863 newStreamProperty.size.u.LowPart = 0;
864 newStreamProperty.size.u.HighPart = 0;
866 newStreamProperty.leftChild = PROPERTY_NULL;
867 newStreamProperty.rightChild = PROPERTY_NULL;
868 newStreamProperty.dirProperty = PROPERTY_NULL;
870 /* call CoFileTime to get the current time
871 newStreamProperty.ctime
872 newStreamProperty.mtime
875 /* newStreamProperty.propertyUniqueID */
878 * Save the new property into a new property spot
880 createDirEntry(This->ancestorStorage, &newStreamProperty, &newPropertyIndex);
883 * Find a spot in the property chain for our newly created property.
885 insertIntoTree(
886 This->ancestorStorage,
887 This->rootPropertySetIndex,
888 newPropertyIndex);
891 * Open the stream to return it.
893 newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
895 if (newStream != 0)
897 *ppstm = (IStream*)newStream;
899 IStream_AddRef(*ppstm);
901 else
903 return STG_E_INSUFFICIENTMEMORY;
906 return S_OK;
909 /************************************************************************
910 * Storage32BaseImpl_SetClass (IStorage)
912 * This method will write the specified CLSID in the property of this
913 * storage.
915 * See Windows documentation for more details on IStorage methods.
917 static HRESULT WINAPI StorageBaseImpl_SetClass(
918 IStorage* iface,
919 REFCLSID clsid) /* [in] */
921 StorageBaseImpl *This = (StorageBaseImpl *)iface;
922 HRESULT hRes = E_FAIL;
923 DirEntry curProperty;
924 BOOL success;
926 TRACE("(%p, %p)\n", iface, clsid);
928 success = StorageImpl_ReadDirEntry(This->ancestorStorage,
929 This->rootPropertySetIndex,
930 &curProperty);
931 if (success)
933 curProperty.propertyUniqueID = *clsid;
935 success = StorageImpl_WriteDirEntry(This->ancestorStorage,
936 This->rootPropertySetIndex,
937 &curProperty);
938 if (success)
939 hRes = S_OK;
942 return hRes;
945 /************************************************************************
946 ** Storage32Impl implementation
949 /************************************************************************
950 * Storage32BaseImpl_CreateStorage (IStorage)
952 * This method will create the storage object within the provided storage.
954 * See Windows documentation for more details on IStorage methods.
956 static HRESULT WINAPI StorageBaseImpl_CreateStorage(
957 IStorage* iface,
958 const OLECHAR *pwcsName, /* [string][in] */
959 DWORD grfMode, /* [in] */
960 DWORD reserved1, /* [in] */
961 DWORD reserved2, /* [in] */
962 IStorage **ppstg) /* [out] */
964 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
966 DirEntry currentProperty;
967 DirEntry newProperty;
968 ULONG foundPropertyIndex;
969 ULONG newPropertyIndex;
970 HRESULT hr;
972 TRACE("(%p, %s, %x, %d, %d, %p)\n",
973 iface, debugstr_w(pwcsName), grfMode,
974 reserved1, reserved2, ppstg);
976 if (ppstg == 0)
977 return STG_E_INVALIDPOINTER;
979 if (pwcsName == 0)
980 return STG_E_INVALIDNAME;
982 *ppstg = NULL;
984 if ( FAILED( validateSTGM(grfMode) ) ||
985 (grfMode & STGM_DELETEONRELEASE) )
987 WARN("bad grfMode: 0x%x\n", grfMode);
988 return STG_E_INVALIDFLAG;
992 * Check that we're compatible with the parent's storage mode
994 if ( STGM_ACCESS_MODE( grfMode ) > STGM_ACCESS_MODE( This->openFlags ) )
996 WARN("access denied\n");
997 return STG_E_ACCESSDENIED;
1000 foundPropertyIndex = findElement(This->ancestorStorage,
1001 This->rootPropertySetIndex,
1002 pwcsName,
1003 &currentProperty);
1005 if (foundPropertyIndex != PROPERTY_NULL)
1008 * An element with this name already exists
1010 if (STGM_CREATE_MODE(grfMode) == STGM_CREATE &&
1011 STGM_ACCESS_MODE(This->openFlags) != STGM_READ)
1013 hr = IStorage_DestroyElement(iface, pwcsName);
1014 if (FAILED(hr))
1015 return hr;
1017 else
1019 WARN("file already exists\n");
1020 return STG_E_FILEALREADYEXISTS;
1023 else if (STGM_ACCESS_MODE(This->openFlags) == STGM_READ)
1025 WARN("read-only storage\n");
1026 return STG_E_ACCESSDENIED;
1030 * memset the empty property
1032 memset(&newProperty, 0, sizeof(DirEntry));
1034 newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1036 if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1038 FIXME("name too long\n");
1039 return STG_E_INVALIDNAME;
1042 strcpyW(newProperty.name, pwcsName);
1044 newProperty.propertyType = STGTY_STORAGE;
1045 newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1046 newProperty.size.u.LowPart = 0;
1047 newProperty.size.u.HighPart = 0;
1049 newProperty.leftChild = PROPERTY_NULL;
1050 newProperty.rightChild = PROPERTY_NULL;
1051 newProperty.dirProperty = PROPERTY_NULL;
1053 /* call CoFileTime to get the current time
1054 newProperty.ctime
1055 newProperty.mtime
1058 /* newStorageProperty.propertyUniqueID */
1061 * Save the new property into a new property spot
1063 createDirEntry(This->ancestorStorage, &newProperty, &newPropertyIndex);
1066 * Find a spot in the property chain for our newly created property.
1068 insertIntoTree(
1069 This->ancestorStorage,
1070 This->rootPropertySetIndex,
1071 newPropertyIndex);
1074 * Open it to get a pointer to return.
1076 hr = IStorage_OpenStorage(iface, pwcsName, 0, grfMode, 0, 0, ppstg);
1078 if( (hr != S_OK) || (*ppstg == NULL))
1080 return hr;
1084 return S_OK;
1088 /***************************************************************************
1090 * Internal Method
1092 * Reserve a directory entry in the file and initialize it.
1094 static HRESULT createDirEntry(
1095 StorageImpl *storage,
1096 const DirEntry *newData,
1097 ULONG *index)
1099 ULONG currentPropertyIndex = 0;
1100 ULONG newPropertyIndex = PROPERTY_NULL;
1101 HRESULT hr = S_OK;
1102 BYTE currentData[PROPSET_BLOCK_SIZE];
1103 WORD sizeOfNameString;
1107 hr = StorageImpl_ReadRawDirEntry(storage,
1108 currentPropertyIndex,
1109 currentData);
1111 if (SUCCEEDED(hr))
1113 StorageUtl_ReadWord(
1114 currentData,
1115 OFFSET_PS_NAMELENGTH,
1116 &sizeOfNameString);
1118 if (sizeOfNameString == 0)
1121 * The property existis and is available, we found it.
1123 newPropertyIndex = currentPropertyIndex;
1126 else
1129 * We exhausted the property list, we will create more space below
1131 newPropertyIndex = currentPropertyIndex;
1133 currentPropertyIndex++;
1135 } while (newPropertyIndex == PROPERTY_NULL);
1138 * grow the property chain
1140 if (FAILED(hr))
1142 BYTE emptyData[PROPSET_BLOCK_SIZE];
1143 ULARGE_INTEGER newSize;
1144 ULONG propertyIndex;
1145 ULONG lastProperty = 0;
1146 ULONG blockCount = 0;
1149 * obtain the new count of property blocks
1151 blockCount = BlockChainStream_GetCount(
1152 storage->rootBlockChain)+1;
1155 * initialize the size used by the property stream
1157 newSize.u.HighPart = 0;
1158 newSize.u.LowPart = storage->bigBlockSize * blockCount;
1161 * add a property block to the property chain
1163 BlockChainStream_SetSize(storage->rootBlockChain, newSize);
1166 * memset the empty property in order to initialize the unused newly
1167 * created property
1169 memset(&emptyData, 0, PROPSET_BLOCK_SIZE);
1172 * initialize them
1174 lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount;
1176 for(
1177 propertyIndex = newPropertyIndex + 1;
1178 propertyIndex < lastProperty;
1179 propertyIndex++)
1181 StorageImpl_WriteRawDirEntry(
1182 storage,
1183 propertyIndex,
1184 emptyData);
1188 UpdateRawDirEntry(currentData, newData);
1190 hr = StorageImpl_WriteRawDirEntry(storage, newPropertyIndex, currentData);
1192 if (SUCCEEDED(hr))
1193 *index = newPropertyIndex;
1195 return hr;
1198 /***************************************************************************
1200 * Internal Method
1202 * Mark a directory entry in the file as free.
1204 static HRESULT destroyDirEntry(
1205 StorageImpl *storage,
1206 ULONG index)
1208 HRESULT hr;
1209 BYTE emptyData[PROPSET_BLOCK_SIZE];
1211 memset(&emptyData, 0, PROPSET_BLOCK_SIZE);
1213 hr = StorageImpl_WriteRawDirEntry(storage, index, emptyData);
1215 return hr;
1219 /****************************************************************************
1221 * Internal Method
1223 * Case insensitive comparison of DirEntry.name by first considering
1224 * their size.
1226 * Returns <0 when newProperty < currentProperty
1227 * >0 when newProperty > currentProperty
1228 * 0 when newProperty == currentProperty
1230 static LONG propertyNameCmp(
1231 const OLECHAR *newProperty,
1232 const OLECHAR *currentProperty)
1234 LONG diff = lstrlenW(newProperty) - lstrlenW(currentProperty);
1236 if (diff == 0)
1239 * We compare the string themselves only when they are of the same length
1241 diff = lstrcmpiW( newProperty, currentProperty);
1244 return diff;
1247 /****************************************************************************
1249 * Internal Method
1251 * Properly link this new element in the property chain.
1253 static HRESULT insertIntoTree(
1254 StorageImpl *This,
1255 ULONG parentStorageIndex,
1256 ULONG newPropertyIndex)
1258 DirEntry currentProperty;
1259 DirEntry newProperty;
1262 * Read the inserted property
1264 StorageImpl_ReadDirEntry(This,
1265 newPropertyIndex,
1266 &newProperty);
1269 * Read the root property
1271 StorageImpl_ReadDirEntry(This,
1272 parentStorageIndex,
1273 &currentProperty);
1275 if (currentProperty.dirProperty != PROPERTY_NULL)
1278 * The root storage contains some element, therefore, start the research
1279 * for the appropriate location.
1281 BOOL found = 0;
1282 ULONG current, next, previous, currentPropertyId;
1285 * Keep the DirEntry sequence number of the storage first property
1287 currentPropertyId = currentProperty.dirProperty;
1290 * Read
1292 StorageImpl_ReadDirEntry(This,
1293 currentProperty.dirProperty,
1294 &currentProperty);
1296 previous = currentProperty.leftChild;
1297 next = currentProperty.rightChild;
1298 current = currentPropertyId;
1300 while (found == 0)
1302 LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1304 if (diff < 0)
1306 if (previous != PROPERTY_NULL)
1308 StorageImpl_ReadDirEntry(This,
1309 previous,
1310 &currentProperty);
1311 current = previous;
1313 else
1315 currentProperty.leftChild = newPropertyIndex;
1316 StorageImpl_WriteDirEntry(This,
1317 current,
1318 &currentProperty);
1319 found = 1;
1322 else if (diff > 0)
1324 if (next != PROPERTY_NULL)
1326 StorageImpl_ReadDirEntry(This,
1327 next,
1328 &currentProperty);
1329 current = next;
1331 else
1333 currentProperty.rightChild = newPropertyIndex;
1334 StorageImpl_WriteDirEntry(This,
1335 current,
1336 &currentProperty);
1337 found = 1;
1340 else
1343 * Trying to insert an item with the same name in the
1344 * subtree structure.
1346 return STG_E_FILEALREADYEXISTS;
1349 previous = currentProperty.leftChild;
1350 next = currentProperty.rightChild;
1353 else
1356 * The root storage is empty, link the new property to its dir property
1358 currentProperty.dirProperty = newPropertyIndex;
1359 StorageImpl_WriteDirEntry(This,
1360 parentStorageIndex,
1361 &currentProperty);
1364 return S_OK;
1367 /****************************************************************************
1369 * Internal Method
1371 * Find and read the element of a storage with the given name.
1373 static ULONG findElement(StorageImpl *storage, ULONG storageEntry,
1374 const OLECHAR *name, DirEntry *data)
1376 ULONG currentEntry;
1378 /* Read the storage entry to find the root of the tree. */
1379 StorageImpl_ReadDirEntry(storage, storageEntry, data);
1381 currentEntry = data->dirProperty;
1383 while (currentEntry != PROPERTY_NULL)
1385 LONG cmp;
1387 StorageImpl_ReadDirEntry(storage, currentEntry, data);
1389 cmp = propertyNameCmp(name, data->name);
1391 if (cmp == 0)
1392 /* found it */
1393 break;
1395 else if (cmp < 0)
1396 currentEntry = data->leftChild;
1398 else if (cmp > 0)
1399 currentEntry = data->rightChild;
1402 return currentEntry;
1405 /****************************************************************************
1407 * Internal Method
1409 * Find and read the binary tree parent of the element with the given name.
1411 * If there is no such element, find a place where it could be inserted and
1412 * return STG_E_FILENOTFOUND.
1414 static HRESULT findTreeParent(StorageImpl *storage, ULONG storageEntry,
1415 const OLECHAR *childName, DirEntry *parentData, ULONG *parentEntry,
1416 ULONG *relation)
1418 ULONG childEntry;
1419 DirEntry childData;
1421 /* Read the storage entry to find the root of the tree. */
1422 StorageImpl_ReadDirEntry(storage, storageEntry, parentData);
1424 *parentEntry = storageEntry;
1425 *relation = PROPERTY_RELATION_DIR;
1427 childEntry = parentData->dirProperty;
1429 while (childEntry != PROPERTY_NULL)
1431 LONG cmp;
1433 StorageImpl_ReadDirEntry(storage, childEntry, &childData);
1435 cmp = propertyNameCmp(childName, childData.name);
1437 if (cmp == 0)
1438 /* found it */
1439 break;
1441 else if (cmp < 0)
1443 *parentData = childData;
1444 *parentEntry = childEntry;
1445 *relation = PROPERTY_RELATION_PREVIOUS;
1447 childEntry = parentData->leftChild;
1450 else if (cmp > 0)
1452 *parentData = childData;
1453 *parentEntry = childEntry;
1454 *relation = PROPERTY_RELATION_NEXT;
1456 childEntry = parentData->rightChild;
1460 if (childEntry == PROPERTY_NULL)
1461 return STG_E_FILENOTFOUND;
1462 else
1463 return S_OK;
1467 /*************************************************************************
1468 * CopyTo (IStorage)
1470 static HRESULT WINAPI StorageBaseImpl_CopyTo(
1471 IStorage* iface,
1472 DWORD ciidExclude, /* [in] */
1473 const IID* rgiidExclude, /* [size_is][unique][in] */
1474 SNB snbExclude, /* [unique][in] */
1475 IStorage* pstgDest) /* [unique][in] */
1477 IEnumSTATSTG *elements = 0;
1478 STATSTG curElement, strStat;
1479 HRESULT hr;
1480 IStorage *pstgTmp, *pstgChild;
1481 IStream *pstrTmp, *pstrChild;
1482 BOOL skip = FALSE, skip_storage = FALSE, skip_stream = FALSE;
1483 int i;
1485 TRACE("(%p, %d, %p, %p, %p)\n",
1486 iface, ciidExclude, rgiidExclude,
1487 snbExclude, pstgDest);
1489 if ( pstgDest == 0 )
1490 return STG_E_INVALIDPOINTER;
1493 * Enumerate the elements
1495 hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1497 if ( hr != S_OK )
1498 return hr;
1501 * set the class ID
1503 IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1504 IStorage_SetClass( pstgDest, &curElement.clsid );
1506 for(i = 0; i < ciidExclude; ++i)
1508 if(IsEqualGUID(&IID_IStorage, &rgiidExclude[i]))
1509 skip_storage = TRUE;
1510 else if(IsEqualGUID(&IID_IStream, &rgiidExclude[i]))
1511 skip_stream = TRUE;
1512 else
1513 WARN("Unknown excluded GUID: %s\n", debugstr_guid(&rgiidExclude[i]));
1519 * Obtain the next element
1521 hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1523 if ( hr == S_FALSE )
1525 hr = S_OK; /* done, every element has been copied */
1526 break;
1529 if ( snbExclude )
1531 WCHAR **snb = snbExclude;
1532 skip = FALSE;
1533 while ( *snb != NULL && !skip )
1535 if ( lstrcmpW(curElement.pwcsName, *snb) == 0 )
1536 skip = TRUE;
1537 ++snb;
1541 if ( skip )
1542 continue;
1544 if (curElement.type == STGTY_STORAGE)
1546 if(skip_storage)
1547 continue;
1550 * open child source storage
1552 hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1553 STGM_READ|STGM_SHARE_EXCLUSIVE,
1554 NULL, 0, &pstgChild );
1556 if (hr != S_OK)
1557 break;
1560 * Check if destination storage is not a child of the source
1561 * storage, which will cause an infinite loop
1563 if (pstgChild == pstgDest)
1565 IEnumSTATSTG_Release(elements);
1567 return STG_E_ACCESSDENIED;
1571 * create a new storage in destination storage
1573 hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1574 STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1575 0, 0,
1576 &pstgTmp );
1578 * if it already exist, don't create a new one use this one
1580 if (hr == STG_E_FILEALREADYEXISTS)
1582 hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1583 STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1584 NULL, 0, &pstgTmp );
1587 if (hr != S_OK)
1588 break;
1592 * do the copy recursively
1594 hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1595 NULL, pstgTmp );
1597 IStorage_Release( pstgTmp );
1598 IStorage_Release( pstgChild );
1600 else if (curElement.type == STGTY_STREAM)
1602 if(skip_stream)
1603 continue;
1606 * create a new stream in destination storage. If the stream already
1607 * exist, it will be deleted and a new one will be created.
1609 hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1610 STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1611 0, 0, &pstrTmp );
1613 if (hr != S_OK)
1614 break;
1617 * open child stream storage
1619 hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1620 STGM_READ|STGM_SHARE_EXCLUSIVE,
1621 0, &pstrChild );
1623 if (hr != S_OK)
1624 break;
1627 * Get the size of the source stream
1629 IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1632 * Set the size of the destination stream.
1634 IStream_SetSize(pstrTmp, strStat.cbSize);
1637 * do the copy
1639 hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1640 NULL, NULL );
1642 IStream_Release( pstrTmp );
1643 IStream_Release( pstrChild );
1645 else
1647 WARN("unknown element type: %d\n", curElement.type);
1650 } while (hr == S_OK);
1653 * Clean-up
1655 IEnumSTATSTG_Release(elements);
1657 return hr;
1660 /*************************************************************************
1661 * MoveElementTo (IStorage)
1663 static HRESULT WINAPI StorageBaseImpl_MoveElementTo(
1664 IStorage* iface,
1665 const OLECHAR *pwcsName, /* [string][in] */
1666 IStorage *pstgDest, /* [unique][in] */
1667 const OLECHAR *pwcsNewName,/* [string][in] */
1668 DWORD grfFlags) /* [in] */
1670 FIXME("(%p %s %p %s %u): stub\n", iface,
1671 debugstr_w(pwcsName), pstgDest,
1672 debugstr_w(pwcsNewName), grfFlags);
1673 return E_NOTIMPL;
1676 /*************************************************************************
1677 * Commit (IStorage)
1679 * Ensures that any changes made to a storage object open in transacted mode
1680 * are reflected in the parent storage
1682 * NOTES
1683 * Wine doesn't implement transacted mode, which seems to be a basic
1684 * optimization, so we can ignore this stub for now.
1686 static HRESULT WINAPI StorageImpl_Commit(
1687 IStorage* iface,
1688 DWORD grfCommitFlags)/* [in] */
1690 FIXME("(%p %d): stub\n", iface, grfCommitFlags);
1691 return S_OK;
1694 /*************************************************************************
1695 * Revert (IStorage)
1697 * Discard all changes that have been made since the last commit operation
1699 static HRESULT WINAPI StorageImpl_Revert(
1700 IStorage* iface)
1702 FIXME("(%p): stub\n", iface);
1703 return E_NOTIMPL;
1706 /*************************************************************************
1707 * DestroyElement (IStorage)
1709 * Strategy: This implementation is built this way for simplicity not for speed.
1710 * I always delete the topmost element of the enumeration and adjust
1711 * the deleted element pointer all the time. This takes longer to
1712 * do but allow to reinvoke DestroyElement whenever we encounter a
1713 * storage object. The optimisation resides in the usage of another
1714 * enumeration strategy that would give all the leaves of a storage
1715 * first. (postfix order)
1717 static HRESULT WINAPI StorageBaseImpl_DestroyElement(
1718 IStorage* iface,
1719 const OLECHAR *pwcsName)/* [string][in] */
1721 StorageBaseImpl* const This=(StorageBaseImpl*)iface;
1723 HRESULT hr = S_OK;
1724 DirEntry propertyToDelete;
1725 ULONG foundPropertyIndexToDelete;
1727 TRACE("(%p, %s)\n",
1728 iface, debugstr_w(pwcsName));
1730 if (pwcsName==NULL)
1731 return STG_E_INVALIDPOINTER;
1733 if ( STGM_ACCESS_MODE( This->openFlags ) == STGM_READ )
1734 return STG_E_ACCESSDENIED;
1736 foundPropertyIndexToDelete = findElement(
1737 This->ancestorStorage,
1738 This->rootPropertySetIndex,
1739 pwcsName,
1740 &propertyToDelete);
1742 if ( foundPropertyIndexToDelete == PROPERTY_NULL )
1744 return STG_E_FILENOTFOUND;
1747 if ( propertyToDelete.propertyType == STGTY_STORAGE )
1749 hr = deleteStorageProperty(
1750 This,
1751 foundPropertyIndexToDelete,
1752 propertyToDelete);
1754 else if ( propertyToDelete.propertyType == STGTY_STREAM )
1756 hr = deleteStreamProperty(
1757 This,
1758 foundPropertyIndexToDelete,
1759 propertyToDelete);
1762 if (hr!=S_OK)
1763 return hr;
1766 * Adjust the property chain
1768 hr = removeFromTree(
1769 This->ancestorStorage,
1770 This->rootPropertySetIndex,
1771 foundPropertyIndexToDelete);
1774 * Invalidate the property
1776 if (SUCCEEDED(hr))
1777 destroyDirEntry(This->ancestorStorage,
1778 foundPropertyIndexToDelete);
1780 return hr;
1784 /************************************************************************
1785 * StorageImpl_Stat (IStorage)
1787 * This method will retrieve information about this storage object.
1789 * See Windows documentation for more details on IStorage methods.
1791 static HRESULT WINAPI StorageImpl_Stat( IStorage* iface,
1792 STATSTG* pstatstg, /* [out] */
1793 DWORD grfStatFlag) /* [in] */
1795 StorageImpl* const This = (StorageImpl*)iface;
1796 HRESULT result = StorageBaseImpl_Stat( iface, pstatstg, grfStatFlag );
1798 if ( SUCCEEDED(result) && ((grfStatFlag & STATFLAG_NONAME) == 0) && This->pwcsName )
1800 CoTaskMemFree(pstatstg->pwcsName);
1801 pstatstg->pwcsName = CoTaskMemAlloc((lstrlenW(This->pwcsName)+1)*sizeof(WCHAR));
1802 strcpyW(pstatstg->pwcsName, This->pwcsName);
1805 return result;
1808 /******************************************************************************
1809 * Internal stream list handlers
1812 void StorageBaseImpl_AddStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1814 TRACE("Stream added (stg=%p strm=%p)\n", stg, strm);
1815 list_add_tail(&stg->strmHead,&strm->StrmListEntry);
1818 void StorageBaseImpl_RemoveStream(StorageBaseImpl * stg, StgStreamImpl * strm)
1820 TRACE("Stream removed (stg=%p strm=%p)\n", stg,strm);
1821 list_remove(&(strm->StrmListEntry));
1824 static void StorageBaseImpl_DeleteAll(StorageBaseImpl * stg)
1826 struct list *cur, *cur2;
1827 StgStreamImpl *strm=NULL;
1829 LIST_FOR_EACH_SAFE(cur, cur2, &stg->strmHead) {
1830 strm = LIST_ENTRY(cur,StgStreamImpl,StrmListEntry);
1831 TRACE("Streams deleted (stg=%p strm=%p next=%p prev=%p)\n", stg,strm,cur->next,cur->prev);
1832 strm->parentStorage = NULL;
1833 list_remove(cur);
1838 /*********************************************************************
1840 * Internal Method
1842 * Perform the deletion of a complete storage node
1845 static HRESULT deleteStorageProperty(
1846 StorageBaseImpl *parentStorage,
1847 ULONG indexOfPropertyToDelete,
1848 DirEntry propertyToDelete)
1850 IEnumSTATSTG *elements = 0;
1851 IStorage *childStorage = 0;
1852 STATSTG currentElement;
1853 HRESULT hr;
1854 HRESULT destroyHr = S_OK;
1857 * Open the storage and enumerate it
1859 hr = StorageBaseImpl_OpenStorage(
1860 (IStorage*)parentStorage,
1861 propertyToDelete.name,
1863 STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1866 &childStorage);
1868 if (hr != S_OK)
1870 return hr;
1874 * Enumerate the elements
1876 IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1881 * Obtain the next element
1883 hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1884 if (hr==S_OK)
1886 destroyHr = IStorage_DestroyElement(childStorage, currentElement.pwcsName);
1888 CoTaskMemFree(currentElement.pwcsName);
1892 * We need to Reset the enumeration every time because we delete elements
1893 * and the enumeration could be invalid
1895 IEnumSTATSTG_Reset(elements);
1897 } while ((hr == S_OK) && (destroyHr == S_OK));
1899 IStorage_Release(childStorage);
1900 IEnumSTATSTG_Release(elements);
1902 return destroyHr;
1905 /*********************************************************************
1907 * Internal Method
1909 * Perform the deletion of a stream node
1912 static HRESULT deleteStreamProperty(
1913 StorageBaseImpl *parentStorage,
1914 ULONG indexOfPropertyToDelete,
1915 DirEntry propertyToDelete)
1917 IStream *pis;
1918 HRESULT hr;
1919 ULARGE_INTEGER size;
1921 size.u.HighPart = 0;
1922 size.u.LowPart = 0;
1924 hr = StorageBaseImpl_OpenStream((IStorage*)parentStorage,
1925 propertyToDelete.name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &pis);
1927 if (hr!=S_OK)
1929 return(hr);
1933 * Zap the stream
1935 hr = IStream_SetSize(pis, size);
1937 if(hr != S_OK)
1939 return hr;
1943 * Release the stream object.
1945 IStream_Release(pis);
1947 return S_OK;
1950 static void setPropertyLink(DirEntry *property, ULONG relation, ULONG new_target)
1952 switch (relation)
1954 case PROPERTY_RELATION_PREVIOUS:
1955 property->leftChild = new_target;
1956 break;
1957 case PROPERTY_RELATION_NEXT:
1958 property->rightChild = new_target;
1959 break;
1960 case PROPERTY_RELATION_DIR:
1961 property->dirProperty = new_target;
1962 break;
1963 default:
1964 assert(0);
1968 /*************************************************************************
1970 * Internal Method
1972 * This method removes a directory entry from its parent storage tree without
1973 * freeing any resources attached to it.
1975 static HRESULT removeFromTree(
1976 StorageImpl *This,
1977 ULONG parentStorageIndex,
1978 ULONG deletedIndex)
1980 HRESULT hr = S_OK;
1981 BOOL res = TRUE;
1982 DirEntry propertyToDelete;
1983 DirEntry parentProperty;
1984 ULONG parentPropertyId;
1985 ULONG typeOfRelation;
1987 res = StorageImpl_ReadDirEntry(This, deletedIndex, &propertyToDelete);
1990 * Find the property that links to the one we want to delete.
1992 hr = findTreeParent(This, parentStorageIndex, propertyToDelete.name,
1993 &parentProperty, &parentPropertyId, &typeOfRelation);
1995 if (hr != S_OK)
1996 return hr;
1998 if (propertyToDelete.leftChild != PROPERTY_NULL)
2001 * Replace the deleted entry with its left child
2003 setPropertyLink(&parentProperty, typeOfRelation, propertyToDelete.leftChild);
2005 res = StorageImpl_WriteDirEntry(
2006 This,
2007 parentPropertyId,
2008 &parentProperty);
2009 if(!res)
2011 return E_FAIL;
2014 if (propertyToDelete.rightChild != PROPERTY_NULL)
2017 * We need to reinsert the right child somewhere. We already know it and
2018 * its children are greater than everything in the left tree, so we
2019 * insert it at the rightmost point in the left tree.
2021 ULONG newRightChildParent = propertyToDelete.leftChild;
2022 DirEntry newRightChildParentProperty;
2026 res = StorageImpl_ReadDirEntry(
2027 This,
2028 newRightChildParent,
2029 &newRightChildParentProperty);
2030 if (!res)
2032 return E_FAIL;
2035 if (newRightChildParentProperty.rightChild != PROPERTY_NULL)
2036 newRightChildParent = newRightChildParentProperty.rightChild;
2037 } while (newRightChildParentProperty.rightChild != PROPERTY_NULL);
2039 newRightChildParentProperty.rightChild = propertyToDelete.rightChild;
2041 res = StorageImpl_WriteDirEntry(
2042 This,
2043 newRightChildParent,
2044 &newRightChildParentProperty);
2045 if (!res)
2047 return E_FAIL;
2051 else
2054 * Replace the deleted entry with its right child
2056 setPropertyLink(&parentProperty, typeOfRelation, propertyToDelete.rightChild);
2058 res = StorageImpl_WriteDirEntry(
2059 This,
2060 parentPropertyId,
2061 &parentProperty);
2062 if(!res)
2064 return E_FAIL;
2068 return hr;
2072 /******************************************************************************
2073 * SetElementTimes (IStorage)
2075 static HRESULT WINAPI StorageBaseImpl_SetElementTimes(
2076 IStorage* iface,
2077 const OLECHAR *pwcsName,/* [string][in] */
2078 const FILETIME *pctime, /* [in] */
2079 const FILETIME *patime, /* [in] */
2080 const FILETIME *pmtime) /* [in] */
2082 FIXME("(%s,...), stub!\n",debugstr_w(pwcsName));
2083 return S_OK;
2086 /******************************************************************************
2087 * SetStateBits (IStorage)
2089 static HRESULT WINAPI StorageBaseImpl_SetStateBits(
2090 IStorage* iface,
2091 DWORD grfStateBits,/* [in] */
2092 DWORD grfMask) /* [in] */
2094 StorageBaseImpl* const This = (StorageBaseImpl*)iface;
2095 This->stateBits = (This->stateBits & ~grfMask) | (grfStateBits & grfMask);
2096 return S_OK;
2100 * Virtual function table for the IStorage32Impl class.
2102 static const IStorageVtbl Storage32Impl_Vtbl =
2104 StorageBaseImpl_QueryInterface,
2105 StorageBaseImpl_AddRef,
2106 StorageBaseImpl_Release,
2107 StorageBaseImpl_CreateStream,
2108 StorageBaseImpl_OpenStream,
2109 StorageBaseImpl_CreateStorage,
2110 StorageBaseImpl_OpenStorage,
2111 StorageBaseImpl_CopyTo,
2112 StorageBaseImpl_MoveElementTo,
2113 StorageImpl_Commit,
2114 StorageImpl_Revert,
2115 StorageBaseImpl_EnumElements,
2116 StorageBaseImpl_DestroyElement,
2117 StorageBaseImpl_RenameElement,
2118 StorageBaseImpl_SetElementTimes,
2119 StorageBaseImpl_SetClass,
2120 StorageBaseImpl_SetStateBits,
2121 StorageImpl_Stat
2124 static HRESULT StorageImpl_Construct(
2125 StorageImpl* This,
2126 HANDLE hFile,
2127 LPCOLESTR pwcsName,
2128 ILockBytes* pLkbyt,
2129 DWORD openFlags,
2130 BOOL fileBased,
2131 BOOL create)
2133 HRESULT hr = S_OK;
2134 DirEntry currentProperty;
2135 BOOL readSuccessful;
2136 ULONG currentPropertyIndex;
2138 if ( FAILED( validateSTGM(openFlags) ))
2139 return STG_E_INVALIDFLAG;
2141 memset(This, 0, sizeof(StorageImpl));
2143 list_init(&This->base.strmHead);
2145 This->base.lpVtbl = &Storage32Impl_Vtbl;
2146 This->base.pssVtbl = &IPropertySetStorage_Vtbl;
2147 This->base.v_destructor = StorageImpl_Destroy;
2148 This->base.openFlags = (openFlags & ~STGM_CREATE);
2149 This->create = create;
2152 * This is the top-level storage so initialize the ancestor pointer
2153 * to this.
2155 This->base.ancestorStorage = This;
2157 This->hFile = hFile;
2159 if(pwcsName) {
2160 This->pwcsName = HeapAlloc(GetProcessHeap(), 0,
2161 (lstrlenW(pwcsName)+1)*sizeof(WCHAR));
2162 if (!This->pwcsName)
2163 return STG_E_INSUFFICIENTMEMORY;
2164 strcpyW(This->pwcsName, pwcsName);
2168 * Initialize the big block cache.
2170 This->bigBlockSize = DEF_BIG_BLOCK_SIZE;
2171 This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2172 This->bigBlockFile = BIGBLOCKFILE_Construct(hFile,
2173 pLkbyt,
2174 openFlags,
2175 This->bigBlockSize,
2176 fileBased);
2178 if (This->bigBlockFile == 0)
2179 return E_FAIL;
2181 if (create)
2183 ULARGE_INTEGER size;
2184 BYTE bigBlockBuffer[BIG_BLOCK_SIZE];
2187 * Initialize all header variables:
2188 * - The big block depot consists of one block and it is at block 0
2189 * - The properties start at block 1
2190 * - There is no small block depot
2192 memset( This->bigBlockDepotStart,
2193 BLOCK_UNUSED,
2194 sizeof(This->bigBlockDepotStart));
2196 This->bigBlockDepotCount = 1;
2197 This->bigBlockDepotStart[0] = 0;
2198 This->rootStartBlock = 1;
2199 This->smallBlockDepotStart = BLOCK_END_OF_CHAIN;
2200 This->bigBlockSizeBits = DEF_BIG_BLOCK_SIZE_BITS;
2201 This->smallBlockSizeBits = DEF_SMALL_BLOCK_SIZE_BITS;
2202 This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2203 This->extBigBlockDepotCount = 0;
2205 StorageImpl_SaveFileHeader(This);
2208 * Add one block for the big block depot and one block for the properties
2210 size.u.HighPart = 0;
2211 size.u.LowPart = This->bigBlockSize * 3;
2212 BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2215 * Initialize the big block depot
2217 memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2218 StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2219 StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2220 StorageImpl_WriteBigBlock(This, 0, bigBlockBuffer);
2222 else
2225 * Load the header for the file.
2227 hr = StorageImpl_LoadFileHeader(This);
2229 if (FAILED(hr))
2231 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2233 return hr;
2238 * There is no block depot cached yet.
2240 This->indexBlockDepotCached = 0xFFFFFFFF;
2243 * Start searching for free blocks with block 0.
2245 This->prevFreeBlock = 0;
2248 * Create the block chain abstractions.
2250 if(!(This->rootBlockChain =
2251 BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2252 return STG_E_READFAULT;
2254 if(!(This->smallBlockDepotChain =
2255 BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2256 PROPERTY_NULL)))
2257 return STG_E_READFAULT;
2260 * Write the root property (memory only)
2262 if (create)
2264 DirEntry rootProp;
2266 * Initialize the property chain
2268 memset(&rootProp, 0, sizeof(rootProp));
2269 MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2270 sizeof(rootProp.name)/sizeof(WCHAR) );
2271 rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2272 rootProp.propertyType = STGTY_ROOT;
2273 rootProp.leftChild = PROPERTY_NULL;
2274 rootProp.rightChild = PROPERTY_NULL;
2275 rootProp.dirProperty = PROPERTY_NULL;
2276 rootProp.startingBlock = BLOCK_END_OF_CHAIN;
2277 rootProp.size.u.HighPart = 0;
2278 rootProp.size.u.LowPart = 0;
2280 StorageImpl_WriteDirEntry(This, 0, &rootProp);
2284 * Find the ID of the root in the property sets.
2286 currentPropertyIndex = 0;
2290 readSuccessful = StorageImpl_ReadDirEntry(
2291 This,
2292 currentPropertyIndex,
2293 &currentProperty);
2295 if (readSuccessful)
2297 if ( (currentProperty.sizeOfNameString != 0 ) &&
2298 (currentProperty.propertyType == STGTY_ROOT) )
2300 This->base.rootPropertySetIndex = currentPropertyIndex;
2304 currentPropertyIndex++;
2306 } while (readSuccessful && (This->base.rootPropertySetIndex == PROPERTY_NULL) );
2308 if (!readSuccessful)
2310 /* TODO CLEANUP */
2311 return STG_E_READFAULT;
2315 * Create the block chain abstraction for the small block root chain.
2317 if(!(This->smallBlockRootChain =
2318 BlockChainStream_Construct(This, NULL, This->base.rootPropertySetIndex)))
2319 return STG_E_READFAULT;
2321 return hr;
2324 static void StorageImpl_Destroy(StorageBaseImpl* iface)
2326 StorageImpl *This = (StorageImpl*) iface;
2327 TRACE("(%p)\n", This);
2329 StorageBaseImpl_DeleteAll(&This->base);
2331 HeapFree(GetProcessHeap(), 0, This->pwcsName);
2333 BlockChainStream_Destroy(This->smallBlockRootChain);
2334 BlockChainStream_Destroy(This->rootBlockChain);
2335 BlockChainStream_Destroy(This->smallBlockDepotChain);
2337 BIGBLOCKFILE_Destructor(This->bigBlockFile);
2338 HeapFree(GetProcessHeap(), 0, This);
2341 /******************************************************************************
2342 * Storage32Impl_GetNextFreeBigBlock
2344 * Returns the index of the next free big block.
2345 * If the big block depot is filled, this method will enlarge it.
2348 static ULONG StorageImpl_GetNextFreeBigBlock(
2349 StorageImpl* This)
2351 ULONG depotBlockIndexPos;
2352 BYTE depotBuffer[BIG_BLOCK_SIZE];
2353 BOOL success;
2354 ULONG depotBlockOffset;
2355 ULONG blocksPerDepot = This->bigBlockSize / sizeof(ULONG);
2356 ULONG nextBlockIndex = BLOCK_SPECIAL;
2357 int depotIndex = 0;
2358 ULONG freeBlock = BLOCK_UNUSED;
2360 depotIndex = This->prevFreeBlock / blocksPerDepot;
2361 depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2364 * Scan the entire big block depot until we find a block marked free
2366 while (nextBlockIndex != BLOCK_UNUSED)
2368 if (depotIndex < COUNT_BBDEPOTINHEADER)
2370 depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2373 * Grow the primary depot.
2375 if (depotBlockIndexPos == BLOCK_UNUSED)
2377 depotBlockIndexPos = depotIndex*blocksPerDepot;
2380 * Add a block depot.
2382 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2383 This->bigBlockDepotCount++;
2384 This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2387 * Flag it as a block depot.
2389 StorageImpl_SetNextBlockInChain(This,
2390 depotBlockIndexPos,
2391 BLOCK_SPECIAL);
2393 /* Save new header information.
2395 StorageImpl_SaveFileHeader(This);
2398 else
2400 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2402 if (depotBlockIndexPos == BLOCK_UNUSED)
2405 * Grow the extended depot.
2407 ULONG extIndex = BLOCK_UNUSED;
2408 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2409 ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2411 if (extBlockOffset == 0)
2413 /* We need an extended block.
2415 extIndex = Storage32Impl_AddExtBlockDepot(This);
2416 This->extBigBlockDepotCount++;
2417 depotBlockIndexPos = extIndex + 1;
2419 else
2420 depotBlockIndexPos = depotIndex * blocksPerDepot;
2423 * Add a block depot and mark it in the extended block.
2425 Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2426 This->bigBlockDepotCount++;
2427 Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2429 /* Flag the block depot.
2431 StorageImpl_SetNextBlockInChain(This,
2432 depotBlockIndexPos,
2433 BLOCK_SPECIAL);
2435 /* If necessary, flag the extended depot block.
2437 if (extIndex != BLOCK_UNUSED)
2438 StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2440 /* Save header information.
2442 StorageImpl_SaveFileHeader(This);
2446 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2448 if (success)
2450 while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2451 ( nextBlockIndex != BLOCK_UNUSED))
2453 StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2455 if (nextBlockIndex == BLOCK_UNUSED)
2457 freeBlock = (depotIndex * blocksPerDepot) +
2458 (depotBlockOffset/sizeof(ULONG));
2461 depotBlockOffset += sizeof(ULONG);
2465 depotIndex++;
2466 depotBlockOffset = 0;
2470 * make sure that the block physically exists before using it
2472 BIGBLOCKFILE_EnsureExists(This->bigBlockFile, freeBlock);
2474 This->prevFreeBlock = freeBlock;
2476 return freeBlock;
2479 /******************************************************************************
2480 * Storage32Impl_AddBlockDepot
2482 * This will create a depot block, essentially it is a block initialized
2483 * to BLOCK_UNUSEDs.
2485 static void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2487 BYTE blockBuffer[BIG_BLOCK_SIZE];
2490 * Initialize blocks as free
2492 memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2493 StorageImpl_WriteBigBlock(This, blockIndex, blockBuffer);
2496 /******************************************************************************
2497 * Storage32Impl_GetExtDepotBlock
2499 * Returns the index of the block that corresponds to the specified depot
2500 * index. This method is only for depot indexes equal or greater than
2501 * COUNT_BBDEPOTINHEADER.
2503 static ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2505 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2506 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2507 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2508 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2509 ULONG blockIndex = BLOCK_UNUSED;
2510 ULONG extBlockIndex = This->extBigBlockDepotStart;
2512 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2514 if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2515 return BLOCK_UNUSED;
2517 while (extBlockCount > 0)
2519 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2520 extBlockCount--;
2523 if (extBlockIndex != BLOCK_UNUSED)
2524 StorageImpl_ReadDWordFromBigBlock(This, extBlockIndex,
2525 extBlockOffset * sizeof(ULONG), &blockIndex);
2527 return blockIndex;
2530 /******************************************************************************
2531 * Storage32Impl_SetExtDepotBlock
2533 * Associates the specified block index to the specified depot index.
2534 * This method is only for depot indexes equal or greater than
2535 * COUNT_BBDEPOTINHEADER.
2537 static void Storage32Impl_SetExtDepotBlock(StorageImpl* This, ULONG depotIndex, ULONG blockIndex)
2539 ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2540 ULONG numExtBlocks = depotIndex - COUNT_BBDEPOTINHEADER;
2541 ULONG extBlockCount = numExtBlocks / depotBlocksPerExtBlock;
2542 ULONG extBlockOffset = numExtBlocks % depotBlocksPerExtBlock;
2543 ULONG extBlockIndex = This->extBigBlockDepotStart;
2545 assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2547 while (extBlockCount > 0)
2549 extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2550 extBlockCount--;
2553 if (extBlockIndex != BLOCK_UNUSED)
2555 StorageImpl_WriteDWordToBigBlock(This, extBlockIndex,
2556 extBlockOffset * sizeof(ULONG),
2557 blockIndex);
2561 /******************************************************************************
2562 * Storage32Impl_AddExtBlockDepot
2564 * Creates an extended depot block.
2566 static ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2568 ULONG numExtBlocks = This->extBigBlockDepotCount;
2569 ULONG nextExtBlock = This->extBigBlockDepotStart;
2570 BYTE depotBuffer[BIG_BLOCK_SIZE];
2571 ULONG index = BLOCK_UNUSED;
2572 ULONG nextBlockOffset = This->bigBlockSize - sizeof(ULONG);
2573 ULONG blocksPerDepotBlock = This->bigBlockSize / sizeof(ULONG);
2574 ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2576 index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2577 blocksPerDepotBlock;
2579 if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2582 * The first extended block.
2584 This->extBigBlockDepotStart = index;
2586 else
2588 unsigned int i;
2590 * Follow the chain to the last one.
2592 for (i = 0; i < (numExtBlocks - 1); i++)
2594 nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2598 * Add the new extended block to the chain.
2600 StorageImpl_WriteDWordToBigBlock(This, nextExtBlock, nextBlockOffset,
2601 index);
2605 * Initialize this block.
2607 memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2608 StorageImpl_WriteBigBlock(This, index, depotBuffer);
2610 return index;
2613 /******************************************************************************
2614 * Storage32Impl_FreeBigBlock
2616 * This method will flag the specified block as free in the big block depot.
2618 static void StorageImpl_FreeBigBlock(
2619 StorageImpl* This,
2620 ULONG blockIndex)
2622 StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2624 if (blockIndex < This->prevFreeBlock)
2625 This->prevFreeBlock = blockIndex;
2628 /************************************************************************
2629 * Storage32Impl_GetNextBlockInChain
2631 * This method will retrieve the block index of the next big block in
2632 * in the chain.
2634 * Params: This - Pointer to the Storage object.
2635 * blockIndex - Index of the block to retrieve the chain
2636 * for.
2637 * nextBlockIndex - receives the return value.
2639 * Returns: This method returns the index of the next block in the chain.
2640 * It will return the constants:
2641 * BLOCK_SPECIAL - If the block given was not part of a
2642 * chain.
2643 * BLOCK_END_OF_CHAIN - If the block given was the last in
2644 * a chain.
2645 * BLOCK_UNUSED - If the block given was not past of a chain
2646 * and is available.
2647 * BLOCK_EXTBBDEPOT - This block is part of the extended
2648 * big block depot.
2650 * See Windows documentation for more details on IStorage methods.
2652 static HRESULT StorageImpl_GetNextBlockInChain(
2653 StorageImpl* This,
2654 ULONG blockIndex,
2655 ULONG* nextBlockIndex)
2657 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2658 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2659 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2660 BYTE depotBuffer[BIG_BLOCK_SIZE];
2661 BOOL success;
2662 ULONG depotBlockIndexPos;
2663 int index;
2665 *nextBlockIndex = BLOCK_SPECIAL;
2667 if(depotBlockCount >= This->bigBlockDepotCount)
2669 WARN("depotBlockCount %d, bigBlockDepotCount %d\n", depotBlockCount,
2670 This->bigBlockDepotCount);
2671 return STG_E_READFAULT;
2675 * Cache the currently accessed depot block.
2677 if (depotBlockCount != This->indexBlockDepotCached)
2679 This->indexBlockDepotCached = depotBlockCount;
2681 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2683 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2685 else
2688 * We have to look in the extended depot.
2690 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2693 success = StorageImpl_ReadBigBlock(This, depotBlockIndexPos, depotBuffer);
2695 if (!success)
2696 return STG_E_READFAULT;
2698 for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2700 StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2701 This->blockDepotCached[index] = *nextBlockIndex;
2705 *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2707 return S_OK;
2710 /******************************************************************************
2711 * Storage32Impl_GetNextExtendedBlock
2713 * Given an extended block this method will return the next extended block.
2715 * NOTES:
2716 * The last ULONG of an extended block is the block index of the next
2717 * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2718 * depot.
2720 * Return values:
2721 * - The index of the next extended block
2722 * - BLOCK_UNUSED: there is no next extended block.
2723 * - Any other return values denotes failure.
2725 static ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2727 ULONG nextBlockIndex = BLOCK_SPECIAL;
2728 ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2730 StorageImpl_ReadDWordFromBigBlock(This, blockIndex, depotBlockOffset,
2731 &nextBlockIndex);
2733 return nextBlockIndex;
2736 /******************************************************************************
2737 * Storage32Impl_SetNextBlockInChain
2739 * This method will write the index of the specified block's next block
2740 * in the big block depot.
2742 * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2743 * do the following
2745 * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2746 * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2747 * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2750 static void StorageImpl_SetNextBlockInChain(
2751 StorageImpl* This,
2752 ULONG blockIndex,
2753 ULONG nextBlock)
2755 ULONG offsetInDepot = blockIndex * sizeof (ULONG);
2756 ULONG depotBlockCount = offsetInDepot / This->bigBlockSize;
2757 ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2758 ULONG depotBlockIndexPos;
2760 assert(depotBlockCount < This->bigBlockDepotCount);
2761 assert(blockIndex != nextBlock);
2763 if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2765 depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2767 else
2770 * We have to look in the extended depot.
2772 depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2775 StorageImpl_WriteDWordToBigBlock(This, depotBlockIndexPos, depotBlockOffset,
2776 nextBlock);
2778 * Update the cached block depot, if necessary.
2780 if (depotBlockCount == This->indexBlockDepotCached)
2782 This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2786 /******************************************************************************
2787 * Storage32Impl_LoadFileHeader
2789 * This method will read in the file header, i.e. big block index -1.
2791 static HRESULT StorageImpl_LoadFileHeader(
2792 StorageImpl* This)
2794 HRESULT hr = STG_E_FILENOTFOUND;
2795 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2796 BOOL success;
2797 int index;
2799 TRACE("\n");
2801 * Get a pointer to the big block of data containing the header.
2803 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2806 * Extract the information from the header.
2808 if (success)
2811 * Check for the "magic number" signature and return an error if it is not
2812 * found.
2814 if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2816 return STG_E_OLDFORMAT;
2819 if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2821 return STG_E_INVALIDHEADER;
2824 StorageUtl_ReadWord(
2825 headerBigBlock,
2826 OFFSET_BIGBLOCKSIZEBITS,
2827 &This->bigBlockSizeBits);
2829 StorageUtl_ReadWord(
2830 headerBigBlock,
2831 OFFSET_SMALLBLOCKSIZEBITS,
2832 &This->smallBlockSizeBits);
2834 StorageUtl_ReadDWord(
2835 headerBigBlock,
2836 OFFSET_BBDEPOTCOUNT,
2837 &This->bigBlockDepotCount);
2839 StorageUtl_ReadDWord(
2840 headerBigBlock,
2841 OFFSET_ROOTSTARTBLOCK,
2842 &This->rootStartBlock);
2844 StorageUtl_ReadDWord(
2845 headerBigBlock,
2846 OFFSET_SBDEPOTSTART,
2847 &This->smallBlockDepotStart);
2849 StorageUtl_ReadDWord(
2850 headerBigBlock,
2851 OFFSET_EXTBBDEPOTSTART,
2852 &This->extBigBlockDepotStart);
2854 StorageUtl_ReadDWord(
2855 headerBigBlock,
2856 OFFSET_EXTBBDEPOTCOUNT,
2857 &This->extBigBlockDepotCount);
2859 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2861 StorageUtl_ReadDWord(
2862 headerBigBlock,
2863 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2864 &(This->bigBlockDepotStart[index]));
2868 * Make the bitwise arithmetic to get the size of the blocks in bytes.
2870 This->bigBlockSize = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2871 This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2874 * Right now, the code is making some assumptions about the size of the
2875 * blocks, just make sure they are what we're expecting.
2877 if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2878 This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2880 WARN("Broken OLE storage file\n");
2881 hr = STG_E_INVALIDHEADER;
2883 else
2884 hr = S_OK;
2887 return hr;
2890 /******************************************************************************
2891 * Storage32Impl_SaveFileHeader
2893 * This method will save to the file the header, i.e. big block -1.
2895 static void StorageImpl_SaveFileHeader(
2896 StorageImpl* This)
2898 BYTE headerBigBlock[BIG_BLOCK_SIZE];
2899 int index;
2900 BOOL success;
2903 * Get a pointer to the big block of data containing the header.
2905 success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2908 * If the block read failed, the file is probably new.
2910 if (!success)
2913 * Initialize for all unknown fields.
2915 memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2918 * Initialize the magic number.
2920 memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2923 * And a bunch of things we don't know what they mean
2925 StorageUtl_WriteWord(headerBigBlock, 0x18, 0x3b);
2926 StorageUtl_WriteWord(headerBigBlock, 0x1a, 0x3);
2927 StorageUtl_WriteWord(headerBigBlock, 0x1c, (WORD)-2);
2928 StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2932 * Write the information to the header.
2934 StorageUtl_WriteWord(
2935 headerBigBlock,
2936 OFFSET_BIGBLOCKSIZEBITS,
2937 This->bigBlockSizeBits);
2939 StorageUtl_WriteWord(
2940 headerBigBlock,
2941 OFFSET_SMALLBLOCKSIZEBITS,
2942 This->smallBlockSizeBits);
2944 StorageUtl_WriteDWord(
2945 headerBigBlock,
2946 OFFSET_BBDEPOTCOUNT,
2947 This->bigBlockDepotCount);
2949 StorageUtl_WriteDWord(
2950 headerBigBlock,
2951 OFFSET_ROOTSTARTBLOCK,
2952 This->rootStartBlock);
2954 StorageUtl_WriteDWord(
2955 headerBigBlock,
2956 OFFSET_SBDEPOTSTART,
2957 This->smallBlockDepotStart);
2959 StorageUtl_WriteDWord(
2960 headerBigBlock,
2961 OFFSET_SBDEPOTCOUNT,
2962 This->smallBlockDepotChain ?
2963 BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
2965 StorageUtl_WriteDWord(
2966 headerBigBlock,
2967 OFFSET_EXTBBDEPOTSTART,
2968 This->extBigBlockDepotStart);
2970 StorageUtl_WriteDWord(
2971 headerBigBlock,
2972 OFFSET_EXTBBDEPOTCOUNT,
2973 This->extBigBlockDepotCount);
2975 for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2977 StorageUtl_WriteDWord(
2978 headerBigBlock,
2979 OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2980 (This->bigBlockDepotStart[index]));
2984 * Write the big block back to the file.
2986 StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2989 /******************************************************************************
2990 * StorageImpl_ReadRawDirEntry
2992 * This method will read the raw data from a directory entry in the file.
2994 * buffer must be PROPSET_BLOCK_SIZE bytes long.
2996 HRESULT StorageImpl_ReadRawDirEntry(StorageImpl *This, ULONG index, BYTE *buffer)
2998 ULARGE_INTEGER offset;
2999 HRESULT hr;
3000 ULONG bytesRead;
3002 offset.u.HighPart = 0;
3003 offset.u.LowPart = index * PROPSET_BLOCK_SIZE;
3005 hr = BlockChainStream_ReadAt(
3006 This->rootBlockChain,
3007 offset,
3008 PROPSET_BLOCK_SIZE,
3009 buffer,
3010 &bytesRead);
3012 return hr;
3015 /******************************************************************************
3016 * StorageImpl_WriteRawDirEntry
3018 * This method will write the raw data from a directory entry in the file.
3020 * buffer must be PROPSET_BLOCK_SIZE bytes long.
3022 HRESULT StorageImpl_WriteRawDirEntry(StorageImpl *This, ULONG index, const BYTE *buffer)
3024 ULARGE_INTEGER offset;
3025 HRESULT hr;
3026 ULONG bytesRead;
3028 offset.u.HighPart = 0;
3029 offset.u.LowPart = index * PROPSET_BLOCK_SIZE;
3031 hr = BlockChainStream_WriteAt(
3032 This->rootBlockChain,
3033 offset,
3034 PROPSET_BLOCK_SIZE,
3035 buffer,
3036 &bytesRead);
3038 return hr;
3041 /******************************************************************************
3042 * UpdateRawDirEntry
3044 * Update raw directory entry data from the fields in newData.
3046 * buffer must be PROPSET_BLOCK_SIZE bytes long.
3048 void UpdateRawDirEntry(BYTE *buffer, const DirEntry *newData)
3050 memset(buffer, 0, PROPSET_BLOCK_SIZE);
3052 memcpy(
3053 buffer + OFFSET_PS_NAME,
3054 newData->name,
3055 PROPERTY_NAME_BUFFER_LEN );
3057 memcpy(buffer + OFFSET_PS_STGTYPE, &newData->propertyType, 1);
3059 StorageUtl_WriteWord(
3060 buffer,
3061 OFFSET_PS_NAMELENGTH,
3062 newData->sizeOfNameString);
3064 StorageUtl_WriteDWord(
3065 buffer,
3066 OFFSET_PS_LEFTCHILD,
3067 newData->leftChild);
3069 StorageUtl_WriteDWord(
3070 buffer,
3071 OFFSET_PS_RIGHTCHILD,
3072 newData->rightChild);
3074 StorageUtl_WriteDWord(
3075 buffer,
3076 OFFSET_PS_DIRPROP,
3077 newData->dirProperty);
3079 StorageUtl_WriteGUID(
3080 buffer,
3081 OFFSET_PS_GUID,
3082 &newData->propertyUniqueID);
3084 StorageUtl_WriteDWord(
3085 buffer,
3086 OFFSET_PS_CTIMELOW,
3087 newData->ctime.dwLowDateTime);
3089 StorageUtl_WriteDWord(
3090 buffer,
3091 OFFSET_PS_CTIMEHIGH,
3092 newData->ctime.dwHighDateTime);
3094 StorageUtl_WriteDWord(
3095 buffer,
3096 OFFSET_PS_MTIMELOW,
3097 newData->mtime.dwLowDateTime);
3099 StorageUtl_WriteDWord(
3100 buffer,
3101 OFFSET_PS_MTIMEHIGH,
3102 newData->ctime.dwHighDateTime);
3104 StorageUtl_WriteDWord(
3105 buffer,
3106 OFFSET_PS_STARTBLOCK,
3107 newData->startingBlock);
3109 StorageUtl_WriteDWord(
3110 buffer,
3111 OFFSET_PS_SIZE,
3112 newData->size.u.LowPart);
3115 /******************************************************************************
3116 * Storage32Impl_ReadProperty
3118 * This method will read the specified property from the property chain.
3120 BOOL StorageImpl_ReadDirEntry(
3121 StorageImpl* This,
3122 ULONG index,
3123 DirEntry* buffer)
3125 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3126 HRESULT readRes;
3128 readRes = StorageImpl_ReadRawDirEntry(This, index, currentProperty);
3130 if (SUCCEEDED(readRes))
3132 /* replace the name of root entry (often "Root Entry") by the file name */
3133 WCHAR *propName = (index == This->base.rootPropertySetIndex) ?
3134 This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3136 memset(buffer->name, 0, sizeof(buffer->name));
3137 memcpy(
3138 buffer->name,
3139 propName,
3140 PROPERTY_NAME_BUFFER_LEN );
3141 TRACE("storage name: %s\n", debugstr_w(buffer->name));
3143 memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_STGTYPE, 1);
3145 StorageUtl_ReadWord(
3146 currentProperty,
3147 OFFSET_PS_NAMELENGTH,
3148 &buffer->sizeOfNameString);
3150 StorageUtl_ReadDWord(
3151 currentProperty,
3152 OFFSET_PS_LEFTCHILD,
3153 &buffer->leftChild);
3155 StorageUtl_ReadDWord(
3156 currentProperty,
3157 OFFSET_PS_RIGHTCHILD,
3158 &buffer->rightChild);
3160 StorageUtl_ReadDWord(
3161 currentProperty,
3162 OFFSET_PS_DIRPROP,
3163 &buffer->dirProperty);
3165 StorageUtl_ReadGUID(
3166 currentProperty,
3167 OFFSET_PS_GUID,
3168 &buffer->propertyUniqueID);
3170 StorageUtl_ReadDWord(
3171 currentProperty,
3172 OFFSET_PS_CTIMELOW,
3173 &buffer->ctime.dwLowDateTime);
3175 StorageUtl_ReadDWord(
3176 currentProperty,
3177 OFFSET_PS_CTIMEHIGH,
3178 &buffer->ctime.dwHighDateTime);
3180 StorageUtl_ReadDWord(
3181 currentProperty,
3182 OFFSET_PS_MTIMELOW,
3183 &buffer->mtime.dwLowDateTime);
3185 StorageUtl_ReadDWord(
3186 currentProperty,
3187 OFFSET_PS_MTIMEHIGH,
3188 &buffer->mtime.dwHighDateTime);
3190 StorageUtl_ReadDWord(
3191 currentProperty,
3192 OFFSET_PS_STARTBLOCK,
3193 &buffer->startingBlock);
3195 StorageUtl_ReadDWord(
3196 currentProperty,
3197 OFFSET_PS_SIZE,
3198 &buffer->size.u.LowPart);
3200 buffer->size.u.HighPart = 0;
3203 return SUCCEEDED(readRes) ? TRUE : FALSE;
3206 /*********************************************************************
3207 * Write the specified property into the property chain
3209 BOOL StorageImpl_WriteDirEntry(
3210 StorageImpl* This,
3211 ULONG index,
3212 const DirEntry* buffer)
3214 BYTE currentProperty[PROPSET_BLOCK_SIZE];
3215 HRESULT writeRes;
3217 UpdateRawDirEntry(currentProperty, buffer);
3219 writeRes = StorageImpl_WriteRawDirEntry(This, index, currentProperty);
3220 return SUCCEEDED(writeRes) ? TRUE : FALSE;
3223 static BOOL StorageImpl_ReadBigBlock(
3224 StorageImpl* This,
3225 ULONG blockIndex,
3226 void* buffer)
3228 ULARGE_INTEGER ulOffset;
3229 DWORD read;
3231 ulOffset.u.HighPart = 0;
3232 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3234 StorageImpl_ReadAt(This, ulOffset, buffer, This->bigBlockSize, &read);
3235 return (read == This->bigBlockSize);
3238 static BOOL StorageImpl_ReadDWordFromBigBlock(
3239 StorageImpl* This,
3240 ULONG blockIndex,
3241 ULONG offset,
3242 DWORD* value)
3244 ULARGE_INTEGER ulOffset;
3245 DWORD read;
3246 DWORD tmp;
3248 ulOffset.u.HighPart = 0;
3249 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3250 ulOffset.u.LowPart += offset;
3252 StorageImpl_ReadAt(This, ulOffset, &tmp, sizeof(DWORD), &read);
3253 *value = lendian32toh(tmp);
3254 return (read == sizeof(DWORD));
3257 static BOOL StorageImpl_WriteBigBlock(
3258 StorageImpl* This,
3259 ULONG blockIndex,
3260 const void* buffer)
3262 ULARGE_INTEGER ulOffset;
3263 DWORD wrote;
3265 ulOffset.u.HighPart = 0;
3266 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3268 StorageImpl_WriteAt(This, ulOffset, buffer, This->bigBlockSize, &wrote);
3269 return (wrote == This->bigBlockSize);
3272 static BOOL StorageImpl_WriteDWordToBigBlock(
3273 StorageImpl* This,
3274 ULONG blockIndex,
3275 ULONG offset,
3276 DWORD value)
3278 ULARGE_INTEGER ulOffset;
3279 DWORD wrote;
3281 ulOffset.u.HighPart = 0;
3282 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex);
3283 ulOffset.u.LowPart += offset;
3285 value = htole32(value);
3286 StorageImpl_WriteAt(This, ulOffset, &value, sizeof(DWORD), &wrote);
3287 return (wrote == sizeof(DWORD));
3290 /******************************************************************************
3291 * Storage32Impl_SmallBlocksToBigBlocks
3293 * This method will convert a small block chain to a big block chain.
3294 * The small block chain will be destroyed.
3296 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3297 StorageImpl* This,
3298 SmallBlockChainStream** ppsbChain)
3300 ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3301 ULARGE_INTEGER size, offset;
3302 ULONG cbRead, cbWritten;
3303 ULARGE_INTEGER cbTotalRead;
3304 ULONG propertyIndex;
3305 HRESULT resWrite = S_OK;
3306 HRESULT resRead;
3307 DirEntry chainProperty;
3308 BYTE *buffer;
3309 BlockChainStream *bbTempChain = NULL;
3310 BlockChainStream *bigBlockChain = NULL;
3313 * Create a temporary big block chain that doesn't have
3314 * an associated property. This temporary chain will be
3315 * used to copy data from small blocks to big blocks.
3317 bbTempChain = BlockChainStream_Construct(This,
3318 &bbHeadOfChain,
3319 PROPERTY_NULL);
3320 if(!bbTempChain) return NULL;
3322 * Grow the big block chain.
3324 size = SmallBlockChainStream_GetSize(*ppsbChain);
3325 BlockChainStream_SetSize(bbTempChain, size);
3328 * Copy the contents of the small block chain to the big block chain
3329 * by small block size increments.
3331 offset.u.LowPart = 0;
3332 offset.u.HighPart = 0;
3333 cbTotalRead.QuadPart = 0;
3335 buffer = HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3338 resRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3339 offset,
3340 min(This->smallBlockSize, size.u.LowPart - offset.u.LowPart),
3341 buffer,
3342 &cbRead);
3343 if (FAILED(resRead))
3344 break;
3346 if (cbRead > 0)
3348 cbTotalRead.QuadPart += cbRead;
3350 resWrite = BlockChainStream_WriteAt(bbTempChain,
3351 offset,
3352 cbRead,
3353 buffer,
3354 &cbWritten);
3356 if (FAILED(resWrite))
3357 break;
3359 offset.u.LowPart += cbRead;
3361 } while (cbTotalRead.QuadPart < size.QuadPart);
3362 HeapFree(GetProcessHeap(),0,buffer);
3364 size.u.HighPart = 0;
3365 size.u.LowPart = 0;
3367 if (FAILED(resRead) || FAILED(resWrite))
3369 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3370 BlockChainStream_SetSize(bbTempChain, size);
3371 BlockChainStream_Destroy(bbTempChain);
3372 return NULL;
3376 * Destroy the small block chain.
3378 propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3379 SmallBlockChainStream_SetSize(*ppsbChain, size);
3380 SmallBlockChainStream_Destroy(*ppsbChain);
3381 *ppsbChain = 0;
3384 * Change the property information. This chain is now a big block chain
3385 * and it doesn't reside in the small blocks chain anymore.
3387 StorageImpl_ReadDirEntry(This, propertyIndex, &chainProperty);
3389 chainProperty.startingBlock = bbHeadOfChain;
3391 StorageImpl_WriteDirEntry(This, propertyIndex, &chainProperty);
3394 * Destroy the temporary propertyless big block chain.
3395 * Create a new big block chain associated with this property.
3397 BlockChainStream_Destroy(bbTempChain);
3398 bigBlockChain = BlockChainStream_Construct(This,
3399 NULL,
3400 propertyIndex);
3402 return bigBlockChain;
3405 /******************************************************************************
3406 * Storage32Impl_BigBlocksToSmallBlocks
3408 * This method will convert a big block chain to a small block chain.
3409 * The big block chain will be destroyed on success.
3411 SmallBlockChainStream* Storage32Impl_BigBlocksToSmallBlocks(
3412 StorageImpl* This,
3413 BlockChainStream** ppbbChain)
3415 ULARGE_INTEGER size, offset, cbTotalRead;
3416 ULONG cbRead, cbWritten, propertyIndex, sbHeadOfChain = BLOCK_END_OF_CHAIN;
3417 HRESULT resWrite = S_OK, resRead;
3418 DirEntry chainProperty;
3419 BYTE* buffer;
3420 SmallBlockChainStream* sbTempChain;
3422 TRACE("%p %p\n", This, ppbbChain);
3424 sbTempChain = SmallBlockChainStream_Construct(This, &sbHeadOfChain,
3425 PROPERTY_NULL);
3427 if(!sbTempChain)
3428 return NULL;
3430 size = BlockChainStream_GetSize(*ppbbChain);
3431 SmallBlockChainStream_SetSize(sbTempChain, size);
3433 offset.u.HighPart = 0;
3434 offset.u.LowPart = 0;
3435 cbTotalRead.QuadPart = 0;
3436 buffer = HeapAlloc(GetProcessHeap(), 0, This->bigBlockSize);
3439 resRead = BlockChainStream_ReadAt(*ppbbChain, offset,
3440 min(This->bigBlockSize, size.u.LowPart - offset.u.LowPart),
3441 buffer, &cbRead);
3443 if(FAILED(resRead))
3444 break;
3446 if(cbRead > 0)
3448 cbTotalRead.QuadPart += cbRead;
3450 resWrite = SmallBlockChainStream_WriteAt(sbTempChain, offset,
3451 cbRead, buffer, &cbWritten);
3453 if(FAILED(resWrite))
3454 break;
3456 offset.u.LowPart += cbRead;
3458 }while(cbTotalRead.QuadPart < size.QuadPart);
3459 HeapFree(GetProcessHeap(), 0, buffer);
3461 size.u.HighPart = 0;
3462 size.u.LowPart = 0;
3464 if(FAILED(resRead) || FAILED(resWrite))
3466 ERR("conversion failed: resRead = 0x%08x, resWrite = 0x%08x\n", resRead, resWrite);
3467 SmallBlockChainStream_SetSize(sbTempChain, size);
3468 SmallBlockChainStream_Destroy(sbTempChain);
3469 return NULL;
3472 /* destroy the original big block chain */
3473 propertyIndex = (*ppbbChain)->ownerPropertyIndex;
3474 BlockChainStream_SetSize(*ppbbChain, size);
3475 BlockChainStream_Destroy(*ppbbChain);
3476 *ppbbChain = NULL;
3478 StorageImpl_ReadDirEntry(This, propertyIndex, &chainProperty);
3479 chainProperty.startingBlock = sbHeadOfChain;
3480 StorageImpl_WriteDirEntry(This, propertyIndex, &chainProperty);
3482 SmallBlockChainStream_Destroy(sbTempChain);
3483 return SmallBlockChainStream_Construct(This, NULL, propertyIndex);
3486 static void StorageInternalImpl_Destroy( StorageBaseImpl *iface)
3488 StorageInternalImpl* This = (StorageInternalImpl*) iface;
3490 HeapFree(GetProcessHeap(), 0, This);
3493 /******************************************************************************
3495 ** Storage32InternalImpl_Commit
3498 static HRESULT WINAPI StorageInternalImpl_Commit(
3499 IStorage* iface,
3500 DWORD grfCommitFlags) /* [in] */
3502 FIXME("(%p,%x): stub\n", iface, grfCommitFlags);
3503 return S_OK;
3506 /******************************************************************************
3508 ** Storage32InternalImpl_Revert
3511 static HRESULT WINAPI StorageInternalImpl_Revert(
3512 IStorage* iface)
3514 FIXME("(%p): stub\n", iface);
3515 return S_OK;
3518 static void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3520 IStorage_Release((IStorage*)This->parentStorage);
3521 HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3522 HeapFree(GetProcessHeap(), 0, This);
3525 static HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3526 IEnumSTATSTG* iface,
3527 REFIID riid,
3528 void** ppvObject)
3530 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3532 if (ppvObject==0)
3533 return E_INVALIDARG;
3535 *ppvObject = 0;
3537 if (IsEqualGUID(&IID_IUnknown, riid) ||
3538 IsEqualGUID(&IID_IEnumSTATSTG, riid))
3540 *ppvObject = This;
3541 IEnumSTATSTG_AddRef((IEnumSTATSTG*)This);
3542 return S_OK;
3545 return E_NOINTERFACE;
3548 static ULONG WINAPI IEnumSTATSTGImpl_AddRef(
3549 IEnumSTATSTG* iface)
3551 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3552 return InterlockedIncrement(&This->ref);
3555 static ULONG WINAPI IEnumSTATSTGImpl_Release(
3556 IEnumSTATSTG* iface)
3558 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3560 ULONG newRef;
3562 newRef = InterlockedDecrement(&This->ref);
3564 if (newRef==0)
3566 IEnumSTATSTGImpl_Destroy(This);
3569 return newRef;
3572 static HRESULT WINAPI IEnumSTATSTGImpl_Next(
3573 IEnumSTATSTG* iface,
3574 ULONG celt,
3575 STATSTG* rgelt,
3576 ULONG* pceltFetched)
3578 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3580 DirEntry currentProperty;
3581 STATSTG* currentReturnStruct = rgelt;
3582 ULONG objectFetched = 0;
3583 ULONG currentSearchNode;
3585 if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3586 return E_INVALIDARG;
3589 * To avoid the special case, get another pointer to a ULONG value if
3590 * the caller didn't supply one.
3592 if (pceltFetched==0)
3593 pceltFetched = &objectFetched;
3596 * Start the iteration, we will iterate until we hit the end of the
3597 * linked list or until we hit the number of items to iterate through
3599 *pceltFetched = 0;
3602 * Start with the node at the top of the stack.
3604 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3606 while ( ( *pceltFetched < celt) &&
3607 ( currentSearchNode!=PROPERTY_NULL) )
3610 * Remove the top node from the stack
3612 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3615 * Read the property from the storage.
3617 StorageImpl_ReadDirEntry(This->parentStorage,
3618 currentSearchNode,
3619 &currentProperty);
3622 * Copy the information to the return buffer.
3624 StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3625 &currentProperty,
3626 STATFLAG_DEFAULT);
3629 * Step to the next item in the iteration
3631 (*pceltFetched)++;
3632 currentReturnStruct++;
3635 * Push the next search node in the search stack.
3637 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.rightChild);
3640 * continue the iteration.
3642 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3645 if (*pceltFetched == celt)
3646 return S_OK;
3648 return S_FALSE;
3652 static HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3653 IEnumSTATSTG* iface,
3654 ULONG celt)
3656 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3658 DirEntry currentProperty;
3659 ULONG objectFetched = 0;
3660 ULONG currentSearchNode;
3663 * Start with the node at the top of the stack.
3665 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3667 while ( (objectFetched < celt) &&
3668 (currentSearchNode!=PROPERTY_NULL) )
3671 * Remove the top node from the stack
3673 IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3676 * Read the property from the storage.
3678 StorageImpl_ReadDirEntry(This->parentStorage,
3679 currentSearchNode,
3680 &currentProperty);
3683 * Step to the next item in the iteration
3685 objectFetched++;
3688 * Push the next search node in the search stack.
3690 IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.rightChild);
3693 * continue the iteration.
3695 currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3698 if (objectFetched == celt)
3699 return S_OK;
3701 return S_FALSE;
3704 static HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3705 IEnumSTATSTG* iface)
3707 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3709 DirEntry rootProperty;
3710 BOOL readSuccessful;
3713 * Re-initialize the search stack to an empty stack
3715 This->stackSize = 0;
3718 * Read the root property from the storage.
3720 readSuccessful = StorageImpl_ReadDirEntry(
3721 This->parentStorage,
3722 This->firstPropertyNode,
3723 &rootProperty);
3725 if (readSuccessful)
3727 assert(rootProperty.sizeOfNameString!=0);
3730 * Push the search node in the search stack.
3732 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3735 return S_OK;
3738 static HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3739 IEnumSTATSTG* iface,
3740 IEnumSTATSTG** ppenum)
3742 IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3744 IEnumSTATSTGImpl* newClone;
3747 * Perform a sanity check on the parameters.
3749 if (ppenum==0)
3750 return E_INVALIDARG;
3752 newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3753 This->firstPropertyNode);
3757 * The new clone enumeration must point to the same current node as
3758 * the ole one.
3760 newClone->stackSize = This->stackSize ;
3761 newClone->stackMaxSize = This->stackMaxSize ;
3762 newClone->stackToVisit =
3763 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3765 memcpy(
3766 newClone->stackToVisit,
3767 This->stackToVisit,
3768 sizeof(ULONG) * newClone->stackSize);
3770 *ppenum = (IEnumSTATSTG*)newClone;
3773 * Don't forget to nail down a reference to the clone before
3774 * returning it.
3776 IEnumSTATSTGImpl_AddRef(*ppenum);
3778 return S_OK;
3781 static void IEnumSTATSTGImpl_PushSearchNode(
3782 IEnumSTATSTGImpl* This,
3783 ULONG nodeToPush)
3785 DirEntry rootProperty;
3786 BOOL readSuccessful;
3789 * First, make sure we're not trying to push an unexisting node.
3791 if (nodeToPush==PROPERTY_NULL)
3792 return;
3795 * First push the node to the stack
3797 if (This->stackSize == This->stackMaxSize)
3799 This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3801 This->stackToVisit = HeapReAlloc(
3802 GetProcessHeap(),
3804 This->stackToVisit,
3805 sizeof(ULONG) * This->stackMaxSize);
3808 This->stackToVisit[This->stackSize] = nodeToPush;
3809 This->stackSize++;
3812 * Read the root property from the storage.
3814 readSuccessful = StorageImpl_ReadDirEntry(
3815 This->parentStorage,
3816 nodeToPush,
3817 &rootProperty);
3819 if (readSuccessful)
3821 assert(rootProperty.sizeOfNameString!=0);
3824 * Push the previous search node in the search stack.
3826 IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.leftChild);
3830 static ULONG IEnumSTATSTGImpl_PopSearchNode(
3831 IEnumSTATSTGImpl* This,
3832 BOOL remove)
3834 ULONG topNode;
3836 if (This->stackSize == 0)
3837 return PROPERTY_NULL;
3839 topNode = This->stackToVisit[This->stackSize-1];
3841 if (remove)
3842 This->stackSize--;
3844 return topNode;
3848 * Virtual function table for the IEnumSTATSTGImpl class.
3850 static const IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
3852 IEnumSTATSTGImpl_QueryInterface,
3853 IEnumSTATSTGImpl_AddRef,
3854 IEnumSTATSTGImpl_Release,
3855 IEnumSTATSTGImpl_Next,
3856 IEnumSTATSTGImpl_Skip,
3857 IEnumSTATSTGImpl_Reset,
3858 IEnumSTATSTGImpl_Clone
3861 /******************************************************************************
3862 ** IEnumSTATSTGImpl implementation
3865 static IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3866 StorageImpl* parentStorage,
3867 ULONG firstPropertyNode)
3869 IEnumSTATSTGImpl* newEnumeration;
3871 newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3873 if (newEnumeration!=0)
3876 * Set-up the virtual function table and reference count.
3878 newEnumeration->lpVtbl = &IEnumSTATSTGImpl_Vtbl;
3879 newEnumeration->ref = 0;
3882 * We want to nail-down the reference to the storage in case the
3883 * enumeration out-lives the storage in the client application.
3885 newEnumeration->parentStorage = parentStorage;
3886 IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3888 newEnumeration->firstPropertyNode = firstPropertyNode;
3891 * Initialize the search stack
3893 newEnumeration->stackSize = 0;
3894 newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3895 newEnumeration->stackToVisit =
3896 HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3899 * Make sure the current node of the iterator is the first one.
3901 IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3904 return newEnumeration;
3908 * Virtual function table for the Storage32InternalImpl class.
3910 static const IStorageVtbl Storage32InternalImpl_Vtbl =
3912 StorageBaseImpl_QueryInterface,
3913 StorageBaseImpl_AddRef,
3914 StorageBaseImpl_Release,
3915 StorageBaseImpl_CreateStream,
3916 StorageBaseImpl_OpenStream,
3917 StorageBaseImpl_CreateStorage,
3918 StorageBaseImpl_OpenStorage,
3919 StorageBaseImpl_CopyTo,
3920 StorageBaseImpl_MoveElementTo,
3921 StorageInternalImpl_Commit,
3922 StorageInternalImpl_Revert,
3923 StorageBaseImpl_EnumElements,
3924 StorageBaseImpl_DestroyElement,
3925 StorageBaseImpl_RenameElement,
3926 StorageBaseImpl_SetElementTimes,
3927 StorageBaseImpl_SetClass,
3928 StorageBaseImpl_SetStateBits,
3929 StorageBaseImpl_Stat
3932 /******************************************************************************
3933 ** Storage32InternalImpl implementation
3936 static StorageInternalImpl* StorageInternalImpl_Construct(
3937 StorageImpl* ancestorStorage,
3938 DWORD openFlags,
3939 ULONG rootPropertyIndex)
3941 StorageInternalImpl* newStorage;
3943 newStorage = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(StorageInternalImpl));
3945 if (newStorage!=0)
3948 * Initialize the stream list
3950 list_init(&newStorage->base.strmHead);
3953 * Initialize the virtual function table.
3955 newStorage->base.lpVtbl = &Storage32InternalImpl_Vtbl;
3956 newStorage->base.v_destructor = StorageInternalImpl_Destroy;
3957 newStorage->base.openFlags = (openFlags & ~STGM_CREATE);
3960 * Keep the ancestor storage pointer but do not nail a reference to it.
3962 newStorage->base.ancestorStorage = ancestorStorage;
3965 * Keep the index of the root property set for this storage,
3967 newStorage->base.rootPropertySetIndex = rootPropertyIndex;
3969 return newStorage;
3972 return 0;
3975 /******************************************************************************
3976 ** StorageUtl implementation
3979 void StorageUtl_ReadWord(const BYTE* buffer, ULONG offset, WORD* value)
3981 WORD tmp;
3983 memcpy(&tmp, buffer+offset, sizeof(WORD));
3984 *value = lendian16toh(tmp);
3987 void StorageUtl_WriteWord(BYTE* buffer, ULONG offset, WORD value)
3989 value = htole16(value);
3990 memcpy(buffer+offset, &value, sizeof(WORD));
3993 void StorageUtl_ReadDWord(const BYTE* buffer, ULONG offset, DWORD* value)
3995 DWORD tmp;
3997 memcpy(&tmp, buffer+offset, sizeof(DWORD));
3998 *value = lendian32toh(tmp);
4001 void StorageUtl_WriteDWord(BYTE* buffer, ULONG offset, DWORD value)
4003 value = htole32(value);
4004 memcpy(buffer+offset, &value, sizeof(DWORD));
4007 void StorageUtl_ReadULargeInteger(const BYTE* buffer, ULONG offset,
4008 ULARGE_INTEGER* value)
4010 #ifdef WORDS_BIGENDIAN
4011 ULARGE_INTEGER tmp;
4013 memcpy(&tmp, buffer + offset, sizeof(ULARGE_INTEGER));
4014 value->u.LowPart = htole32(tmp.u.HighPart);
4015 value->u.HighPart = htole32(tmp.u.LowPart);
4016 #else
4017 memcpy(value, buffer + offset, sizeof(ULARGE_INTEGER));
4018 #endif
4021 void StorageUtl_WriteULargeInteger(BYTE* buffer, ULONG offset,
4022 const ULARGE_INTEGER *value)
4024 #ifdef WORDS_BIGENDIAN
4025 ULARGE_INTEGER tmp;
4027 tmp.u.LowPart = htole32(value->u.HighPart);
4028 tmp.u.HighPart = htole32(value->u.LowPart);
4029 memcpy(buffer + offset, &tmp, sizeof(ULARGE_INTEGER));
4030 #else
4031 memcpy(buffer + offset, value, sizeof(ULARGE_INTEGER));
4032 #endif
4035 void StorageUtl_ReadGUID(const BYTE* buffer, ULONG offset, GUID* value)
4037 StorageUtl_ReadDWord(buffer, offset, &(value->Data1));
4038 StorageUtl_ReadWord(buffer, offset+4, &(value->Data2));
4039 StorageUtl_ReadWord(buffer, offset+6, &(value->Data3));
4041 memcpy(value->Data4, buffer+offset+8, sizeof(value->Data4));
4044 void StorageUtl_WriteGUID(BYTE* buffer, ULONG offset, const GUID* value)
4046 StorageUtl_WriteDWord(buffer, offset, value->Data1);
4047 StorageUtl_WriteWord(buffer, offset+4, value->Data2);
4048 StorageUtl_WriteWord(buffer, offset+6, value->Data3);
4050 memcpy(buffer+offset+8, value->Data4, sizeof(value->Data4));
4053 void StorageUtl_CopyPropertyToSTATSTG(
4054 STATSTG* destination,
4055 const DirEntry* source,
4056 int statFlags)
4059 * The copy of the string occurs only when the flag is not set
4061 if( ((statFlags & STATFLAG_NONAME) != 0) ||
4062 (source->name == NULL) ||
4063 (source->name[0] == 0) )
4065 destination->pwcsName = 0;
4067 else
4069 destination->pwcsName =
4070 CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4072 strcpyW(destination->pwcsName, source->name);
4075 switch (source->propertyType)
4077 case STGTY_STORAGE:
4078 case STGTY_ROOT:
4079 destination->type = STGTY_STORAGE;
4080 break;
4081 case STGTY_STREAM:
4082 destination->type = STGTY_STREAM;
4083 break;
4084 default:
4085 destination->type = STGTY_STREAM;
4086 break;
4089 destination->cbSize = source->size;
4091 currentReturnStruct->mtime = {0}; TODO
4092 currentReturnStruct->ctime = {0};
4093 currentReturnStruct->atime = {0};
4095 destination->grfMode = 0;
4096 destination->grfLocksSupported = 0;
4097 destination->clsid = source->propertyUniqueID;
4098 destination->grfStateBits = 0;
4099 destination->reserved = 0;
4102 /******************************************************************************
4103 ** BlockChainStream implementation
4106 BlockChainStream* BlockChainStream_Construct(
4107 StorageImpl* parentStorage,
4108 ULONG* headOfStreamPlaceHolder,
4109 ULONG propertyIndex)
4111 BlockChainStream* newStream;
4112 ULONG blockIndex;
4114 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4116 newStream->parentStorage = parentStorage;
4117 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4118 newStream->ownerPropertyIndex = propertyIndex;
4119 newStream->lastBlockNoInSequence = 0xFFFFFFFF;
4120 newStream->tailIndex = BLOCK_END_OF_CHAIN;
4121 newStream->numBlocks = 0;
4123 blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4125 while (blockIndex != BLOCK_END_OF_CHAIN)
4127 newStream->numBlocks++;
4128 newStream->tailIndex = blockIndex;
4130 if(FAILED(StorageImpl_GetNextBlockInChain(
4131 parentStorage,
4132 blockIndex,
4133 &blockIndex)))
4135 HeapFree(GetProcessHeap(), 0, newStream);
4136 return NULL;
4140 return newStream;
4143 void BlockChainStream_Destroy(BlockChainStream* This)
4145 HeapFree(GetProcessHeap(), 0, This);
4148 /******************************************************************************
4149 * BlockChainStream_GetHeadOfChain
4151 * Returns the head of this stream chain.
4152 * Some special chains don't have properties, their heads are kept in
4153 * This->headOfStreamPlaceHolder.
4156 static ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4158 DirEntry chainProperty;
4159 BOOL readSuccessful;
4161 if (This->headOfStreamPlaceHolder != 0)
4162 return *(This->headOfStreamPlaceHolder);
4164 if (This->ownerPropertyIndex != PROPERTY_NULL)
4166 readSuccessful = StorageImpl_ReadDirEntry(
4167 This->parentStorage,
4168 This->ownerPropertyIndex,
4169 &chainProperty);
4171 if (readSuccessful)
4173 return chainProperty.startingBlock;
4177 return BLOCK_END_OF_CHAIN;
4180 /******************************************************************************
4181 * BlockChainStream_GetCount
4183 * Returns the number of blocks that comprises this chain.
4184 * This is not the size of the stream as the last block may not be full!
4187 static ULONG BlockChainStream_GetCount(BlockChainStream* This)
4189 ULONG blockIndex;
4190 ULONG count = 0;
4192 blockIndex = BlockChainStream_GetHeadOfChain(This);
4194 while (blockIndex != BLOCK_END_OF_CHAIN)
4196 count++;
4198 if(FAILED(StorageImpl_GetNextBlockInChain(
4199 This->parentStorage,
4200 blockIndex,
4201 &blockIndex)))
4202 return 0;
4205 return count;
4208 /******************************************************************************
4209 * BlockChainStream_ReadAt
4211 * Reads a specified number of bytes from this chain at the specified offset.
4212 * bytesRead may be NULL.
4213 * Failure will be returned if the specified number of bytes has not been read.
4215 HRESULT BlockChainStream_ReadAt(BlockChainStream* This,
4216 ULARGE_INTEGER offset,
4217 ULONG size,
4218 void* buffer,
4219 ULONG* bytesRead)
4221 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4222 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4223 ULONG bytesToReadInBuffer;
4224 ULONG blockIndex;
4225 BYTE* bufferWalker;
4227 TRACE("(%p)-> %i %p %i %p\n",This, offset.u.LowPart, buffer, size, bytesRead);
4230 * Find the first block in the stream that contains part of the buffer.
4232 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4233 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4234 (blockNoInSequence < This->lastBlockNoInSequence) )
4236 blockIndex = BlockChainStream_GetHeadOfChain(This);
4237 This->lastBlockNoInSequence = blockNoInSequence;
4239 else
4241 ULONG temp = blockNoInSequence;
4243 blockIndex = This->lastBlockNoInSequenceIndex;
4244 blockNoInSequence -= This->lastBlockNoInSequence;
4245 This->lastBlockNoInSequence = temp;
4248 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4250 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4251 return STG_E_DOCFILECORRUPT;
4252 blockNoInSequence--;
4255 if ((blockNoInSequence > 0) && (blockIndex == BLOCK_END_OF_CHAIN))
4256 return STG_E_DOCFILECORRUPT; /* We failed to find the starting block */
4258 This->lastBlockNoInSequenceIndex = blockIndex;
4261 * Start reading the buffer.
4263 *bytesRead = 0;
4264 bufferWalker = buffer;
4266 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4268 ULARGE_INTEGER ulOffset;
4269 DWORD bytesReadAt;
4271 * Calculate how many bytes we can copy from this big block.
4273 bytesToReadInBuffer =
4274 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4276 TRACE("block %i\n",blockIndex);
4277 ulOffset.u.HighPart = 0;
4278 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4279 offsetInBlock;
4281 StorageImpl_ReadAt(This->parentStorage,
4282 ulOffset,
4283 bufferWalker,
4284 bytesToReadInBuffer,
4285 &bytesReadAt);
4287 * Step to the next big block.
4289 if( size > bytesReadAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4290 return STG_E_DOCFILECORRUPT;
4292 bufferWalker += bytesReadAt;
4293 size -= bytesReadAt;
4294 *bytesRead += bytesReadAt;
4295 offsetInBlock = 0; /* There is no offset on the next block */
4297 if (bytesToReadInBuffer != bytesReadAt)
4298 break;
4301 return (size == 0) ? S_OK : STG_E_READFAULT;
4304 /******************************************************************************
4305 * BlockChainStream_WriteAt
4307 * Writes the specified number of bytes to this chain at the specified offset.
4308 * Will fail if not all specified number of bytes have been written.
4310 HRESULT BlockChainStream_WriteAt(BlockChainStream* This,
4311 ULARGE_INTEGER offset,
4312 ULONG size,
4313 const void* buffer,
4314 ULONG* bytesWritten)
4316 ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4317 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->bigBlockSize;
4318 ULONG bytesToWrite;
4319 ULONG blockIndex;
4320 const BYTE* bufferWalker;
4323 * Find the first block in the stream that contains part of the buffer.
4325 if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4326 (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4327 (blockNoInSequence < This->lastBlockNoInSequence) )
4329 blockIndex = BlockChainStream_GetHeadOfChain(This);
4330 This->lastBlockNoInSequence = blockNoInSequence;
4332 else
4334 ULONG temp = blockNoInSequence;
4336 blockIndex = This->lastBlockNoInSequenceIndex;
4337 blockNoInSequence -= This->lastBlockNoInSequence;
4338 This->lastBlockNoInSequence = temp;
4341 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4343 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4344 &blockIndex)))
4345 return STG_E_DOCFILECORRUPT;
4346 blockNoInSequence--;
4349 This->lastBlockNoInSequenceIndex = blockIndex;
4351 /* BlockChainStream_SetSize should have already been called to ensure we have
4352 * enough blocks in the chain to write into */
4353 if (blockIndex == BLOCK_END_OF_CHAIN)
4355 ERR("not enough blocks in chain to write data\n");
4356 return STG_E_DOCFILECORRUPT;
4359 *bytesWritten = 0;
4360 bufferWalker = buffer;
4362 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4364 ULARGE_INTEGER ulOffset;
4365 DWORD bytesWrittenAt;
4367 * Calculate how many bytes we can copy from this big block.
4369 bytesToWrite =
4370 min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4372 TRACE("block %i\n",blockIndex);
4373 ulOffset.u.HighPart = 0;
4374 ulOffset.u.LowPart = BLOCK_GetBigBlockOffset(blockIndex) +
4375 offsetInBlock;
4377 StorageImpl_WriteAt(This->parentStorage,
4378 ulOffset,
4379 bufferWalker,
4380 bytesToWrite,
4381 &bytesWrittenAt);
4384 * Step to the next big block.
4386 if(size > bytesWrittenAt && FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4387 &blockIndex)))
4388 return STG_E_DOCFILECORRUPT;
4390 bufferWalker += bytesWrittenAt;
4391 size -= bytesWrittenAt;
4392 *bytesWritten += bytesWrittenAt;
4393 offsetInBlock = 0; /* There is no offset on the next block */
4395 if (bytesWrittenAt != bytesToWrite)
4396 break;
4399 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
4402 /******************************************************************************
4403 * BlockChainStream_Shrink
4405 * Shrinks this chain in the big block depot.
4407 static BOOL BlockChainStream_Shrink(BlockChainStream* This,
4408 ULARGE_INTEGER newSize)
4410 ULONG blockIndex, extraBlock;
4411 ULONG numBlocks;
4412 ULONG count = 1;
4415 * Reset the last accessed block cache.
4417 This->lastBlockNoInSequence = 0xFFFFFFFF;
4418 This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4421 * Figure out how many blocks are needed to contain the new size
4423 numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4425 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4426 numBlocks++;
4428 blockIndex = BlockChainStream_GetHeadOfChain(This);
4431 * Go to the new end of chain
4433 while (count < numBlocks)
4435 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4436 &blockIndex)))
4437 return FALSE;
4438 count++;
4441 /* Get the next block before marking the new end */
4442 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4443 &extraBlock)))
4444 return FALSE;
4446 /* Mark the new end of chain */
4447 StorageImpl_SetNextBlockInChain(
4448 This->parentStorage,
4449 blockIndex,
4450 BLOCK_END_OF_CHAIN);
4452 This->tailIndex = blockIndex;
4453 This->numBlocks = numBlocks;
4456 * Mark the extra blocks as free
4458 while (extraBlock != BLOCK_END_OF_CHAIN)
4460 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4461 &blockIndex)))
4462 return FALSE;
4463 StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4464 extraBlock = blockIndex;
4467 return TRUE;
4470 /******************************************************************************
4471 * BlockChainStream_Enlarge
4473 * Grows this chain in the big block depot.
4475 static BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4476 ULARGE_INTEGER newSize)
4478 ULONG blockIndex, currentBlock;
4479 ULONG newNumBlocks;
4480 ULONG oldNumBlocks = 0;
4482 blockIndex = BlockChainStream_GetHeadOfChain(This);
4485 * Empty chain. Create the head.
4487 if (blockIndex == BLOCK_END_OF_CHAIN)
4489 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4490 StorageImpl_SetNextBlockInChain(This->parentStorage,
4491 blockIndex,
4492 BLOCK_END_OF_CHAIN);
4494 if (This->headOfStreamPlaceHolder != 0)
4496 *(This->headOfStreamPlaceHolder) = blockIndex;
4498 else
4500 DirEntry chainProp;
4501 assert(This->ownerPropertyIndex != PROPERTY_NULL);
4503 StorageImpl_ReadDirEntry(
4504 This->parentStorage,
4505 This->ownerPropertyIndex,
4506 &chainProp);
4508 chainProp.startingBlock = blockIndex;
4510 StorageImpl_WriteDirEntry(
4511 This->parentStorage,
4512 This->ownerPropertyIndex,
4513 &chainProp);
4516 This->tailIndex = blockIndex;
4517 This->numBlocks = 1;
4521 * Figure out how many blocks are needed to contain this stream
4523 newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4525 if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4526 newNumBlocks++;
4529 * Go to the current end of chain
4531 if (This->tailIndex == BLOCK_END_OF_CHAIN)
4533 currentBlock = blockIndex;
4535 while (blockIndex != BLOCK_END_OF_CHAIN)
4537 This->numBlocks++;
4538 currentBlock = blockIndex;
4540 if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4541 &blockIndex)))
4542 return FALSE;
4545 This->tailIndex = currentBlock;
4548 currentBlock = This->tailIndex;
4549 oldNumBlocks = This->numBlocks;
4552 * Add new blocks to the chain
4554 if (oldNumBlocks < newNumBlocks)
4556 while (oldNumBlocks < newNumBlocks)
4558 blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4560 StorageImpl_SetNextBlockInChain(
4561 This->parentStorage,
4562 currentBlock,
4563 blockIndex);
4565 StorageImpl_SetNextBlockInChain(
4566 This->parentStorage,
4567 blockIndex,
4568 BLOCK_END_OF_CHAIN);
4570 currentBlock = blockIndex;
4571 oldNumBlocks++;
4574 This->tailIndex = blockIndex;
4575 This->numBlocks = newNumBlocks;
4578 return TRUE;
4581 /******************************************************************************
4582 * BlockChainStream_SetSize
4584 * Sets the size of this stream. The big block depot will be updated.
4585 * The file will grow if we grow the chain.
4587 * TODO: Free the actual blocks in the file when we shrink the chain.
4588 * Currently, the blocks are still in the file. So the file size
4589 * doesn't shrink even if we shrink streams.
4591 BOOL BlockChainStream_SetSize(
4592 BlockChainStream* This,
4593 ULARGE_INTEGER newSize)
4595 ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4597 if (newSize.u.LowPart == size.u.LowPart)
4598 return TRUE;
4600 if (newSize.u.LowPart < size.u.LowPart)
4602 BlockChainStream_Shrink(This, newSize);
4604 else
4606 BlockChainStream_Enlarge(This, newSize);
4609 return TRUE;
4612 /******************************************************************************
4613 * BlockChainStream_GetSize
4615 * Returns the size of this chain.
4616 * Will return the block count if this chain doesn't have a property.
4618 static ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4620 DirEntry chainProperty;
4622 if(This->headOfStreamPlaceHolder == NULL)
4625 * This chain is a data stream read the property and return
4626 * the appropriate size
4628 StorageImpl_ReadDirEntry(
4629 This->parentStorage,
4630 This->ownerPropertyIndex,
4631 &chainProperty);
4633 return chainProperty.size;
4635 else
4638 * this chain is a chain that does not have a property, figure out the
4639 * size by making the product number of used blocks times the
4640 * size of them
4642 ULARGE_INTEGER result;
4643 result.u.HighPart = 0;
4645 result.u.LowPart =
4646 BlockChainStream_GetCount(This) *
4647 This->parentStorage->bigBlockSize;
4649 return result;
4653 /******************************************************************************
4654 ** SmallBlockChainStream implementation
4657 SmallBlockChainStream* SmallBlockChainStream_Construct(
4658 StorageImpl* parentStorage,
4659 ULONG* headOfStreamPlaceHolder,
4660 ULONG propertyIndex)
4662 SmallBlockChainStream* newStream;
4664 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4666 newStream->parentStorage = parentStorage;
4667 newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4668 newStream->ownerPropertyIndex = propertyIndex;
4670 return newStream;
4673 void SmallBlockChainStream_Destroy(
4674 SmallBlockChainStream* This)
4676 HeapFree(GetProcessHeap(), 0, This);
4679 /******************************************************************************
4680 * SmallBlockChainStream_GetHeadOfChain
4682 * Returns the head of this chain of small blocks.
4684 static ULONG SmallBlockChainStream_GetHeadOfChain(
4685 SmallBlockChainStream* This)
4687 DirEntry chainProperty;
4688 BOOL readSuccessful;
4690 if (This->headOfStreamPlaceHolder != NULL)
4691 return *(This->headOfStreamPlaceHolder);
4693 if (This->ownerPropertyIndex)
4695 readSuccessful = StorageImpl_ReadDirEntry(
4696 This->parentStorage,
4697 This->ownerPropertyIndex,
4698 &chainProperty);
4700 if (readSuccessful)
4702 return chainProperty.startingBlock;
4707 return BLOCK_END_OF_CHAIN;
4710 /******************************************************************************
4711 * SmallBlockChainStream_GetNextBlockInChain
4713 * Returns the index of the next small block in this chain.
4715 * Return Values:
4716 * - BLOCK_END_OF_CHAIN: end of this chain
4717 * - BLOCK_UNUSED: small block 'blockIndex' is free
4719 static HRESULT SmallBlockChainStream_GetNextBlockInChain(
4720 SmallBlockChainStream* This,
4721 ULONG blockIndex,
4722 ULONG* nextBlockInChain)
4724 ULARGE_INTEGER offsetOfBlockInDepot;
4725 DWORD buffer;
4726 ULONG bytesRead;
4727 HRESULT res;
4729 *nextBlockInChain = BLOCK_END_OF_CHAIN;
4731 offsetOfBlockInDepot.u.HighPart = 0;
4732 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4735 * Read those bytes in the buffer from the small block file.
4737 res = BlockChainStream_ReadAt(
4738 This->parentStorage->smallBlockDepotChain,
4739 offsetOfBlockInDepot,
4740 sizeof(DWORD),
4741 &buffer,
4742 &bytesRead);
4744 if (SUCCEEDED(res))
4746 StorageUtl_ReadDWord((BYTE *)&buffer, 0, nextBlockInChain);
4747 return S_OK;
4750 return res;
4753 /******************************************************************************
4754 * SmallBlockChainStream_SetNextBlockInChain
4756 * Writes the index of the next block of the specified block in the small
4757 * block depot.
4758 * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4759 * To flag a block as free use BLOCK_UNUSED as nextBlock.
4761 static void SmallBlockChainStream_SetNextBlockInChain(
4762 SmallBlockChainStream* This,
4763 ULONG blockIndex,
4764 ULONG nextBlock)
4766 ULARGE_INTEGER offsetOfBlockInDepot;
4767 DWORD buffer;
4768 ULONG bytesWritten;
4770 offsetOfBlockInDepot.u.HighPart = 0;
4771 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4773 StorageUtl_WriteDWord((BYTE *)&buffer, 0, nextBlock);
4776 * Read those bytes in the buffer from the small block file.
4778 BlockChainStream_WriteAt(
4779 This->parentStorage->smallBlockDepotChain,
4780 offsetOfBlockInDepot,
4781 sizeof(DWORD),
4782 &buffer,
4783 &bytesWritten);
4786 /******************************************************************************
4787 * SmallBlockChainStream_FreeBlock
4789 * Flag small block 'blockIndex' as free in the small block depot.
4791 static void SmallBlockChainStream_FreeBlock(
4792 SmallBlockChainStream* This,
4793 ULONG blockIndex)
4795 SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4798 /******************************************************************************
4799 * SmallBlockChainStream_GetNextFreeBlock
4801 * Returns the index of a free small block. The small block depot will be
4802 * enlarged if necessary. The small block chain will also be enlarged if
4803 * necessary.
4805 static ULONG SmallBlockChainStream_GetNextFreeBlock(
4806 SmallBlockChainStream* This)
4808 ULARGE_INTEGER offsetOfBlockInDepot;
4809 DWORD buffer;
4810 ULONG bytesRead;
4811 ULONG blockIndex = 0;
4812 ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4813 HRESULT res = S_OK;
4814 ULONG smallBlocksPerBigBlock;
4816 offsetOfBlockInDepot.u.HighPart = 0;
4819 * Scan the small block depot for a free block
4821 while (nextBlockIndex != BLOCK_UNUSED)
4823 offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4825 res = BlockChainStream_ReadAt(
4826 This->parentStorage->smallBlockDepotChain,
4827 offsetOfBlockInDepot,
4828 sizeof(DWORD),
4829 &buffer,
4830 &bytesRead);
4833 * If we run out of space for the small block depot, enlarge it
4835 if (SUCCEEDED(res))
4837 StorageUtl_ReadDWord((BYTE *)&buffer, 0, &nextBlockIndex);
4839 if (nextBlockIndex != BLOCK_UNUSED)
4840 blockIndex++;
4842 else
4844 ULONG count =
4845 BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4847 ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4848 ULONG nextBlock, newsbdIndex;
4849 BYTE smallBlockDepot[BIG_BLOCK_SIZE];
4851 nextBlock = sbdIndex;
4852 while (nextBlock != BLOCK_END_OF_CHAIN)
4854 sbdIndex = nextBlock;
4855 StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4858 newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4859 if (sbdIndex != BLOCK_END_OF_CHAIN)
4860 StorageImpl_SetNextBlockInChain(
4861 This->parentStorage,
4862 sbdIndex,
4863 newsbdIndex);
4865 StorageImpl_SetNextBlockInChain(
4866 This->parentStorage,
4867 newsbdIndex,
4868 BLOCK_END_OF_CHAIN);
4871 * Initialize all the small blocks to free
4873 memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4874 StorageImpl_WriteBigBlock(This->parentStorage, newsbdIndex, smallBlockDepot);
4876 if (count == 0)
4879 * We have just created the small block depot.
4881 DirEntry rootProp;
4882 ULONG sbStartIndex;
4885 * Save it in the header
4887 This->parentStorage->smallBlockDepotStart = newsbdIndex;
4888 StorageImpl_SaveFileHeader(This->parentStorage);
4891 * And allocate the first big block that will contain small blocks
4893 sbStartIndex =
4894 StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4896 StorageImpl_SetNextBlockInChain(
4897 This->parentStorage,
4898 sbStartIndex,
4899 BLOCK_END_OF_CHAIN);
4901 StorageImpl_ReadDirEntry(
4902 This->parentStorage,
4903 This->parentStorage->base.rootPropertySetIndex,
4904 &rootProp);
4906 rootProp.startingBlock = sbStartIndex;
4907 rootProp.size.u.HighPart = 0;
4908 rootProp.size.u.LowPart = This->parentStorage->bigBlockSize;
4910 StorageImpl_WriteDirEntry(
4911 This->parentStorage,
4912 This->parentStorage->base.rootPropertySetIndex,
4913 &rootProp);
4915 else
4916 StorageImpl_SaveFileHeader(This->parentStorage);
4920 smallBlocksPerBigBlock =
4921 This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4924 * Verify if we have to allocate big blocks to contain small blocks
4926 if (blockIndex % smallBlocksPerBigBlock == 0)
4928 DirEntry rootProp;
4929 ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4931 StorageImpl_ReadDirEntry(
4932 This->parentStorage,
4933 This->parentStorage->base.rootPropertySetIndex,
4934 &rootProp);
4936 if (rootProp.size.u.LowPart <
4937 (blocksRequired * This->parentStorage->bigBlockSize))
4939 rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
4941 BlockChainStream_SetSize(
4942 This->parentStorage->smallBlockRootChain,
4943 rootProp.size);
4945 StorageImpl_WriteDirEntry(
4946 This->parentStorage,
4947 This->parentStorage->base.rootPropertySetIndex,
4948 &rootProp);
4952 return blockIndex;
4955 /******************************************************************************
4956 * SmallBlockChainStream_ReadAt
4958 * Reads a specified number of bytes from this chain at the specified offset.
4959 * bytesRead may be NULL.
4960 * Failure will be returned if the specified number of bytes has not been read.
4962 HRESULT SmallBlockChainStream_ReadAt(
4963 SmallBlockChainStream* This,
4964 ULARGE_INTEGER offset,
4965 ULONG size,
4966 void* buffer,
4967 ULONG* bytesRead)
4969 HRESULT rc = S_OK;
4970 ULARGE_INTEGER offsetInBigBlockFile;
4971 ULONG blockNoInSequence =
4972 offset.u.LowPart / This->parentStorage->smallBlockSize;
4974 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
4975 ULONG bytesToReadInBuffer;
4976 ULONG blockIndex;
4977 ULONG bytesReadFromBigBlockFile;
4978 BYTE* bufferWalker;
4981 * This should never happen on a small block file.
4983 assert(offset.u.HighPart==0);
4986 * Find the first block in the stream that contains part of the buffer.
4988 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4990 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
4992 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
4993 if(FAILED(rc))
4994 return rc;
4995 blockNoInSequence--;
4999 * Start reading the buffer.
5001 *bytesRead = 0;
5002 bufferWalker = buffer;
5004 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5007 * Calculate how many bytes we can copy from this small block.
5009 bytesToReadInBuffer =
5010 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5013 * Calculate the offset of the small block in the small block file.
5015 offsetInBigBlockFile.u.HighPart = 0;
5016 offsetInBigBlockFile.u.LowPart =
5017 blockIndex * This->parentStorage->smallBlockSize;
5019 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5022 * Read those bytes in the buffer from the small block file.
5023 * The small block has already been identified so it shouldn't fail
5024 * unless the file is corrupt.
5026 rc = BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5027 offsetInBigBlockFile,
5028 bytesToReadInBuffer,
5029 bufferWalker,
5030 &bytesReadFromBigBlockFile);
5032 if (FAILED(rc))
5033 return rc;
5036 * Step to the next big block.
5038 rc = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex);
5039 if(FAILED(rc))
5040 return STG_E_DOCFILECORRUPT;
5042 bufferWalker += bytesReadFromBigBlockFile;
5043 size -= bytesReadFromBigBlockFile;
5044 *bytesRead += bytesReadFromBigBlockFile;
5045 offsetInBlock = (offsetInBlock + bytesReadFromBigBlockFile) % This->parentStorage->smallBlockSize;
5048 return (size == 0) ? S_OK : STG_E_READFAULT;
5051 /******************************************************************************
5052 * SmallBlockChainStream_WriteAt
5054 * Writes the specified number of bytes to this chain at the specified offset.
5055 * Will fail if not all specified number of bytes have been written.
5057 HRESULT SmallBlockChainStream_WriteAt(
5058 SmallBlockChainStream* This,
5059 ULARGE_INTEGER offset,
5060 ULONG size,
5061 const void* buffer,
5062 ULONG* bytesWritten)
5064 ULARGE_INTEGER offsetInBigBlockFile;
5065 ULONG blockNoInSequence =
5066 offset.u.LowPart / This->parentStorage->smallBlockSize;
5068 ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5069 ULONG bytesToWriteInBuffer;
5070 ULONG blockIndex;
5071 ULONG bytesWrittenToBigBlockFile;
5072 const BYTE* bufferWalker;
5073 HRESULT res;
5076 * This should never happen on a small block file.
5078 assert(offset.u.HighPart==0);
5081 * Find the first block in the stream that contains part of the buffer.
5083 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5085 while ( (blockNoInSequence > 0) && (blockIndex != BLOCK_END_OF_CHAIN))
5087 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5088 return STG_E_DOCFILECORRUPT;
5089 blockNoInSequence--;
5093 * Start writing the buffer.
5095 *bytesWritten = 0;
5096 bufferWalker = buffer;
5097 while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5100 * Calculate how many bytes we can copy to this small block.
5102 bytesToWriteInBuffer =
5103 min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5106 * Calculate the offset of the small block in the small block file.
5108 offsetInBigBlockFile.u.HighPart = 0;
5109 offsetInBigBlockFile.u.LowPart =
5110 blockIndex * This->parentStorage->smallBlockSize;
5112 offsetInBigBlockFile.u.LowPart += offsetInBlock;
5115 * Write those bytes in the buffer to the small block file.
5117 res = BlockChainStream_WriteAt(
5118 This->parentStorage->smallBlockRootChain,
5119 offsetInBigBlockFile,
5120 bytesToWriteInBuffer,
5121 bufferWalker,
5122 &bytesWrittenToBigBlockFile);
5123 if (FAILED(res))
5124 return res;
5127 * Step to the next big block.
5129 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5130 &blockIndex)))
5131 return FALSE;
5132 bufferWalker += bytesWrittenToBigBlockFile;
5133 size -= bytesWrittenToBigBlockFile;
5134 *bytesWritten += bytesWrittenToBigBlockFile;
5135 offsetInBlock = (offsetInBlock + bytesWrittenToBigBlockFile) % This->parentStorage->smallBlockSize;
5138 return (size == 0) ? S_OK : STG_E_WRITEFAULT;
5141 /******************************************************************************
5142 * SmallBlockChainStream_Shrink
5144 * Shrinks this chain in the small block depot.
5146 static BOOL SmallBlockChainStream_Shrink(
5147 SmallBlockChainStream* This,
5148 ULARGE_INTEGER newSize)
5150 ULONG blockIndex, extraBlock;
5151 ULONG numBlocks;
5152 ULONG count = 0;
5154 numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5156 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5157 numBlocks++;
5159 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5162 * Go to the new end of chain
5164 while (count < numBlocks)
5166 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5167 &blockIndex)))
5168 return FALSE;
5169 count++;
5173 * If the count is 0, we have a special case, the head of the chain was
5174 * just freed.
5176 if (count == 0)
5178 DirEntry chainProp;
5180 StorageImpl_ReadDirEntry(This->parentStorage,
5181 This->ownerPropertyIndex,
5182 &chainProp);
5184 chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5186 StorageImpl_WriteDirEntry(This->parentStorage,
5187 This->ownerPropertyIndex,
5188 &chainProp);
5191 * We start freeing the chain at the head block.
5193 extraBlock = blockIndex;
5195 else
5197 /* Get the next block before marking the new end */
5198 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5199 &extraBlock)))
5200 return FALSE;
5202 /* Mark the new end of chain */
5203 SmallBlockChainStream_SetNextBlockInChain(
5204 This,
5205 blockIndex,
5206 BLOCK_END_OF_CHAIN);
5210 * Mark the extra blocks as free
5212 while (extraBlock != BLOCK_END_OF_CHAIN)
5214 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5215 &blockIndex)))
5216 return FALSE;
5217 SmallBlockChainStream_FreeBlock(This, extraBlock);
5218 extraBlock = blockIndex;
5221 return TRUE;
5224 /******************************************************************************
5225 * SmallBlockChainStream_Enlarge
5227 * Grows this chain in the small block depot.
5229 static BOOL SmallBlockChainStream_Enlarge(
5230 SmallBlockChainStream* This,
5231 ULARGE_INTEGER newSize)
5233 ULONG blockIndex, currentBlock;
5234 ULONG newNumBlocks;
5235 ULONG oldNumBlocks = 0;
5237 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5240 * Empty chain. Create the head.
5242 if (blockIndex == BLOCK_END_OF_CHAIN)
5244 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5245 SmallBlockChainStream_SetNextBlockInChain(
5246 This,
5247 blockIndex,
5248 BLOCK_END_OF_CHAIN);
5250 if (This->headOfStreamPlaceHolder != NULL)
5252 *(This->headOfStreamPlaceHolder) = blockIndex;
5254 else
5256 DirEntry chainProp;
5258 StorageImpl_ReadDirEntry(This->parentStorage, This->ownerPropertyIndex,
5259 &chainProp);
5261 chainProp.startingBlock = blockIndex;
5263 StorageImpl_WriteDirEntry(This->parentStorage, This->ownerPropertyIndex,
5264 &chainProp);
5268 currentBlock = blockIndex;
5271 * Figure out how many blocks are needed to contain this stream
5273 newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5275 if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5276 newNumBlocks++;
5279 * Go to the current end of chain
5281 while (blockIndex != BLOCK_END_OF_CHAIN)
5283 oldNumBlocks++;
5284 currentBlock = blockIndex;
5285 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5286 return FALSE;
5290 * Add new blocks to the chain
5292 while (oldNumBlocks < newNumBlocks)
5294 blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5295 SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5297 SmallBlockChainStream_SetNextBlockInChain(
5298 This,
5299 blockIndex,
5300 BLOCK_END_OF_CHAIN);
5302 currentBlock = blockIndex;
5303 oldNumBlocks++;
5306 return TRUE;
5309 /******************************************************************************
5310 * SmallBlockChainStream_SetSize
5312 * Sets the size of this stream.
5313 * The file will grow if we grow the chain.
5315 * TODO: Free the actual blocks in the file when we shrink the chain.
5316 * Currently, the blocks are still in the file. So the file size
5317 * doesn't shrink even if we shrink streams.
5319 BOOL SmallBlockChainStream_SetSize(
5320 SmallBlockChainStream* This,
5321 ULARGE_INTEGER newSize)
5323 ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5325 if (newSize.u.LowPart == size.u.LowPart)
5326 return TRUE;
5328 if (newSize.u.LowPart < size.u.LowPart)
5330 SmallBlockChainStream_Shrink(This, newSize);
5332 else
5334 SmallBlockChainStream_Enlarge(This, newSize);
5337 return TRUE;
5340 /******************************************************************************
5341 * SmallBlockChainStream_GetCount
5343 * Returns the number of small blocks that comprises this chain.
5344 * This is not the size of the stream as the last block may not be full!
5347 static ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5349 ULONG blockIndex;
5350 ULONG count = 0;
5352 blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5354 while(blockIndex != BLOCK_END_OF_CHAIN)
5356 count++;
5358 if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This,
5359 blockIndex, &blockIndex)))
5360 return 0;
5363 return count;
5366 /******************************************************************************
5367 * SmallBlockChainStream_GetSize
5369 * Returns the size of this chain.
5371 static ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5373 DirEntry chainProperty;
5375 if(This->headOfStreamPlaceHolder != NULL)
5377 ULARGE_INTEGER result;
5378 result.u.HighPart = 0;
5380 result.u.LowPart = SmallBlockChainStream_GetCount(This) *
5381 This->parentStorage->smallBlockSize;
5383 return result;
5386 StorageImpl_ReadDirEntry(
5387 This->parentStorage,
5388 This->ownerPropertyIndex,
5389 &chainProperty);
5391 return chainProperty.size;
5394 /******************************************************************************
5395 * StgCreateDocfile [OLE32.@]
5396 * Creates a new compound file storage object
5398 * PARAMS
5399 * pwcsName [ I] Unicode string with filename (can be relative or NULL)
5400 * grfMode [ I] Access mode for opening the new storage object (see STGM_ constants)
5401 * reserved [ ?] unused?, usually 0
5402 * ppstgOpen [IO] A pointer to IStorage pointer to the new onject
5404 * RETURNS
5405 * S_OK if the file was successfully created
5406 * some STG_E_ value if error
5407 * NOTES
5408 * if pwcsName is NULL, create file with new unique name
5409 * the function can returns
5410 * STG_S_CONVERTED if the specified file was successfully converted to storage format
5411 * (unrealized now)
5413 HRESULT WINAPI StgCreateDocfile(
5414 LPCOLESTR pwcsName,
5415 DWORD grfMode,
5416 DWORD reserved,
5417 IStorage **ppstgOpen)
5419 StorageImpl* newStorage = 0;
5420 HANDLE hFile = INVALID_HANDLE_VALUE;
5421 HRESULT hr = STG_E_INVALIDFLAG;
5422 DWORD shareMode;
5423 DWORD accessMode;
5424 DWORD creationMode;
5425 DWORD fileAttributes;
5426 WCHAR tempFileName[MAX_PATH];
5428 TRACE("(%s, %x, %d, %p)\n",
5429 debugstr_w(pwcsName), grfMode,
5430 reserved, ppstgOpen);
5432 if (ppstgOpen == 0)
5433 return STG_E_INVALIDPOINTER;
5434 if (reserved != 0)
5435 return STG_E_INVALIDPARAMETER;
5437 /* if no share mode given then DENY_NONE is the default */
5438 if (STGM_SHARE_MODE(grfMode) == 0)
5439 grfMode |= STGM_SHARE_DENY_NONE;
5441 if ( FAILED( validateSTGM(grfMode) ))
5442 goto end;
5444 /* StgCreateDocFile seems to refuse readonly access, despite MSDN */
5445 switch(STGM_ACCESS_MODE(grfMode))
5447 case STGM_WRITE:
5448 case STGM_READWRITE:
5449 break;
5450 default:
5451 goto end;
5454 /* in direct mode, can only use SHARE_EXCLUSIVE */
5455 if (!(grfMode & STGM_TRANSACTED) && (STGM_SHARE_MODE(grfMode) != STGM_SHARE_EXCLUSIVE))
5456 goto end;
5458 /* but in transacted mode, any share mode is valid */
5461 * Generate a unique name.
5463 if (pwcsName == 0)
5465 WCHAR tempPath[MAX_PATH];
5466 static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5468 memset(tempPath, 0, sizeof(tempPath));
5469 memset(tempFileName, 0, sizeof(tempFileName));
5471 if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5472 tempPath[0] = '.';
5474 if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5475 pwcsName = tempFileName;
5476 else
5478 hr = STG_E_INSUFFICIENTMEMORY;
5479 goto end;
5482 creationMode = TRUNCATE_EXISTING;
5484 else
5486 creationMode = GetCreationModeFromSTGM(grfMode);
5490 * Interpret the STGM value grfMode
5492 shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5493 accessMode = GetAccessModeFromSTGM(grfMode);
5495 if (grfMode & STGM_DELETEONRELEASE)
5496 fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5497 else
5498 fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5500 if (STGM_SHARE_MODE(grfMode) && !(grfMode & STGM_SHARE_DENY_NONE))
5501 FIXME("Storage share mode not implemented.\n");
5503 if (grfMode & STGM_TRANSACTED)
5504 FIXME("Transacted mode not implemented.\n");
5506 *ppstgOpen = 0;
5508 hFile = CreateFileW(pwcsName,
5509 accessMode,
5510 shareMode,
5511 NULL,
5512 creationMode,
5513 fileAttributes,
5516 if (hFile == INVALID_HANDLE_VALUE)
5518 if(GetLastError() == ERROR_FILE_EXISTS)
5519 hr = STG_E_FILEALREADYEXISTS;
5520 else
5521 hr = E_FAIL;
5522 goto end;
5526 * Allocate and initialize the new IStorage32object.
5528 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5530 if (newStorage == 0)
5532 hr = STG_E_INSUFFICIENTMEMORY;
5533 goto end;
5536 hr = StorageImpl_Construct(
5537 newStorage,
5538 hFile,
5539 pwcsName,
5540 NULL,
5541 grfMode,
5542 TRUE,
5543 TRUE);
5545 if (FAILED(hr))
5547 HeapFree(GetProcessHeap(), 0, newStorage);
5548 goto end;
5552 * Get an "out" pointer for the caller.
5554 hr = StorageBaseImpl_QueryInterface(
5555 (IStorage*)newStorage,
5556 &IID_IStorage,
5557 (void**)ppstgOpen);
5558 end:
5559 TRACE("<-- %p r = %08x\n", *ppstgOpen, hr);
5561 return hr;
5564 /******************************************************************************
5565 * StgCreateStorageEx [OLE32.@]
5567 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5569 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5570 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5572 if (stgfmt != STGFMT_FILE && grfAttrs != 0)
5574 ERR("grfAttrs must be 0 if stgfmt != STGFMT_FILE\n");
5575 return STG_E_INVALIDPARAMETER;
5578 if (stgfmt == STGFMT_FILE && grfAttrs != 0 && grfAttrs != FILE_FLAG_NO_BUFFERING)
5580 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_FILE\n");
5581 return STG_E_INVALIDPARAMETER;
5584 if (stgfmt == STGFMT_FILE)
5586 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5587 return STG_E_INVALIDPARAMETER;
5590 if (stgfmt == STGFMT_STORAGE || stgfmt == STGFMT_DOCFILE)
5592 FIXME("Stub: calling StgCreateDocfile, but ignoring pStgOptions and grfAttrs\n");
5593 return StgCreateDocfile(pwcsName, grfMode, 0, (IStorage **)ppObjectOpen);
5596 ERR("Invalid stgfmt argument\n");
5597 return STG_E_INVALIDPARAMETER;
5600 /******************************************************************************
5601 * StgCreatePropSetStg [OLE32.@]
5603 HRESULT WINAPI StgCreatePropSetStg(IStorage *pstg, DWORD reserved,
5604 IPropertySetStorage **ppPropSetStg)
5606 HRESULT hr;
5608 TRACE("(%p, 0x%x, %p)\n", pstg, reserved, ppPropSetStg);
5609 if (reserved)
5610 hr = STG_E_INVALIDPARAMETER;
5611 else
5612 hr = StorageBaseImpl_QueryInterface(pstg, &IID_IPropertySetStorage,
5613 (void**)ppPropSetStg);
5614 return hr;
5617 /******************************************************************************
5618 * StgOpenStorageEx [OLE32.@]
5620 HRESULT WINAPI StgOpenStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5622 TRACE("(%s, %x, %x, %x, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5623 grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5625 if (stgfmt != STGFMT_DOCFILE && grfAttrs != 0)
5627 ERR("grfAttrs must be 0 if stgfmt != STGFMT_DOCFILE\n");
5628 return STG_E_INVALIDPARAMETER;
5631 switch (stgfmt)
5633 case STGFMT_FILE:
5634 ERR("Cannot use STGFMT_FILE - this is NTFS only\n");
5635 return STG_E_INVALIDPARAMETER;
5637 case STGFMT_STORAGE:
5638 break;
5640 case STGFMT_DOCFILE:
5641 if (grfAttrs && grfAttrs != FILE_FLAG_NO_BUFFERING)
5643 ERR("grfAttrs must be 0 or FILE_FLAG_NO_BUFFERING if stgfmt == STGFMT_DOCFILE\n");
5644 return STG_E_INVALIDPARAMETER;
5646 FIXME("Stub: calling StgOpenStorage, but ignoring pStgOptions and grfAttrs\n");
5647 break;
5649 case STGFMT_ANY:
5650 WARN("STGFMT_ANY assuming storage\n");
5651 break;
5653 default:
5654 return STG_E_INVALIDPARAMETER;
5657 return StgOpenStorage(pwcsName, NULL, grfMode, NULL, 0, (IStorage **)ppObjectOpen);
5661 /******************************************************************************
5662 * StgOpenStorage [OLE32.@]
5664 HRESULT WINAPI StgOpenStorage(
5665 const OLECHAR *pwcsName,
5666 IStorage *pstgPriority,
5667 DWORD grfMode,
5668 SNB snbExclude,
5669 DWORD reserved,
5670 IStorage **ppstgOpen)
5672 StorageImpl* newStorage = 0;
5673 HRESULT hr = S_OK;
5674 HANDLE hFile = 0;
5675 DWORD shareMode;
5676 DWORD accessMode;
5677 WCHAR fullname[MAX_PATH];
5679 TRACE("(%s, %p, %x, %p, %d, %p)\n",
5680 debugstr_w(pwcsName), pstgPriority, grfMode,
5681 snbExclude, reserved, ppstgOpen);
5683 if (pwcsName == 0)
5685 hr = STG_E_INVALIDNAME;
5686 goto end;
5689 if (ppstgOpen == 0)
5691 hr = STG_E_INVALIDPOINTER;
5692 goto end;
5695 if (reserved)
5697 hr = STG_E_INVALIDPARAMETER;
5698 goto end;
5701 if (grfMode & STGM_PRIORITY)
5703 if (grfMode & (STGM_TRANSACTED|STGM_SIMPLE|STGM_NOSCRATCH|STGM_NOSNAPSHOT))
5704 return STG_E_INVALIDFLAG;
5705 if (grfMode & STGM_DELETEONRELEASE)
5706 return STG_E_INVALIDFUNCTION;
5707 if(STGM_ACCESS_MODE(grfMode) != STGM_READ)
5708 return STG_E_INVALIDFLAG;
5709 grfMode &= ~0xf0; /* remove the existing sharing mode */
5710 grfMode |= STGM_SHARE_DENY_NONE;
5712 /* STGM_PRIORITY stops other IStorage objects on the same file from
5713 * committing until the STGM_PRIORITY IStorage is closed. it also
5714 * stops non-transacted mode StgOpenStorage calls with write access from
5715 * succeeding. obviously, both of these cannot be achieved through just
5716 * file share flags */
5717 FIXME("STGM_PRIORITY mode not implemented correctly\n");
5721 * Validate the sharing mode
5723 if (!(grfMode & (STGM_TRANSACTED|STGM_PRIORITY)))
5724 switch(STGM_SHARE_MODE(grfMode))
5726 case STGM_SHARE_EXCLUSIVE:
5727 case STGM_SHARE_DENY_WRITE:
5728 break;
5729 default:
5730 hr = STG_E_INVALIDFLAG;
5731 goto end;
5734 if ( FAILED( validateSTGM(grfMode) ) ||
5735 (grfMode&STGM_CREATE))
5737 hr = STG_E_INVALIDFLAG;
5738 goto end;
5741 /* shared reading requires transacted mode */
5742 if( STGM_SHARE_MODE(grfMode) == STGM_SHARE_DENY_WRITE &&
5743 STGM_ACCESS_MODE(grfMode) == STGM_READWRITE &&
5744 !(grfMode&STGM_TRANSACTED) )
5746 hr = STG_E_INVALIDFLAG;
5747 goto end;
5751 * Interpret the STGM value grfMode
5753 shareMode = GetShareModeFromSTGM(grfMode);
5754 accessMode = GetAccessModeFromSTGM(grfMode);
5756 *ppstgOpen = 0;
5758 hFile = CreateFileW( pwcsName,
5759 accessMode,
5760 shareMode,
5761 NULL,
5762 OPEN_EXISTING,
5763 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5766 if (hFile==INVALID_HANDLE_VALUE)
5768 DWORD last_error = GetLastError();
5770 hr = E_FAIL;
5772 switch (last_error)
5774 case ERROR_FILE_NOT_FOUND:
5775 hr = STG_E_FILENOTFOUND;
5776 break;
5778 case ERROR_PATH_NOT_FOUND:
5779 hr = STG_E_PATHNOTFOUND;
5780 break;
5782 case ERROR_ACCESS_DENIED:
5783 case ERROR_WRITE_PROTECT:
5784 hr = STG_E_ACCESSDENIED;
5785 break;
5787 case ERROR_SHARING_VIOLATION:
5788 hr = STG_E_SHAREVIOLATION;
5789 break;
5791 default:
5792 hr = E_FAIL;
5795 goto end;
5799 * Refuse to open the file if it's too small to be a structured storage file
5800 * FIXME: verify the file when reading instead of here
5802 if (GetFileSize(hFile, NULL) < 0x100)
5804 CloseHandle(hFile);
5805 hr = STG_E_FILEALREADYEXISTS;
5806 goto end;
5810 * Allocate and initialize the new IStorage32object.
5812 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5814 if (newStorage == 0)
5816 hr = STG_E_INSUFFICIENTMEMORY;
5817 goto end;
5820 /* Initialize the storage */
5821 hr = StorageImpl_Construct(
5822 newStorage,
5823 hFile,
5824 pwcsName,
5825 NULL,
5826 grfMode,
5827 TRUE,
5828 FALSE );
5830 if (FAILED(hr))
5832 HeapFree(GetProcessHeap(), 0, newStorage);
5834 * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5836 if(hr == STG_E_INVALIDHEADER)
5837 hr = STG_E_FILEALREADYEXISTS;
5838 goto end;
5841 /* prepare the file name string given in lieu of the root property name */
5842 GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5843 memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5844 newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5847 * Get an "out" pointer for the caller.
5849 hr = StorageBaseImpl_QueryInterface(
5850 (IStorage*)newStorage,
5851 &IID_IStorage,
5852 (void**)ppstgOpen);
5854 end:
5855 TRACE("<-- %08x, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5856 return hr;
5859 /******************************************************************************
5860 * StgCreateDocfileOnILockBytes [OLE32.@]
5862 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5863 ILockBytes *plkbyt,
5864 DWORD grfMode,
5865 DWORD reserved,
5866 IStorage** ppstgOpen)
5868 StorageImpl* newStorage = 0;
5869 HRESULT hr = S_OK;
5871 if ((ppstgOpen == 0) || (plkbyt == 0))
5872 return STG_E_INVALIDPOINTER;
5875 * Allocate and initialize the new IStorage object.
5877 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5879 if (newStorage == 0)
5880 return STG_E_INSUFFICIENTMEMORY;
5882 hr = StorageImpl_Construct(
5883 newStorage,
5886 plkbyt,
5887 grfMode,
5888 FALSE,
5889 TRUE);
5891 if (FAILED(hr))
5893 HeapFree(GetProcessHeap(), 0, newStorage);
5894 return hr;
5898 * Get an "out" pointer for the caller.
5900 hr = StorageBaseImpl_QueryInterface(
5901 (IStorage*)newStorage,
5902 &IID_IStorage,
5903 (void**)ppstgOpen);
5905 return hr;
5908 /******************************************************************************
5909 * StgOpenStorageOnILockBytes [OLE32.@]
5911 HRESULT WINAPI StgOpenStorageOnILockBytes(
5912 ILockBytes *plkbyt,
5913 IStorage *pstgPriority,
5914 DWORD grfMode,
5915 SNB snbExclude,
5916 DWORD reserved,
5917 IStorage **ppstgOpen)
5919 StorageImpl* newStorage = 0;
5920 HRESULT hr = S_OK;
5922 if ((plkbyt == 0) || (ppstgOpen == 0))
5923 return STG_E_INVALIDPOINTER;
5925 if ( FAILED( validateSTGM(grfMode) ))
5926 return STG_E_INVALIDFLAG;
5928 *ppstgOpen = 0;
5931 * Allocate and initialize the new IStorage object.
5933 newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5935 if (newStorage == 0)
5936 return STG_E_INSUFFICIENTMEMORY;
5938 hr = StorageImpl_Construct(
5939 newStorage,
5942 plkbyt,
5943 grfMode,
5944 FALSE,
5945 FALSE);
5947 if (FAILED(hr))
5949 HeapFree(GetProcessHeap(), 0, newStorage);
5950 return hr;
5954 * Get an "out" pointer for the caller.
5956 hr = StorageBaseImpl_QueryInterface(
5957 (IStorage*)newStorage,
5958 &IID_IStorage,
5959 (void**)ppstgOpen);
5961 return hr;
5964 /******************************************************************************
5965 * StgSetTimes [ole32.@]
5966 * StgSetTimes [OLE32.@]
5970 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *pctime,
5971 FILETIME const *patime, FILETIME const *pmtime)
5973 IStorage *stg = NULL;
5974 HRESULT r;
5976 TRACE("%s %p %p %p\n", debugstr_w(str), pctime, patime, pmtime);
5978 r = StgOpenStorage(str, NULL, STGM_READWRITE | STGM_SHARE_DENY_WRITE,
5979 0, 0, &stg);
5980 if( SUCCEEDED(r) )
5982 r = IStorage_SetElementTimes(stg, NULL, pctime, patime, pmtime);
5983 IStorage_Release(stg);
5986 return r;
5989 /******************************************************************************
5990 * StgIsStorageILockBytes [OLE32.@]
5992 * Determines if the ILockBytes contains a storage object.
5994 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5996 BYTE sig[8];
5997 ULARGE_INTEGER offset;
5999 offset.u.HighPart = 0;
6000 offset.u.LowPart = 0;
6002 ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
6004 if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
6005 return S_OK;
6007 return S_FALSE;
6010 /******************************************************************************
6011 * WriteClassStg [OLE32.@]
6013 * This method will store the specified CLSID in the specified storage object
6015 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
6017 HRESULT hRes;
6019 if(!pStg)
6020 return E_INVALIDARG;
6022 if(!rclsid)
6023 return STG_E_INVALIDPOINTER;
6025 hRes = IStorage_SetClass(pStg, rclsid);
6027 return hRes;
6030 /***********************************************************************
6031 * ReadClassStg (OLE32.@)
6033 * This method reads the CLSID previously written to a storage object with
6034 * the WriteClassStg.
6036 * PARAMS
6037 * pstg [I] IStorage pointer
6038 * pclsid [O] Pointer to where the CLSID is written
6040 * RETURNS
6041 * Success: S_OK.
6042 * Failure: HRESULT code.
6044 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
6046 STATSTG pstatstg;
6047 HRESULT hRes;
6049 TRACE("(%p, %p)\n", pstg, pclsid);
6051 if(!pstg || !pclsid)
6052 return E_INVALIDARG;
6055 * read a STATSTG structure (contains the clsid) from the storage
6057 hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_NONAME);
6059 if(SUCCEEDED(hRes))
6060 *pclsid=pstatstg.clsid;
6062 return hRes;
6065 /***********************************************************************
6066 * OleLoadFromStream (OLE32.@)
6068 * This function loads an object from stream
6070 HRESULT WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
6072 CLSID clsid;
6073 HRESULT res;
6074 LPPERSISTSTREAM xstm;
6076 TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
6078 res=ReadClassStm(pStm,&clsid);
6079 if (FAILED(res))
6080 return res;
6081 res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
6082 if (FAILED(res))
6083 return res;
6084 res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
6085 if (FAILED(res)) {
6086 IUnknown_Release((IUnknown*)*ppvObj);
6087 return res;
6089 res=IPersistStream_Load(xstm,pStm);
6090 IPersistStream_Release(xstm);
6091 /* FIXME: all refcounts ok at this point? I think they should be:
6092 * pStm : unchanged
6093 * ppvObj : 1
6094 * xstm : 0 (released)
6096 return res;
6099 /***********************************************************************
6100 * OleSaveToStream (OLE32.@)
6102 * This function saves an object with the IPersistStream interface on it
6103 * to the specified stream.
6105 HRESULT WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
6108 CLSID clsid;
6109 HRESULT res;
6111 TRACE("(%p,%p)\n",pPStm,pStm);
6113 res=IPersistStream_GetClassID(pPStm,&clsid);
6115 if (SUCCEEDED(res)){
6117 res=WriteClassStm(pStm,&clsid);
6119 if (SUCCEEDED(res))
6121 res=IPersistStream_Save(pPStm,pStm,TRUE);
6124 TRACE("Finished Save\n");
6125 return res;
6128 /****************************************************************************
6129 * This method validate a STGM parameter that can contain the values below
6131 * The stgm modes in 0x0000ffff are not bit masks, but distinct 4 bit values.
6132 * The stgm values contained in 0xffff0000 are bitmasks.
6134 * STGM_DIRECT 0x00000000
6135 * STGM_TRANSACTED 0x00010000
6136 * STGM_SIMPLE 0x08000000
6138 * STGM_READ 0x00000000
6139 * STGM_WRITE 0x00000001
6140 * STGM_READWRITE 0x00000002
6142 * STGM_SHARE_DENY_NONE 0x00000040
6143 * STGM_SHARE_DENY_READ 0x00000030
6144 * STGM_SHARE_DENY_WRITE 0x00000020
6145 * STGM_SHARE_EXCLUSIVE 0x00000010
6147 * STGM_PRIORITY 0x00040000
6148 * STGM_DELETEONRELEASE 0x04000000
6150 * STGM_CREATE 0x00001000
6151 * STGM_CONVERT 0x00020000
6152 * STGM_FAILIFTHERE 0x00000000
6154 * STGM_NOSCRATCH 0x00100000
6155 * STGM_NOSNAPSHOT 0x00200000
6157 static HRESULT validateSTGM(DWORD stgm)
6159 DWORD access = STGM_ACCESS_MODE(stgm);
6160 DWORD share = STGM_SHARE_MODE(stgm);
6161 DWORD create = STGM_CREATE_MODE(stgm);
6163 if (stgm&~STGM_KNOWN_FLAGS)
6165 ERR("unknown flags %08x\n", stgm);
6166 return E_FAIL;
6169 switch (access)
6171 case STGM_READ:
6172 case STGM_WRITE:
6173 case STGM_READWRITE:
6174 break;
6175 default:
6176 return E_FAIL;
6179 switch (share)
6181 case STGM_SHARE_DENY_NONE:
6182 case STGM_SHARE_DENY_READ:
6183 case STGM_SHARE_DENY_WRITE:
6184 case STGM_SHARE_EXCLUSIVE:
6185 break;
6186 default:
6187 return E_FAIL;
6190 switch (create)
6192 case STGM_CREATE:
6193 case STGM_FAILIFTHERE:
6194 break;
6195 default:
6196 return E_FAIL;
6200 * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
6202 if ( (stgm & STGM_TRANSACTED) && (stgm & STGM_SIMPLE) )
6203 return E_FAIL;
6206 * STGM_CREATE | STGM_CONVERT
6207 * if both are false, STGM_FAILIFTHERE is set to TRUE
6209 if ( create == STGM_CREATE && (stgm & STGM_CONVERT) )
6210 return E_FAIL;
6213 * STGM_NOSCRATCH requires STGM_TRANSACTED
6215 if ( (stgm & STGM_NOSCRATCH) && !(stgm & STGM_TRANSACTED) )
6216 return E_FAIL;
6219 * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6220 * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6222 if ( (stgm & STGM_NOSNAPSHOT) &&
6223 (!(stgm & STGM_TRANSACTED) ||
6224 share == STGM_SHARE_EXCLUSIVE ||
6225 share == STGM_SHARE_DENY_WRITE) )
6226 return E_FAIL;
6228 return S_OK;
6231 /****************************************************************************
6232 * GetShareModeFromSTGM
6234 * This method will return a share mode flag from a STGM value.
6235 * The STGM value is assumed valid.
6237 static DWORD GetShareModeFromSTGM(DWORD stgm)
6239 switch (STGM_SHARE_MODE(stgm))
6241 case STGM_SHARE_DENY_NONE:
6242 return FILE_SHARE_READ | FILE_SHARE_WRITE;
6243 case STGM_SHARE_DENY_READ:
6244 return FILE_SHARE_WRITE;
6245 case STGM_SHARE_DENY_WRITE:
6246 return FILE_SHARE_READ;
6247 case STGM_SHARE_EXCLUSIVE:
6248 return 0;
6250 ERR("Invalid share mode!\n");
6251 assert(0);
6252 return 0;
6255 /****************************************************************************
6256 * GetAccessModeFromSTGM
6258 * This method will return an access mode flag from a STGM value.
6259 * The STGM value is assumed valid.
6261 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6263 switch (STGM_ACCESS_MODE(stgm))
6265 case STGM_READ:
6266 return GENERIC_READ;
6267 case STGM_WRITE:
6268 case STGM_READWRITE:
6269 return GENERIC_READ | GENERIC_WRITE;
6271 ERR("Invalid access mode!\n");
6272 assert(0);
6273 return 0;
6276 /****************************************************************************
6277 * GetCreationModeFromSTGM
6279 * This method will return a creation mode flag from a STGM value.
6280 * The STGM value is assumed valid.
6282 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6284 switch(STGM_CREATE_MODE(stgm))
6286 case STGM_CREATE:
6287 return CREATE_ALWAYS;
6288 case STGM_CONVERT:
6289 FIXME("STGM_CONVERT not implemented!\n");
6290 return CREATE_NEW;
6291 case STGM_FAILIFTHERE:
6292 return CREATE_NEW;
6294 ERR("Invalid create mode!\n");
6295 assert(0);
6296 return 0;
6300 /*************************************************************************
6301 * OLECONVERT_LoadOLE10 [Internal]
6303 * Loads the OLE10 STREAM to memory
6305 * PARAMS
6306 * pOleStream [I] The OLESTREAM
6307 * pData [I] Data Structure for the OLESTREAM Data
6309 * RETURNS
6310 * Success: S_OK
6311 * Failure: CONVERT10_E_OLESTREAM_GET for invalid Get
6312 * CONVERT10_E_OLESTREAM_FMT if the OLEID is invalid
6314 * NOTES
6315 * This function is used by OleConvertOLESTREAMToIStorage only.
6317 * Memory allocated for pData must be freed by the caller
6319 static HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6321 DWORD dwSize;
6322 HRESULT hRes = S_OK;
6323 int nTryCnt=0;
6324 int max_try = 6;
6326 pData->pData = NULL;
6327 pData->pstrOleObjFileName = NULL;
6329 for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6331 /* Get the OleID */
6332 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6333 if(dwSize != sizeof(pData->dwOleID))
6335 hRes = CONVERT10_E_OLESTREAM_GET;
6337 else if(pData->dwOleID != OLESTREAM_ID)
6339 hRes = CONVERT10_E_OLESTREAM_FMT;
6341 else
6343 hRes = S_OK;
6344 break;
6348 if(hRes == S_OK)
6350 /* Get the TypeID... more info needed for this field */
6351 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6352 if(dwSize != sizeof(pData->dwTypeID))
6354 hRes = CONVERT10_E_OLESTREAM_GET;
6357 if(hRes == S_OK)
6359 if(pData->dwTypeID != 0)
6361 /* Get the length of the OleTypeName */
6362 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6363 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6365 hRes = CONVERT10_E_OLESTREAM_GET;
6368 if(hRes == S_OK)
6370 if(pData->dwOleTypeNameLength > 0)
6372 /* Get the OleTypeName */
6373 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6374 if(dwSize != pData->dwOleTypeNameLength)
6376 hRes = CONVERT10_E_OLESTREAM_GET;
6380 if(bStrem1)
6382 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6383 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6385 hRes = CONVERT10_E_OLESTREAM_GET;
6387 if(hRes == S_OK)
6389 if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6390 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6391 pData->pstrOleObjFileName = HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6392 if(pData->pstrOleObjFileName)
6394 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->pstrOleObjFileName, pData->dwOleObjFileNameLength);
6395 if(dwSize != pData->dwOleObjFileNameLength)
6397 hRes = CONVERT10_E_OLESTREAM_GET;
6400 else
6401 hRes = CONVERT10_E_OLESTREAM_GET;
6404 else
6406 /* Get the Width of the Metafile */
6407 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6408 if(dwSize != sizeof(pData->dwMetaFileWidth))
6410 hRes = CONVERT10_E_OLESTREAM_GET;
6412 if(hRes == S_OK)
6414 /* Get the Height of the Metafile */
6415 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6416 if(dwSize != sizeof(pData->dwMetaFileHeight))
6418 hRes = CONVERT10_E_OLESTREAM_GET;
6422 if(hRes == S_OK)
6424 /* Get the Length of the Data */
6425 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6426 if(dwSize != sizeof(pData->dwDataLength))
6428 hRes = CONVERT10_E_OLESTREAM_GET;
6432 if(hRes == S_OK) /* I don't know what this 8 byte information is. We have to figure out */
6434 if(!bStrem1) /* if it is a second OLE stream data */
6436 pData->dwDataLength -= 8;
6437 dwSize = pOleStream->lpstbl->Get(pOleStream, pData->strUnknown, sizeof(pData->strUnknown));
6438 if(dwSize != sizeof(pData->strUnknown))
6440 hRes = CONVERT10_E_OLESTREAM_GET;
6444 if(hRes == S_OK)
6446 if(pData->dwDataLength > 0)
6448 pData->pData = HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6450 /* Get Data (ex. IStorage, Metafile, or BMP) */
6451 if(pData->pData)
6453 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6454 if(dwSize != pData->dwDataLength)
6456 hRes = CONVERT10_E_OLESTREAM_GET;
6459 else
6461 hRes = CONVERT10_E_OLESTREAM_GET;
6467 return hRes;
6470 /*************************************************************************
6471 * OLECONVERT_SaveOLE10 [Internal]
6473 * Saves the OLE10 STREAM From memory
6475 * PARAMS
6476 * pData [I] Data Structure for the OLESTREAM Data
6477 * pOleStream [I] The OLESTREAM to save
6479 * RETURNS
6480 * Success: S_OK
6481 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
6483 * NOTES
6484 * This function is used by OleConvertIStorageToOLESTREAM only.
6487 static HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6489 DWORD dwSize;
6490 HRESULT hRes = S_OK;
6493 /* Set the OleID */
6494 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6495 if(dwSize != sizeof(pData->dwOleID))
6497 hRes = CONVERT10_E_OLESTREAM_PUT;
6500 if(hRes == S_OK)
6502 /* Set the TypeID */
6503 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6504 if(dwSize != sizeof(pData->dwTypeID))
6506 hRes = CONVERT10_E_OLESTREAM_PUT;
6510 if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6512 /* Set the Length of the OleTypeName */
6513 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6514 if(dwSize != sizeof(pData->dwOleTypeNameLength))
6516 hRes = CONVERT10_E_OLESTREAM_PUT;
6519 if(hRes == S_OK)
6521 if(pData->dwOleTypeNameLength > 0)
6523 /* Set the OleTypeName */
6524 dwSize = pOleStream->lpstbl->Put(pOleStream, pData->strOleTypeName, pData->dwOleTypeNameLength);
6525 if(dwSize != pData->dwOleTypeNameLength)
6527 hRes = CONVERT10_E_OLESTREAM_PUT;
6532 if(hRes == S_OK)
6534 /* Set the width of the Metafile */
6535 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6536 if(dwSize != sizeof(pData->dwMetaFileWidth))
6538 hRes = CONVERT10_E_OLESTREAM_PUT;
6542 if(hRes == S_OK)
6544 /* Set the height of the Metafile */
6545 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6546 if(dwSize != sizeof(pData->dwMetaFileHeight))
6548 hRes = CONVERT10_E_OLESTREAM_PUT;
6552 if(hRes == S_OK)
6554 /* Set the length of the Data */
6555 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6556 if(dwSize != sizeof(pData->dwDataLength))
6558 hRes = CONVERT10_E_OLESTREAM_PUT;
6562 if(hRes == S_OK)
6564 if(pData->dwDataLength > 0)
6566 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6567 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *) pData->pData, pData->dwDataLength);
6568 if(dwSize != pData->dwDataLength)
6570 hRes = CONVERT10_E_OLESTREAM_PUT;
6575 return hRes;
6578 /*************************************************************************
6579 * OLECONVERT_GetOLE20FromOLE10[Internal]
6581 * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6582 * opens it, and copies the content to the dest IStorage for
6583 * OleConvertOLESTREAMToIStorage
6586 * PARAMS
6587 * pDestStorage [I] The IStorage to copy the data to
6588 * pBuffer [I] Buffer that contains the IStorage from the OLESTREAM
6589 * nBufferLength [I] The size of the buffer
6591 * RETURNS
6592 * Nothing
6594 * NOTES
6598 static void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, const BYTE *pBuffer, DWORD nBufferLength)
6600 HRESULT hRes;
6601 HANDLE hFile;
6602 IStorage *pTempStorage;
6603 DWORD dwNumOfBytesWritten;
6604 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6605 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6607 /* Create a temp File */
6608 GetTempPathW(MAX_PATH, wstrTempDir);
6609 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6610 hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6612 if(hFile != INVALID_HANDLE_VALUE)
6614 /* Write IStorage Data to File */
6615 WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6616 CloseHandle(hFile);
6618 /* Open and copy temp storage to the Dest Storage */
6619 hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6620 if(hRes == S_OK)
6622 hRes = IStorage_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6623 IStorage_Release(pTempStorage);
6625 DeleteFileW(wstrTempFile);
6630 /*************************************************************************
6631 * OLECONVERT_WriteOLE20ToBuffer [Internal]
6633 * Saves the OLE10 STREAM From memory
6635 * PARAMS
6636 * pStorage [I] The Src IStorage to copy
6637 * pData [I] The Dest Memory to write to.
6639 * RETURNS
6640 * The size in bytes allocated for pData
6642 * NOTES
6643 * Memory allocated for pData must be freed by the caller
6645 * Used by OleConvertIStorageToOLESTREAM only.
6648 static DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6650 HANDLE hFile;
6651 HRESULT hRes;
6652 DWORD nDataLength = 0;
6653 IStorage *pTempStorage;
6654 WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6655 static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6657 *pData = NULL;
6659 /* Create temp Storage */
6660 GetTempPathW(MAX_PATH, wstrTempDir);
6661 GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6662 hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6664 if(hRes == S_OK)
6666 /* Copy Src Storage to the Temp Storage */
6667 IStorage_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6668 IStorage_Release(pTempStorage);
6670 /* Open Temp Storage as a file and copy to memory */
6671 hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6672 if(hFile != INVALID_HANDLE_VALUE)
6674 nDataLength = GetFileSize(hFile, NULL);
6675 *pData = HeapAlloc(GetProcessHeap(),0,nDataLength);
6676 ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6677 CloseHandle(hFile);
6679 DeleteFileW(wstrTempFile);
6681 return nDataLength;
6684 /*************************************************************************
6685 * OLECONVERT_CreateOleStream [Internal]
6687 * Creates the "\001OLE" stream in the IStorage if necessary.
6689 * PARAMS
6690 * pStorage [I] Dest storage to create the stream in
6692 * RETURNS
6693 * Nothing
6695 * NOTES
6696 * This function is used by OleConvertOLESTREAMToIStorage only.
6698 * This stream is still unknown, MS Word seems to have extra data
6699 * but since the data is stored in the OLESTREAM there should be
6700 * no need to recreate the stream. If the stream is manually
6701 * deleted it will create it with this default data.
6704 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6706 HRESULT hRes;
6707 IStream *pStream;
6708 static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6709 BYTE pOleStreamHeader [] =
6711 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6712 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6713 0x00, 0x00, 0x00, 0x00
6716 /* Create stream if not present */
6717 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6718 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6720 if(hRes == S_OK)
6722 /* Write default Data */
6723 hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6724 IStream_Release(pStream);
6728 /* write a string to a stream, preceded by its length */
6729 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6731 HRESULT r;
6732 LPSTR str;
6733 DWORD len = 0;
6735 if( string )
6736 len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6737 r = IStream_Write( stm, &len, sizeof(len), NULL);
6738 if( FAILED( r ) )
6739 return r;
6740 if(len == 0)
6741 return r;
6742 str = CoTaskMemAlloc( len );
6743 WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6744 r = IStream_Write( stm, str, len, NULL);
6745 CoTaskMemFree( str );
6746 return r;
6749 /* read a string preceded by its length from a stream */
6750 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6752 HRESULT r;
6753 DWORD len, count = 0;
6754 LPSTR str;
6755 LPWSTR wstr;
6757 r = IStream_Read( stm, &len, sizeof(len), &count );
6758 if( FAILED( r ) )
6759 return r;
6760 if( count != sizeof(len) )
6761 return E_OUTOFMEMORY;
6763 TRACE("%d bytes\n",len);
6765 str = CoTaskMemAlloc( len );
6766 if( !str )
6767 return E_OUTOFMEMORY;
6768 count = 0;
6769 r = IStream_Read( stm, str, len, &count );
6770 if( FAILED( r ) )
6771 return r;
6772 if( count != len )
6774 CoTaskMemFree( str );
6775 return E_OUTOFMEMORY;
6778 TRACE("Read string %s\n",debugstr_an(str,len));
6780 len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6781 wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6782 if( wstr )
6783 MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6784 CoTaskMemFree( str );
6786 *string = wstr;
6788 return r;
6792 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6793 LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6795 IStream *pstm;
6796 HRESULT r = S_OK;
6797 static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6799 static const BYTE unknown1[12] =
6800 { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6801 0xFF, 0xFF, 0xFF, 0xFF};
6802 static const BYTE unknown2[16] =
6803 { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6804 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6806 TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6807 debugstr_w(lpszUserType), debugstr_w(szClipName),
6808 debugstr_w(szProgIDName));
6810 /* Create a CompObj stream */
6811 r = IStorage_CreateStream(pstg, szwStreamName,
6812 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6813 if( FAILED (r) )
6814 return r;
6816 /* Write CompObj Structure to stream */
6817 r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6819 if( SUCCEEDED( r ) )
6820 r = WriteClassStm( pstm, clsid );
6822 if( SUCCEEDED( r ) )
6823 r = STREAM_WriteString( pstm, lpszUserType );
6824 if( SUCCEEDED( r ) )
6825 r = STREAM_WriteString( pstm, szClipName );
6826 if( SUCCEEDED( r ) )
6827 r = STREAM_WriteString( pstm, szProgIDName );
6828 if( SUCCEEDED( r ) )
6829 r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6831 IStream_Release( pstm );
6833 return r;
6836 /***********************************************************************
6837 * WriteFmtUserTypeStg (OLE32.@)
6839 HRESULT WINAPI WriteFmtUserTypeStg(
6840 LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6842 HRESULT r;
6843 WCHAR szwClipName[0x40];
6844 CLSID clsid = CLSID_NULL;
6845 LPWSTR wstrProgID = NULL;
6846 DWORD n;
6848 TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6850 /* get the clipboard format name */
6851 n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName)/sizeof(szwClipName[0]) );
6852 szwClipName[n]=0;
6854 TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6856 /* FIXME: There's room to save a CLSID and its ProgID, but
6857 the CLSID is not looked up in the registry and in all the
6858 tests I wrote it was CLSID_NULL. Where does it come from?
6861 /* get the real program ID. This may fail, but that's fine */
6862 ProgIDFromCLSID(&clsid, &wstrProgID);
6864 TRACE("progid is %s\n",debugstr_w(wstrProgID));
6866 r = STORAGE_WriteCompObj( pstg, &clsid,
6867 lpszUserType, szwClipName, wstrProgID );
6869 CoTaskMemFree(wstrProgID);
6871 return r;
6875 /******************************************************************************
6876 * ReadFmtUserTypeStg [OLE32.@]
6878 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6880 HRESULT r;
6881 IStream *stm = 0;
6882 static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6883 unsigned char unknown1[12];
6884 unsigned char unknown2[16];
6885 DWORD count;
6886 LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6887 CLSID clsid;
6889 TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6891 r = IStorage_OpenStream( pstg, szCompObj, NULL,
6892 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6893 if( FAILED ( r ) )
6895 WARN("Failed to open stream r = %08x\n", r);
6896 return r;
6899 /* read the various parts of the structure */
6900 r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6901 if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6902 goto end;
6903 r = ReadClassStm( stm, &clsid );
6904 if( FAILED( r ) )
6905 goto end;
6907 r = STREAM_ReadString( stm, &szCLSIDName );
6908 if( FAILED( r ) )
6909 goto end;
6911 r = STREAM_ReadString( stm, &szOleTypeName );
6912 if( FAILED( r ) )
6913 goto end;
6915 r = STREAM_ReadString( stm, &szProgIDName );
6916 if( FAILED( r ) )
6917 goto end;
6919 r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6920 if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6921 goto end;
6923 /* ok, success... now we just need to store what we found */
6924 if( pcf )
6925 *pcf = RegisterClipboardFormatW( szOleTypeName );
6926 CoTaskMemFree( szOleTypeName );
6928 if( lplpszUserType )
6929 *lplpszUserType = szCLSIDName;
6930 CoTaskMemFree( szProgIDName );
6932 end:
6933 IStream_Release( stm );
6935 return r;
6939 /*************************************************************************
6940 * OLECONVERT_CreateCompObjStream [Internal]
6942 * Creates a "\001CompObj" is the destination IStorage if necessary.
6944 * PARAMS
6945 * pStorage [I] The dest IStorage to create the CompObj Stream
6946 * if necessary.
6947 * strOleTypeName [I] The ProgID
6949 * RETURNS
6950 * Success: S_OK
6951 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6953 * NOTES
6954 * This function is used by OleConvertOLESTREAMToIStorage only.
6956 * The stream data is stored in the OLESTREAM and there should be
6957 * no need to recreate the stream. If the stream is manually
6958 * deleted it will attempt to create it by querying the registry.
6962 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6964 IStream *pStream;
6965 HRESULT hStorageRes, hRes = S_OK;
6966 OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6967 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6968 WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
6970 BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6971 BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6973 /* Initialize the CompObj structure */
6974 memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6975 memcpy(IStorageCompObj.byUnknown1, pCompObjUnknown1, sizeof(pCompObjUnknown1));
6976 memcpy(IStorageCompObj.byUnknown2, pCompObjUnknown2, sizeof(pCompObjUnknown2));
6979 /* Create a CompObj stream if it doesn't exist */
6980 hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6981 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6982 if(hStorageRes == S_OK)
6984 /* copy the OleTypeName to the compobj struct */
6985 IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6986 strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6988 /* copy the OleTypeName to the compobj struct */
6989 /* Note: in the test made, these were Identical */
6990 IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6991 strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6993 /* Get the CLSID */
6994 MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
6995 bufferW, OLESTREAM_MAX_STR_LEN );
6996 hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
6998 if(hRes == S_OK)
7000 HKEY hKey;
7001 LONG hErr;
7002 /* Get the CLSID Default Name from the Registry */
7003 hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
7004 if(hErr == ERROR_SUCCESS)
7006 char strTemp[OLESTREAM_MAX_STR_LEN];
7007 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
7008 hErr = RegQueryValueA(hKey, NULL, strTemp, (LONG*) &(IStorageCompObj.dwCLSIDNameLength));
7009 if(hErr == ERROR_SUCCESS)
7011 strcpy(IStorageCompObj.strCLSIDName, strTemp);
7013 RegCloseKey(hKey);
7017 /* Write CompObj Structure to stream */
7018 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
7020 WriteClassStm(pStream,&(IStorageCompObj.clsid));
7022 hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
7023 if(IStorageCompObj.dwCLSIDNameLength > 0)
7025 hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
7027 hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
7028 if(IStorageCompObj.dwOleTypeNameLength > 0)
7030 hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
7032 hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
7033 if(IStorageCompObj.dwProgIDNameLength > 0)
7035 hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
7037 hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
7038 IStream_Release(pStream);
7040 return hRes;
7044 /*************************************************************************
7045 * OLECONVERT_CreateOlePresStream[Internal]
7047 * Creates the "\002OlePres000" Stream with the Metafile data
7049 * PARAMS
7050 * pStorage [I] The dest IStorage to create \002OLEPres000 stream in.
7051 * dwExtentX [I] Width of the Metafile
7052 * dwExtentY [I] Height of the Metafile
7053 * pData [I] Metafile data
7054 * dwDataLength [I] Size of the Metafile data
7056 * RETURNS
7057 * Success: S_OK
7058 * Failure: CONVERT10_E_OLESTREAM_PUT for invalid Put
7060 * NOTES
7061 * This function is used by OleConvertOLESTREAMToIStorage only.
7064 static void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
7066 HRESULT hRes;
7067 IStream *pStream;
7068 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7069 BYTE pOlePresStreamHeader [] =
7071 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
7072 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7073 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7074 0x00, 0x00, 0x00, 0x00
7077 BYTE pOlePresStreamHeaderEmpty [] =
7079 0x00, 0x00, 0x00, 0x00,
7080 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
7081 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
7082 0x00, 0x00, 0x00, 0x00
7085 /* Create the OlePres000 Stream */
7086 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7087 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7089 if(hRes == S_OK)
7091 DWORD nHeaderSize;
7092 OLECONVERT_ISTORAGE_OLEPRES OlePres;
7094 memset(&OlePres, 0, sizeof(OlePres));
7095 /* Do we have any metafile data to save */
7096 if(dwDataLength > 0)
7098 memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
7099 nHeaderSize = sizeof(pOlePresStreamHeader);
7101 else
7103 memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7104 nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7106 /* Set width and height of the metafile */
7107 OlePres.dwExtentX = dwExtentX;
7108 OlePres.dwExtentY = -dwExtentY;
7110 /* Set Data and Length */
7111 if(dwDataLength > sizeof(METAFILEPICT16))
7113 OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7114 OlePres.pData = &(pData[8]);
7116 /* Save OlePres000 Data to Stream */
7117 hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7118 hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7119 hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7120 hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7121 if(OlePres.dwSize > 0)
7123 hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7125 IStream_Release(pStream);
7129 /*************************************************************************
7130 * OLECONVERT_CreateOle10NativeStream [Internal]
7132 * Creates the "\001Ole10Native" Stream (should contain a BMP)
7134 * PARAMS
7135 * pStorage [I] Dest storage to create the stream in
7136 * pData [I] Ole10 Native Data (ex. bmp)
7137 * dwDataLength [I] Size of the Ole10 Native Data
7139 * RETURNS
7140 * Nothing
7142 * NOTES
7143 * This function is used by OleConvertOLESTREAMToIStorage only.
7145 * Might need to verify the data and return appropriate error message
7148 static void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, const BYTE *pData, DWORD dwDataLength)
7150 HRESULT hRes;
7151 IStream *pStream;
7152 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7154 /* Create the Ole10Native Stream */
7155 hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7156 STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7158 if(hRes == S_OK)
7160 /* Write info to stream */
7161 hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7162 hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7163 IStream_Release(pStream);
7168 /*************************************************************************
7169 * OLECONVERT_GetOLE10ProgID [Internal]
7171 * Finds the ProgID (or OleTypeID) from the IStorage
7173 * PARAMS
7174 * pStorage [I] The Src IStorage to get the ProgID
7175 * strProgID [I] the ProgID string to get
7176 * dwSize [I] the size of the string
7178 * RETURNS
7179 * Success: S_OK
7180 * Failure: REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7182 * NOTES
7183 * This function is used by OleConvertIStorageToOLESTREAM only.
7187 static HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7189 HRESULT hRes;
7190 IStream *pStream;
7191 LARGE_INTEGER iSeekPos;
7192 OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7193 static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7195 /* Open the CompObj Stream */
7196 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7197 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7198 if(hRes == S_OK)
7201 /*Get the OleType from the CompObj Stream */
7202 iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7203 iSeekPos.u.HighPart = 0;
7205 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7206 IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7207 iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7208 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7209 IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7210 iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7211 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7213 IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7214 if(*dwSize > 0)
7216 IStream_Read(pStream, strProgID, *dwSize, NULL);
7218 IStream_Release(pStream);
7220 else
7222 STATSTG stat;
7223 LPOLESTR wstrProgID;
7225 /* Get the OleType from the registry */
7226 REFCLSID clsid = &(stat.clsid);
7227 IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7228 hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7229 if(hRes == S_OK)
7231 *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7235 return hRes;
7238 /*************************************************************************
7239 * OLECONVERT_GetOle10PresData [Internal]
7241 * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7243 * PARAMS
7244 * pStorage [I] Src IStroage
7245 * pOleStream [I] Dest OleStream Mem Struct
7247 * RETURNS
7248 * Nothing
7250 * NOTES
7251 * This function is used by OleConvertIStorageToOLESTREAM only.
7253 * Memory allocated for pData must be freed by the caller
7257 static void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7260 HRESULT hRes;
7261 IStream *pStream;
7262 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7264 /* Initialize Default data for OLESTREAM */
7265 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7266 pOleStreamData[0].dwTypeID = 2;
7267 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7268 pOleStreamData[1].dwTypeID = 0;
7269 pOleStreamData[0].dwMetaFileWidth = 0;
7270 pOleStreamData[0].dwMetaFileHeight = 0;
7271 pOleStreamData[0].pData = NULL;
7272 pOleStreamData[1].pData = NULL;
7274 /* Open Ole10Native Stream */
7275 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7276 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7277 if(hRes == S_OK)
7280 /* Read Size and Data */
7281 IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7282 if(pOleStreamData->dwDataLength > 0)
7284 pOleStreamData->pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7285 IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7287 IStream_Release(pStream);
7293 /*************************************************************************
7294 * OLECONVERT_GetOle20PresData[Internal]
7296 * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7298 * PARAMS
7299 * pStorage [I] Src IStroage
7300 * pOleStreamData [I] Dest OleStream Mem Struct
7302 * RETURNS
7303 * Nothing
7305 * NOTES
7306 * This function is used by OleConvertIStorageToOLESTREAM only.
7308 * Memory allocated for pData must be freed by the caller
7310 static void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7312 HRESULT hRes;
7313 IStream *pStream;
7314 OLECONVERT_ISTORAGE_OLEPRES olePress;
7315 static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7317 /* Initialize Default data for OLESTREAM */
7318 pOleStreamData[0].dwOleID = OLESTREAM_ID;
7319 pOleStreamData[0].dwTypeID = 2;
7320 pOleStreamData[0].dwMetaFileWidth = 0;
7321 pOleStreamData[0].dwMetaFileHeight = 0;
7322 pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7323 pOleStreamData[1].dwOleID = OLESTREAM_ID;
7324 pOleStreamData[1].dwTypeID = 0;
7325 pOleStreamData[1].dwOleTypeNameLength = 0;
7326 pOleStreamData[1].strOleTypeName[0] = 0;
7327 pOleStreamData[1].dwMetaFileWidth = 0;
7328 pOleStreamData[1].dwMetaFileHeight = 0;
7329 pOleStreamData[1].pData = NULL;
7330 pOleStreamData[1].dwDataLength = 0;
7333 /* Open OlePress000 stream */
7334 hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7335 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7336 if(hRes == S_OK)
7338 LARGE_INTEGER iSeekPos;
7339 METAFILEPICT16 MetaFilePict;
7340 static const char strMetafilePictName[] = "METAFILEPICT";
7342 /* Set the TypeID for a Metafile */
7343 pOleStreamData[1].dwTypeID = 5;
7345 /* Set the OleTypeName to Metafile */
7346 pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7347 strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7349 iSeekPos.u.HighPart = 0;
7350 iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7352 /* Get Presentation Data */
7353 IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7354 IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7355 IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7356 IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7358 /*Set width and Height */
7359 pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7360 pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7361 if(olePress.dwSize > 0)
7363 /* Set Length */
7364 pOleStreamData[1].dwDataLength = olePress.dwSize + sizeof(METAFILEPICT16);
7366 /* Set MetaFilePict struct */
7367 MetaFilePict.mm = 8;
7368 MetaFilePict.xExt = olePress.dwExtentX;
7369 MetaFilePict.yExt = olePress.dwExtentY;
7370 MetaFilePict.hMF = 0;
7372 /* Get Metafile Data */
7373 pOleStreamData[1].pData = HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7374 memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7375 IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7377 IStream_Release(pStream);
7381 /*************************************************************************
7382 * OleConvertOLESTREAMToIStorage [OLE32.@]
7384 * Read info on MSDN
7386 * TODO
7387 * DVTARGETDEVICE parameter is not handled
7388 * Still unsure of some mem fields for OLE 10 Stream
7389 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7390 * and "\001OLE" streams
7393 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7394 LPOLESTREAM pOleStream,
7395 LPSTORAGE pstg,
7396 const DVTARGETDEVICE* ptd)
7398 int i;
7399 HRESULT hRes=S_OK;
7400 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7402 TRACE("%p %p %p\n", pOleStream, pstg, ptd);
7404 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7406 if(ptd != NULL)
7408 FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7411 if(pstg == NULL || pOleStream == NULL)
7413 hRes = E_INVALIDARG;
7416 if(hRes == S_OK)
7418 /* Load the OLESTREAM to Memory */
7419 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7422 if(hRes == S_OK)
7424 /* Load the OLESTREAM to Memory (part 2)*/
7425 hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7428 if(hRes == S_OK)
7431 if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7433 /* Do we have the IStorage Data in the OLESTREAM */
7434 if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7436 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7437 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7439 else
7441 /* It must be an original OLE 1.0 source */
7442 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7445 else
7447 /* It must be an original OLE 1.0 source */
7448 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7451 /* Create CompObj Stream if necessary */
7452 hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7453 if(hRes == S_OK)
7455 /*Create the Ole Stream if necessary */
7456 OLECONVERT_CreateOleStream(pstg);
7461 /* Free allocated memory */
7462 for(i=0; i < 2; i++)
7464 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7465 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7466 pOleStreamData[i].pstrOleObjFileName = NULL;
7468 return hRes;
7471 /*************************************************************************
7472 * OleConvertIStorageToOLESTREAM [OLE32.@]
7474 * Read info on MSDN
7476 * Read info on MSDN
7478 * TODO
7479 * Still unsure of some mem fields for OLE 10 Stream
7480 * Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7481 * and "\001OLE" streams.
7484 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7485 LPSTORAGE pstg,
7486 LPOLESTREAM pOleStream)
7488 int i;
7489 HRESULT hRes = S_OK;
7490 IStream *pStream;
7491 OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7492 static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7494 TRACE("%p %p\n", pstg, pOleStream);
7496 memset(pOleStreamData, 0, sizeof(pOleStreamData));
7498 if(pstg == NULL || pOleStream == NULL)
7500 hRes = E_INVALIDARG;
7502 if(hRes == S_OK)
7504 /* Get the ProgID */
7505 pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7506 hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7508 if(hRes == S_OK)
7510 /* Was it originally Ole10 */
7511 hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7512 if(hRes == S_OK)
7514 IStream_Release(pStream);
7515 /* Get Presentation Data for Ole10Native */
7516 OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7518 else
7520 /* Get Presentation Data (OLE20) */
7521 OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7524 /* Save OLESTREAM */
7525 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7526 if(hRes == S_OK)
7528 hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7533 /* Free allocated memory */
7534 for(i=0; i < 2; i++)
7536 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7539 return hRes;
7542 /***********************************************************************
7543 * GetConvertStg (OLE32.@)
7545 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7546 FIXME("unimplemented stub!\n");
7547 return E_FAIL;
7550 /******************************************************************************
7551 * StgIsStorageFile [OLE32.@]
7552 * Verify if the file contains a storage object
7554 * PARAMS
7555 * fn [ I] Filename
7557 * RETURNS
7558 * S_OK if file has magic bytes as a storage object
7559 * S_FALSE if file is not storage
7561 HRESULT WINAPI
7562 StgIsStorageFile(LPCOLESTR fn)
7564 HANDLE hf;
7565 BYTE magic[8];
7566 DWORD bytes_read;
7568 TRACE("%s\n", debugstr_w(fn));
7569 hf = CreateFileW(fn, GENERIC_READ,
7570 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7571 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7573 if (hf == INVALID_HANDLE_VALUE)
7574 return STG_E_FILENOTFOUND;
7576 if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7578 WARN(" unable to read file\n");
7579 CloseHandle(hf);
7580 return S_FALSE;
7583 CloseHandle(hf);
7585 if (bytes_read != 8) {
7586 WARN(" too short\n");
7587 return S_FALSE;
7590 if (!memcmp(magic,STORAGE_magic,8)) {
7591 WARN(" -> YES\n");
7592 return S_OK;
7595 WARN(" -> Invalid header.\n");
7596 return S_FALSE;
7599 /***********************************************************************
7600 * WriteClassStm (OLE32.@)
7602 * Writes a CLSID to a stream.
7604 * PARAMS
7605 * pStm [I] Stream to write to.
7606 * rclsid [I] CLSID to write.
7608 * RETURNS
7609 * Success: S_OK.
7610 * Failure: HRESULT code.
7612 HRESULT WINAPI WriteClassStm(IStream *pStm,REFCLSID rclsid)
7614 TRACE("(%p,%p)\n",pStm,rclsid);
7616 if (!pStm || !rclsid)
7617 return E_INVALIDARG;
7619 return IStream_Write(pStm,rclsid,sizeof(CLSID),NULL);
7622 /***********************************************************************
7623 * ReadClassStm (OLE32.@)
7625 * Reads a CLSID from a stream.
7627 * PARAMS
7628 * pStm [I] Stream to read from.
7629 * rclsid [O] CLSID to read.
7631 * RETURNS
7632 * Success: S_OK.
7633 * Failure: HRESULT code.
7635 HRESULT WINAPI ReadClassStm(IStream *pStm,CLSID *pclsid)
7637 ULONG nbByte;
7638 HRESULT res;
7640 TRACE("(%p,%p)\n",pStm,pclsid);
7642 if (!pStm || !pclsid)
7643 return E_INVALIDARG;
7645 /* clear the output args */
7646 *pclsid = CLSID_NULL;
7648 res = IStream_Read(pStm,(void*)pclsid,sizeof(CLSID),&nbByte);
7650 if (FAILED(res))
7651 return res;
7653 if (nbByte != sizeof(CLSID))
7654 return STG_E_READFAULT;
7655 else
7656 return S_OK;