Add support for HCBT_SYSCOMMAND hook, add logging for HCBT_SYSCOMMAND
[wine.git] / dlls / ole32 / stg_stream.c
blob3a205ed855f5f64c513a7ef91964963cea2814d5
1 /*
2 * Compound Storage (32 bit version)
3 * Stream implementation
5 * This file contains the implementation of the stream interface
6 * for streams contained in a compound storage.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Thuy Nguyen
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
32 #define NONAMELESSUNION
33 #define NONAMELESSSTRUCT
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winerror.h"
37 #include "winreg.h"
38 #include "winternl.h"
39 #include "wine/debug.h"
41 #include "storage32.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(storage);
47 * Virtual function table for the StgStreamImpl class.
49 static IStreamVtbl StgStreamImpl_Vtbl =
51 StgStreamImpl_QueryInterface,
52 StgStreamImpl_AddRef,
53 StgStreamImpl_Release,
54 StgStreamImpl_Read,
55 StgStreamImpl_Write,
56 StgStreamImpl_Seek,
57 StgStreamImpl_SetSize,
58 StgStreamImpl_CopyTo,
59 StgStreamImpl_Commit,
60 StgStreamImpl_Revert,
61 StgStreamImpl_LockRegion,
62 StgStreamImpl_UnlockRegion,
63 StgStreamImpl_Stat,
64 StgStreamImpl_Clone
67 /******************************************************************************
68 ** StgStreamImpl implementation
71 /***
72 * This is the constructor for the StgStreamImpl class.
74 * Params:
75 * parentStorage - Pointer to the storage that contains the stream to open
76 * ownerProperty - Index of the property that points to this stream.
78 StgStreamImpl* StgStreamImpl_Construct(
79 StorageBaseImpl* parentStorage,
80 DWORD grfMode,
81 ULONG ownerProperty)
83 StgStreamImpl* newStream;
85 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
87 if (newStream!=0)
90 * Set-up the virtual function table and reference count.
92 newStream->lpVtbl = &StgStreamImpl_Vtbl;
93 newStream->ref = 0;
96 * We want to nail-down the reference to the storage in case the
97 * stream out-lives the storage in the client application.
99 newStream->parentStorage = parentStorage;
100 IStorage_AddRef((IStorage*)newStream->parentStorage);
102 newStream->grfMode = grfMode;
103 newStream->ownerProperty = ownerProperty;
106 * Start the stream at the beginning.
108 newStream->currentPosition.u.HighPart = 0;
109 newStream->currentPosition.u.LowPart = 0;
112 * Initialize the rest of the data.
114 newStream->streamSize.u.HighPart = 0;
115 newStream->streamSize.u.LowPart = 0;
116 newStream->bigBlockChain = 0;
117 newStream->smallBlockChain = 0;
120 * Read the size from the property and determine if the blocks forming
121 * this stream are large or small.
123 StgStreamImpl_OpenBlockChain(newStream);
126 return newStream;
129 /***
130 * This is the destructor of the StgStreamImpl class.
132 * This method will clean-up all the resources used-up by the given StgStreamImpl
133 * class. The pointer passed-in to this function will be freed and will not
134 * be valid anymore.
136 void StgStreamImpl_Destroy(StgStreamImpl* This)
138 TRACE("(%p)\n", This);
141 * Release the reference we are holding on the parent storage.
143 IStorage_Release((IStorage*)This->parentStorage);
144 This->parentStorage = 0;
147 * Make sure we clean-up the block chain stream objects that we were using.
149 if (This->bigBlockChain != 0)
151 BlockChainStream_Destroy(This->bigBlockChain);
152 This->bigBlockChain = 0;
155 if (This->smallBlockChain != 0)
157 SmallBlockChainStream_Destroy(This->smallBlockChain);
158 This->smallBlockChain = 0;
162 * Finally, free the memory used-up by the class.
164 HeapFree(GetProcessHeap(), 0, This);
167 /***
168 * This implements the IUnknown method QueryInterface for this
169 * class
171 HRESULT WINAPI StgStreamImpl_QueryInterface(
172 IStream* iface,
173 REFIID riid, /* [in] */
174 void** ppvObject) /* [iid_is][out] */
176 StgStreamImpl* const This=(StgStreamImpl*)iface;
179 * Perform a sanity check on the parameters.
181 if (ppvObject==0)
182 return E_INVALIDARG;
185 * Initialize the return parameter.
187 *ppvObject = 0;
190 * Compare the riid with the interface IDs implemented by this object.
192 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
194 *ppvObject = (IStream*)This;
196 else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
198 *ppvObject = (IStream*)This;
202 * Check that we obtained an interface.
204 if ((*ppvObject)==0)
205 return E_NOINTERFACE;
208 * Query Interface always increases the reference count by one when it is
209 * successful
211 StgStreamImpl_AddRef(iface);
213 return S_OK;
216 /***
217 * This implements the IUnknown method AddRef for this
218 * class
220 ULONG WINAPI StgStreamImpl_AddRef(
221 IStream* iface)
223 StgStreamImpl* const This=(StgStreamImpl*)iface;
225 This->ref++;
227 return This->ref;
230 /***
231 * This implements the IUnknown method Release for this
232 * class
234 ULONG WINAPI StgStreamImpl_Release(
235 IStream* iface)
237 StgStreamImpl* const This=(StgStreamImpl*)iface;
239 ULONG newRef;
241 This->ref--;
243 newRef = This->ref;
246 * If the reference count goes down to 0, perform suicide.
248 if (newRef==0)
250 StgStreamImpl_Destroy(This);
253 return newRef;
256 /***
257 * This method will open the block chain pointed by the property
258 * that describes the stream.
259 * If the stream's size is null, no chain is opened.
261 void StgStreamImpl_OpenBlockChain(
262 StgStreamImpl* This)
264 StgProperty curProperty;
265 BOOL readSucessful;
268 * Make sure no old object is left over.
270 if (This->smallBlockChain != 0)
272 SmallBlockChainStream_Destroy(This->smallBlockChain);
273 This->smallBlockChain = 0;
276 if (This->bigBlockChain != 0)
278 BlockChainStream_Destroy(This->bigBlockChain);
279 This->bigBlockChain = 0;
283 * Read the information from the property.
285 readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
286 This->ownerProperty,
287 &curProperty);
289 if (readSucessful)
291 This->streamSize = curProperty.size;
294 * This code supports only streams that are <32 bits in size.
296 assert(This->streamSize.u.HighPart == 0);
298 if(curProperty.startingBlock == BLOCK_END_OF_CHAIN)
300 assert( (This->streamSize.u.HighPart == 0) && (This->streamSize.u.LowPart == 0) );
302 else
304 if ( (This->streamSize.u.HighPart == 0) &&
305 (This->streamSize.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
307 This->smallBlockChain = SmallBlockChainStream_Construct(
308 This->parentStorage->ancestorStorage,
309 This->ownerProperty);
311 else
313 This->bigBlockChain = BlockChainStream_Construct(
314 This->parentStorage->ancestorStorage,
315 NULL,
316 This->ownerProperty);
322 /***
323 * This method is part of the ISequentialStream interface.
325 * It reads a block of information from the stream at the current
326 * position. It then moves the current position at the end of the
327 * read block
329 * See the documentation of ISequentialStream for more info.
331 HRESULT WINAPI StgStreamImpl_Read(
332 IStream* iface,
333 void* pv, /* [length_is][size_is][out] */
334 ULONG cb, /* [in] */
335 ULONG* pcbRead) /* [out] */
337 StgStreamImpl* const This=(StgStreamImpl*)iface;
339 ULONG bytesReadBuffer;
340 ULONG bytesToReadFromBuffer;
341 HRESULT res = S_FALSE;
343 TRACE("(%p, %p, %ld, %p)\n",
344 iface, pv, cb, pcbRead);
347 * If the caller is not interested in the number of bytes read,
348 * we use another buffer to avoid "if" statements in the code.
350 if (pcbRead==0)
351 pcbRead = &bytesReadBuffer;
354 * Using the known size of the stream, calculate the number of bytes
355 * to read from the block chain
357 bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
360 * Depending on the type of chain that was opened when the stream was constructed,
361 * we delegate the work to the method that reads the block chains.
363 if (This->smallBlockChain!=0)
365 SmallBlockChainStream_ReadAt(This->smallBlockChain,
366 This->currentPosition,
367 bytesToReadFromBuffer,
369 pcbRead);
372 else if (This->bigBlockChain!=0)
374 BlockChainStream_ReadAt(This->bigBlockChain,
375 This->currentPosition,
376 bytesToReadFromBuffer,
378 pcbRead);
380 else
383 * Small and big block chains are both NULL. This case will happen
384 * when a stream starts with BLOCK_END_OF_CHAIN and has size zero.
387 *pcbRead = 0;
388 res = S_OK;
389 goto end;
393 * We should always be able to read the proper amount of data from the
394 * chain.
396 assert(bytesToReadFromBuffer == *pcbRead);
399 * Advance the pointer for the number of positions read.
401 This->currentPosition.u.LowPart += *pcbRead;
403 if(*pcbRead != cb)
405 WARN("read %ld instead of the required %ld bytes !\n", *pcbRead, cb);
407 * this used to return S_FALSE, however MSDN docu says that an app should
408 * be prepared to handle error in case of stream end reached, as *some*
409 * implementations *might* return an error (IOW: most do *not*).
410 * As some program fails on returning S_FALSE, I better use S_OK here.
412 res = S_OK;
414 else
415 res = S_OK;
417 end:
418 TRACE("<-- %08lx\n", res);
419 return res;
422 /***
423 * This method is part of the ISequentialStream interface.
425 * It writes a block of information to the stream at the current
426 * position. It then moves the current position at the end of the
427 * written block. If the stream is too small to fit the block,
428 * the stream is grown to fit.
430 * See the documentation of ISequentialStream for more info.
432 HRESULT WINAPI StgStreamImpl_Write(
433 IStream* iface,
434 const void* pv, /* [size_is][in] */
435 ULONG cb, /* [in] */
436 ULONG* pcbWritten) /* [out] */
438 StgStreamImpl* const This=(StgStreamImpl*)iface;
440 ULARGE_INTEGER newSize;
441 ULONG bytesWritten = 0;
443 TRACE("(%p, %p, %ld, %p)\n",
444 iface, pv, cb, pcbWritten);
447 * Do we have permission to write to this stream?
449 if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE))) {
450 return STG_E_ACCESSDENIED;
454 * If the caller is not interested in the number of bytes written,
455 * we use another buffer to avoid "if" statements in the code.
457 if (pcbWritten == 0)
458 pcbWritten = &bytesWritten;
461 * Initialize the out parameter
463 *pcbWritten = 0;
465 if (cb == 0)
467 return S_OK;
469 else
471 newSize.u.HighPart = 0;
472 newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
476 * Verify if we need to grow the stream
478 if (newSize.u.LowPart > This->streamSize.u.LowPart)
480 /* grow stream */
481 IStream_SetSize(iface, newSize);
485 * Depending on the type of chain that was opened when the stream was constructed,
486 * we delegate the work to the method that readwrites to the block chains.
488 if (This->smallBlockChain!=0)
490 SmallBlockChainStream_WriteAt(This->smallBlockChain,
491 This->currentPosition,
494 pcbWritten);
497 else if (This->bigBlockChain!=0)
499 BlockChainStream_WriteAt(This->bigBlockChain,
500 This->currentPosition,
503 pcbWritten);
505 else
506 assert(FALSE);
509 * Advance the position pointer for the number of positions written.
511 This->currentPosition.u.LowPart += *pcbWritten;
513 return S_OK;
516 /***
517 * This method is part of the IStream interface.
519 * It will move the current stream pointer according to the parameters
520 * given.
522 * See the documentation of IStream for more info.
524 HRESULT WINAPI StgStreamImpl_Seek(
525 IStream* iface,
526 LARGE_INTEGER dlibMove, /* [in] */
527 DWORD dwOrigin, /* [in] */
528 ULARGE_INTEGER* plibNewPosition) /* [out] */
530 StgStreamImpl* const This=(StgStreamImpl*)iface;
532 ULARGE_INTEGER newPosition;
534 TRACE("(%p, %ld, %ld, %p)\n",
535 iface, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
538 * The caller is allowed to pass in NULL as the new position return value.
539 * If it happens, we assign it to a dynamic variable to avoid special cases
540 * in the code below.
542 if (plibNewPosition == 0)
544 plibNewPosition = &newPosition;
548 * The file pointer is moved depending on the given "function"
549 * parameter.
551 switch (dwOrigin)
553 case STREAM_SEEK_SET:
554 plibNewPosition->u.HighPart = 0;
555 plibNewPosition->u.LowPart = 0;
556 break;
557 case STREAM_SEEK_CUR:
558 *plibNewPosition = This->currentPosition;
559 break;
560 case STREAM_SEEK_END:
561 *plibNewPosition = This->streamSize;
562 break;
563 default:
564 return STG_E_INVALIDFUNCTION;
567 plibNewPosition->QuadPart = RtlLargeIntegerAdd( plibNewPosition->QuadPart, dlibMove.QuadPart );
570 * tell the caller what we calculated
572 This->currentPosition = *plibNewPosition;
574 return S_OK;
577 /***
578 * This method is part of the IStream interface.
580 * It will change the size of a stream.
582 * TODO: Switch from small blocks to big blocks and vice versa.
584 * See the documentation of IStream for more info.
586 HRESULT WINAPI StgStreamImpl_SetSize(
587 IStream* iface,
588 ULARGE_INTEGER libNewSize) /* [in] */
590 StgStreamImpl* const This=(StgStreamImpl*)iface;
592 StgProperty curProperty;
593 BOOL Success;
595 TRACE("(%p, %ld)\n", iface, libNewSize.u.LowPart);
598 * As documented.
600 if (libNewSize.u.HighPart != 0)
601 return STG_E_INVALIDFUNCTION;
604 * Do we have permission?
606 if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
607 return STG_E_ACCESSDENIED;
609 if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
610 return S_OK;
613 * This will happen if we're creating a stream
615 if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
617 if (libNewSize.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
619 This->smallBlockChain = SmallBlockChainStream_Construct(
620 This->parentStorage->ancestorStorage,
621 This->ownerProperty);
623 else
625 This->bigBlockChain = BlockChainStream_Construct(
626 This->parentStorage->ancestorStorage,
627 NULL,
628 This->ownerProperty);
633 * Read this stream's property to see if it's small blocks or big blocks
635 Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
636 This->ownerProperty,
637 &curProperty);
639 * Determine if we have to switch from small to big blocks or vice versa
641 if ( (This->smallBlockChain!=0) &&
642 (curProperty.size.u.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
644 if (libNewSize.u.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
647 * Transform the small block chain into a big block chain
649 This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
650 This->parentStorage->ancestorStorage,
651 &This->smallBlockChain);
655 if (This->smallBlockChain!=0)
657 Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
659 else
661 Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
665 * Write the new information about this stream to the property
667 Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
668 This->ownerProperty,
669 &curProperty);
671 curProperty.size.u.HighPart = libNewSize.u.HighPart;
672 curProperty.size.u.LowPart = libNewSize.u.LowPart;
674 if (Success)
676 StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
677 This->ownerProperty,
678 &curProperty);
681 This->streamSize = libNewSize;
683 return S_OK;
686 /***
687 * This method is part of the IStream interface.
689 * It will copy the 'cb' Bytes to 'pstm' IStream.
691 * See the documentation of IStream for more info.
693 HRESULT WINAPI StgStreamImpl_CopyTo(
694 IStream* iface,
695 IStream* pstm, /* [unique][in] */
696 ULARGE_INTEGER cb, /* [in] */
697 ULARGE_INTEGER* pcbRead, /* [out] */
698 ULARGE_INTEGER* pcbWritten) /* [out] */
700 HRESULT hr = S_OK;
701 BYTE tmpBuffer[128];
702 ULONG bytesRead, bytesWritten, copySize;
703 ULARGE_INTEGER totalBytesRead;
704 ULARGE_INTEGER totalBytesWritten;
706 TRACE("(%p, %p, %ld, %p, %p)\n",
707 iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
710 * Sanity check
712 if ( pstm == 0 )
713 return STG_E_INVALIDPOINTER;
715 totalBytesRead.u.LowPart = totalBytesRead.u.HighPart = 0;
716 totalBytesWritten.u.LowPart = totalBytesWritten.u.HighPart = 0;
719 * use stack to store data temporarily
720 * there is surely a more performant way of doing it, for now this basic
721 * implementation will do the job
723 while ( cb.u.LowPart > 0 )
725 if ( cb.u.LowPart >= 128 )
726 copySize = 128;
727 else
728 copySize = cb.u.LowPart;
730 IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
732 totalBytesRead.u.LowPart += bytesRead;
734 IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
736 totalBytesWritten.u.LowPart += bytesWritten;
739 * Check that read & write operations were successful
741 if (bytesRead != bytesWritten)
743 hr = STG_E_MEDIUMFULL;
744 break;
747 if (bytesRead!=copySize)
748 cb.u.LowPart = 0;
749 else
750 cb.u.LowPart -= bytesRead;
754 * Update number of bytes read and written
756 if (pcbRead)
758 pcbRead->u.LowPart = totalBytesRead.u.LowPart;
759 pcbRead->u.HighPart = totalBytesRead.u.HighPart;
762 if (pcbWritten)
764 pcbWritten->u.LowPart = totalBytesWritten.u.LowPart;
765 pcbWritten->u.HighPart = totalBytesWritten.u.HighPart;
767 return hr;
770 /***
771 * This method is part of the IStream interface.
773 * For streams contained in structured storages, this method
774 * does nothing. This is what the documentation tells us.
776 * See the documentation of IStream for more info.
778 HRESULT WINAPI StgStreamImpl_Commit(
779 IStream* iface,
780 DWORD grfCommitFlags) /* [in] */
782 return S_OK;
785 /***
786 * This method is part of the IStream interface.
788 * For streams contained in structured storages, this method
789 * does nothing. This is what the documentation tells us.
791 * See the documentation of IStream for more info.
793 HRESULT WINAPI StgStreamImpl_Revert(
794 IStream* iface)
796 return S_OK;
799 HRESULT WINAPI StgStreamImpl_LockRegion(
800 IStream* iface,
801 ULARGE_INTEGER libOffset, /* [in] */
802 ULARGE_INTEGER cb, /* [in] */
803 DWORD dwLockType) /* [in] */
805 FIXME("not implemented!\n");
806 return E_NOTIMPL;
809 HRESULT WINAPI StgStreamImpl_UnlockRegion(
810 IStream* iface,
811 ULARGE_INTEGER libOffset, /* [in] */
812 ULARGE_INTEGER cb, /* [in] */
813 DWORD dwLockType) /* [in] */
815 FIXME("not implemented!\n");
816 return E_NOTIMPL;
819 /***
820 * This method is part of the IStream interface.
822 * This method returns information about the current
823 * stream.
825 * See the documentation of IStream for more info.
827 HRESULT WINAPI StgStreamImpl_Stat(
828 IStream* iface,
829 STATSTG* pstatstg, /* [out] */
830 DWORD grfStatFlag) /* [in] */
832 StgStreamImpl* const This=(StgStreamImpl*)iface;
834 StgProperty curProperty;
835 BOOL readSucessful;
838 * Read the information from the property.
840 readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
841 This->ownerProperty,
842 &curProperty);
844 if (readSucessful)
846 StorageUtl_CopyPropertyToSTATSTG(pstatstg,
847 &curProperty,
848 grfStatFlag);
850 pstatstg->grfMode = This->grfMode;
852 return S_OK;
855 return E_FAIL;
858 /***
859 * This method is part of the IStream interface.
861 * This method returns a clone of the interface that allows for
862 * another seek pointer
864 * See the documentation of IStream for more info.
866 * I am not totally sure what I am doing here but I presume that this
867 * should be basically as simple as creating a new stream with the same
868 * parent etc and positioning its seek cursor.
870 HRESULT WINAPI StgStreamImpl_Clone(
871 IStream* iface,
872 IStream** ppstm) /* [out] */
874 StgStreamImpl* const This=(StgStreamImpl*)iface;
875 HRESULT hres;
876 StgStreamImpl* new_stream;
877 LARGE_INTEGER seek_pos;
880 * Sanity check
882 if ( ppstm == 0 )
883 return STG_E_INVALIDPOINTER;
885 new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->ownerProperty);
887 if (!new_stream)
888 return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */
890 *ppstm = (IStream*) new_stream;
891 seek_pos.QuadPart = This->currentPosition.QuadPart;
893 hres=StgStreamImpl_Seek (*ppstm, seek_pos, STREAM_SEEK_SET, NULL);
895 assert (SUCCEEDED(hres));
897 return S_OK;