source: trunk/src/ole32/stg_stream.c@ 6502

Last change on this file since 6502 was 5602, checked in by sandervl, 24 years ago

resync with Wine 20010418

File size: 21.5 KB
Line 
1/*
2 * Compound Storage (32 bit version)
3 * Stream implementation
4 *
5 * This file contains the implementation of the stream interface
6 * for streams contained in a compound storage.
7 *
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Thuy Nguyen
10 */
11#ifdef __WIN32OS2__
12
13#include <odin.h>
14#include "ole32.h"
15#include "heapstring.h"
16
17#endif
18#include <assert.h>
19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22
23#include "winbase.h"
24#include "winerror.h"
25#include "ntddk.h"
26#include "debugtools.h"
27
28#include "storage32.h"
29
30DEFAULT_DEBUG_CHANNEL(storage);
31
32
33/*
34 * Virtual function table for the StgStreamImpl class.
35 */
36static ICOM_VTABLE(IStream) StgStreamImpl_Vtbl =
37{
38 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
39 StgStreamImpl_QueryInterface,
40 StgStreamImpl_AddRef,
41 StgStreamImpl_Release,
42 StgStreamImpl_Read,
43 StgStreamImpl_Write,
44 StgStreamImpl_Seek,
45 StgStreamImpl_SetSize,
46 StgStreamImpl_CopyTo,
47 StgStreamImpl_Commit,
48 StgStreamImpl_Revert,
49 StgStreamImpl_LockRegion,
50 StgStreamImpl_UnlockRegion,
51 StgStreamImpl_Stat,
52 StgStreamImpl_Clone
53};
54
55/******************************************************************************
56** StgStreamImpl implementation
57*/
58
59/***
60 * This is the constructor for the StgStreamImpl class.
61 *
62 * Params:
63 * parentStorage - Pointer to the storage that contains the stream to open
64 * ownerProperty - Index of the property that points to this stream.
65 */
66StgStreamImpl* StgStreamImpl_Construct(
67 StorageBaseImpl* parentStorage,
68 DWORD grfMode,
69 ULONG ownerProperty)
70{
71 StgStreamImpl* newStream;
72
73 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
74
75 if (newStream!=0)
76 {
77 /*
78 * Set-up the virtual function table and reference count.
79 */
80 ICOM_VTBL(newStream) = &StgStreamImpl_Vtbl;
81 newStream->ref = 0;
82
83 /*
84 * We want to nail-down the reference to the storage in case the
85 * stream out-lives the storage in the client application.
86 */
87 newStream->parentStorage = parentStorage;
88 IStorage_AddRef((IStorage*)newStream->parentStorage);
89
90 newStream->grfMode = grfMode;
91 newStream->ownerProperty = ownerProperty;
92
93 /*
94 * Start the stream at the begining.
95 */
96 newStream->currentPosition.s.HighPart = 0;
97 newStream->currentPosition.s.LowPart = 0;
98
99 /*
100 * Initialize the rest of the data.
101 */
102 newStream->streamSize.s.HighPart = 0;
103 newStream->streamSize.s.LowPart = 0;
104 newStream->bigBlockChain = 0;
105 newStream->smallBlockChain = 0;
106
107 /*
108 * Read the size from the property and determine if the blocks forming
109 * this stream are large or small.
110 */
111 StgStreamImpl_OpenBlockChain(newStream);
112 }
113
114 return newStream;
115}
116
117/***
118 * This is the destructor of the StgStreamImpl class.
119 *
120 * This method will clean-up all the resources used-up by the given StgStreamImpl
121 * class. The pointer passed-in to this function will be freed and will not
122 * be valid anymore.
123 */
124void StgStreamImpl_Destroy(StgStreamImpl* This)
125{
126 TRACE("(%p)\n", This);
127
128 /*
129 * Release the reference we are holding on the parent storage.
130 */
131 IStorage_Release((IStorage*)This->parentStorage);
132 This->parentStorage = 0;
133
134 /*
135 * Make sure we clean-up the block chain stream objects that we were using.
136 */
137 if (This->bigBlockChain != 0)
138 {
139 BlockChainStream_Destroy(This->bigBlockChain);
140 This->bigBlockChain = 0;
141 }
142
143 if (This->smallBlockChain != 0)
144 {
145 SmallBlockChainStream_Destroy(This->smallBlockChain);
146 This->smallBlockChain = 0;
147 }
148
149 /*
150 * Finally, free the memory used-up by the class.
151 */
152 HeapFree(GetProcessHeap(), 0, This);
153}
154
155/***
156 * This implements the IUnknown method QueryInterface for this
157 * class
158 */
159HRESULT WINAPI StgStreamImpl_QueryInterface(
160 IStream* iface,
161 REFIID riid, /* [in] */
162 void** ppvObject) /* [iid_is][out] */
163{
164 StgStreamImpl* const This=(StgStreamImpl*)iface;
165
166 /*
167 * Perform a sanity check on the parameters.
168 */
169 if (ppvObject==0)
170 return E_INVALIDARG;
171
172 /*
173 * Initialize the return parameter.
174 */
175 *ppvObject = 0;
176
177 /*
178 * Compare the riid with the interface IDs implemented by this object.
179 */
180 if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
181 {
182 *ppvObject = (IStream*)This;
183 }
184 else if (memcmp(&IID_IStream, riid, sizeof(IID_IStream)) == 0)
185 {
186 *ppvObject = (IStream*)This;
187 }
188
189 /*
190 * Check that we obtained an interface.
191 */
192 if ((*ppvObject)==0)
193 return E_NOINTERFACE;
194
195 /*
196 * Query Interface always increases the reference count by one when it is
197 * successful
198 */
199 StgStreamImpl_AddRef(iface);
200
201 return S_OK;;
202}
203
204/***
205 * This implements the IUnknown method AddRef for this
206 * class
207 */
208ULONG WINAPI StgStreamImpl_AddRef(
209 IStream* iface)
210{
211 StgStreamImpl* const This=(StgStreamImpl*)iface;
212
213 This->ref++;
214
215 return This->ref;
216}
217
218/***
219 * This implements the IUnknown method Release for this
220 * class
221 */
222ULONG WINAPI StgStreamImpl_Release(
223 IStream* iface)
224{
225 StgStreamImpl* const This=(StgStreamImpl*)iface;
226
227 ULONG newRef;
228
229 This->ref--;
230
231 newRef = This->ref;
232
233 /*
234 * If the reference count goes down to 0, perform suicide.
235 */
236 if (newRef==0)
237 {
238 StgStreamImpl_Destroy(This);
239 }
240
241 return newRef;
242}
243
244/***
245 * This method will open the block chain pointed by the property
246 * that describes the stream.
247 * If the stream's size is null, no chain is opened.
248 */
249void StgStreamImpl_OpenBlockChain(
250 StgStreamImpl* This)
251{
252 StgProperty curProperty;
253 BOOL readSucessful;
254
255 /*
256 * Make sure no old object is staying behind.
257 */
258 if (This->smallBlockChain != 0)
259 {
260 SmallBlockChainStream_Destroy(This->smallBlockChain);
261 This->smallBlockChain = 0;
262 }
263
264 if (This->bigBlockChain != 0)
265 {
266 BlockChainStream_Destroy(This->bigBlockChain);
267 This->bigBlockChain = 0;
268 }
269
270 /*
271 * Read the information from the property.
272 */
273 readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
274 This->ownerProperty,
275 &curProperty);
276
277 if (readSucessful)
278 {
279 This->streamSize = curProperty.size;
280
281 /*
282 * This code supports only streams that are <32 bits in size.
283 */
284 assert(This->streamSize.s.HighPart == 0);
285
286 if(curProperty.startingBlock == BLOCK_END_OF_CHAIN)
287 {
288 assert( (This->streamSize.s.HighPart == 0) && (This->streamSize.s.LowPart == 0) );
289 }
290 else
291 {
292 if ( (This->streamSize.s.HighPart == 0) &&
293 (This->streamSize.s.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
294 {
295 This->smallBlockChain = SmallBlockChainStream_Construct(
296 This->parentStorage->ancestorStorage,
297 This->ownerProperty);
298 }
299 else
300 {
301 This->bigBlockChain = BlockChainStream_Construct(
302 This->parentStorage->ancestorStorage,
303 NULL,
304 This->ownerProperty);
305 }
306 }
307 }
308}
309
310/***
311 * This method is part of the ISequentialStream interface.
312 *
313 * If reads a block of information from the stream at the current
314 * position. It then moves the current position at the end of the
315 * read block
316 *
317 * See the documentation of ISequentialStream for more info.
318 */
319HRESULT WINAPI StgStreamImpl_Read(
320 IStream* iface,
321 void* pv, /* [length_is][size_is][out] */
322 ULONG cb, /* [in] */
323 ULONG* pcbRead) /* [out] */
324{
325 StgStreamImpl* const This=(StgStreamImpl*)iface;
326
327 ULONG bytesReadBuffer;
328 ULONG bytesToReadFromBuffer;
329
330 TRACE("(%p, %p, %ld, %p)\n",
331 iface, pv, cb, pcbRead);
332
333 /*
334 * If the caller is not interested in the nubmer of bytes read,
335 * we use another buffer to avoid "if" statements in the code.
336 */
337 if (pcbRead==0)
338 pcbRead = &bytesReadBuffer;
339
340 /*
341 * Using the known size of the stream, calculate the number of bytes
342 * to read from the block chain
343 */
344 bytesToReadFromBuffer = min( This->streamSize.s.LowPart - This->currentPosition.s.LowPart, cb);
345
346 /*
347 * Depending on the type of chain that was opened when the stream was constructed,
348 * we delegate the work to the method that read the block chains.
349 */
350 if (This->smallBlockChain!=0)
351 {
352 SmallBlockChainStream_ReadAt(This->smallBlockChain,
353 This->currentPosition,
354 bytesToReadFromBuffer,
355 pv,
356 pcbRead);
357
358 }
359 else if (This->bigBlockChain!=0)
360 {
361 BlockChainStream_ReadAt(This->bigBlockChain,
362 This->currentPosition,
363 bytesToReadFromBuffer,
364 pv,
365 pcbRead);
366 }
367 else
368 {
369 /*
370 * Small and big block chains are both NULL. This case will happen
371 * when a stream starts with BLOCK_END_OF_CHAIN and has size zero.
372 */
373
374 *pcbRead = 0;
375 return S_OK;
376 }
377
378 /*
379 * We should always be able to read the proper amount of data from the
380 * chain.
381 */
382 assert(bytesToReadFromBuffer == *pcbRead);
383
384 /*
385 * Advance the pointer for the number of positions read.
386 */
387 This->currentPosition.s.LowPart += *pcbRead;
388
389 /*
390 * The function returns S_OK if the buffer was filled completely
391 * it returns S_FALSE if the end of the stream is reached before the
392 * buffer is filled
393 */
394 if(*pcbRead == cb)
395 return S_OK;
396
397 return S_FALSE;
398}
399
400/***
401 * This method is part of the ISequentialStream interface.
402 *
403 * It writes a block of information to the stream at the current
404 * position. It then moves the current position at the end of the
405 * written block. If the stream is too small to fit the block,
406 * the stream is grown to fit.
407 *
408 * See the documentation of ISequentialStream for more info.
409 */
410HRESULT WINAPI StgStreamImpl_Write(
411 IStream* iface,
412 const void* pv, /* [size_is][in] */
413 ULONG cb, /* [in] */
414 ULONG* pcbWritten) /* [out] */
415{
416 StgStreamImpl* const This=(StgStreamImpl*)iface;
417
418 ULARGE_INTEGER newSize;
419 ULONG bytesWritten = 0;
420
421 TRACE("(%p, %p, %ld, %p)\n",
422 iface, pv, cb, pcbWritten);
423
424 /*
425 * Do we have permission to write to this stream?
426 */
427 if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE))) {
428 return STG_E_ACCESSDENIED;
429 }
430
431 /*
432 * If the caller is not interested in the number of bytes written,
433 * we use another buffer to avoid "if" statements in the code.
434 */
435 if (pcbWritten == 0)
436 pcbWritten = &bytesWritten;
437
438 /*
439 * Initialize the out parameter
440 */
441 *pcbWritten = 0;
442
443 if (cb == 0)
444 {
445 return S_OK;
446 }
447 else
448 {
449 newSize.s.HighPart = 0;
450 newSize.s.LowPart = This->currentPosition.s.LowPart + cb;
451 }
452
453 /*
454 * Verify if we need to grow the stream
455 */
456 if (newSize.s.LowPart > This->streamSize.s.LowPart)
457 {
458 /* grow stream */
459 IStream_SetSize(iface, newSize);
460 }
461
462 /*
463 * Depending on the type of chain that was opened when the stream was constructed,
464 * we delegate the work to the method that readwrites to the block chains.
465 */
466 if (This->smallBlockChain!=0)
467 {
468 SmallBlockChainStream_WriteAt(This->smallBlockChain,
469 This->currentPosition,
470 cb,
471 pv,
472 pcbWritten);
473
474 }
475 else if (This->bigBlockChain!=0)
476 {
477 BlockChainStream_WriteAt(This->bigBlockChain,
478 This->currentPosition,
479 cb,
480 pv,
481 pcbWritten);
482 }
483 else
484 assert(FALSE);
485
486 /*
487 * Advance the position pointer for the number of positions written.
488 */
489 This->currentPosition.s.LowPart += *pcbWritten;
490
491 return S_OK;
492}
493
494/***
495 * This method is part of the IStream interface.
496 *
497 * It will move the current stream pointer according to the parameters
498 * given.
499 *
500 * See the documentation of IStream for more info.
501 */
502HRESULT WINAPI StgStreamImpl_Seek(
503 IStream* iface,
504 LARGE_INTEGER dlibMove, /* [in] */
505 DWORD dwOrigin, /* [in] */
506 ULARGE_INTEGER* plibNewPosition) /* [out] */
507{
508 StgStreamImpl* const This=(StgStreamImpl*)iface;
509
510 ULARGE_INTEGER newPosition;
511
512 TRACE("(%p, %ld, %ld, %p)\n",
513 iface, dlibMove.s.LowPart, dwOrigin, plibNewPosition);
514
515 /*
516 * The caller is allowed to pass in NULL as the new position return value.
517 * If it happens, we assign it to a dynamic variable to avoid special cases
518 * in the code below.
519 */
520 if (plibNewPosition == 0)
521 {
522 plibNewPosition = &newPosition;
523 }
524
525 /*
526 * The file pointer is moved depending on the given "function"
527 * parameter.
528 */
529 switch (dwOrigin)
530 {
531 case STREAM_SEEK_SET:
532 plibNewPosition->s.HighPart = 0;
533 plibNewPosition->s.LowPart = 0;
534 break;
535 case STREAM_SEEK_CUR:
536 *plibNewPosition = This->currentPosition;
537 break;
538 case STREAM_SEEK_END:
539 *plibNewPosition = This->streamSize;
540 break;
541 default:
542 return STG_E_INVALIDFUNCTION;
543 }
544#ifdef __WIN32OS2__
545 /*
546 * do some multiword arithmetic:
547 * treat HighPart as a signed value
548 * treat LowPart as unsigned
549 * NOTE: this stuff is two's complement specific!
550 */
551 if (dlibMove.s.HighPart < 0) { /* dlibMove is < 0 */
552 /* calculate the absolute value of dlibMove ... */
553 dlibMove.s.HighPart = -dlibMove.s.HighPart;
554 dlibMove.s.LowPart ^= -1;
555 /* ... and subtract with carry */
556 if (dlibMove.s.LowPart > plibNewPosition->s.LowPart) {
557 /* carry needed, This accounts for any underflows at [1]*/
558 plibNewPosition->s.HighPart -= 1;
559 }
560 plibNewPosition->s.LowPart -= dlibMove.s.LowPart; /* [1] */
561 plibNewPosition->s.HighPart -= dlibMove.s.HighPart;
562 } else {
563 /* add directly */
564 int initialLowPart = plibNewPosition->s.LowPart;
565 plibNewPosition->s.LowPart += dlibMove.s.LowPart;
566 if((plibNewPosition->s.LowPart < initialLowPart) ||
567 (plibNewPosition->s.LowPart < dlibMove.s.LowPart)) {
568 /* LowPart has rolled over => add the carry digit to HighPart */
569 plibNewPosition->s.HighPart++;
570 }
571 plibNewPosition->s.HighPart += dlibMove.s.HighPart;
572 }
573 /*
574 * Check if we end-up before the beginning of the file. That should
575 * trigger an error.
576 */
577 if (plibNewPosition->s.HighPart < 0) {
578 return STG_E_INVALIDPOINTER;
579 }
580
581 /*
582 * We currently don't support files with offsets of >32 bits.
583 * Note that we have checked for a negative offset already
584 */
585 assert(plibNewPosition->s.HighPart <= 0);
586
587#else
588 plibNewPosition->QuadPart = RtlLargeIntegerAdd( plibNewPosition->QuadPart, dlibMove.QuadPart );
589#endif
590
591 /*
592 * tell the caller what we calculated
593 */
594 This->currentPosition = *plibNewPosition;
595
596 return S_OK;
597}
598
599/***
600 * This method is part of the IStream interface.
601 *
602 * It will change the size of a stream.
603 *
604 * TODO: Switch from small blocks to big blocks and vice versa.
605 *
606 * See the documentation of IStream for more info.
607 */
608HRESULT WINAPI StgStreamImpl_SetSize(
609 IStream* iface,
610 ULARGE_INTEGER libNewSize) /* [in] */
611{
612 StgStreamImpl* const This=(StgStreamImpl*)iface;
613
614 StgProperty curProperty;
615 BOOL Success;
616
617 TRACE("(%p, %ld)\n", iface, libNewSize.s.LowPart);
618
619 /*
620 * As documented.
621 */
622 if (libNewSize.s.HighPart != 0)
623 return STG_E_INVALIDFUNCTION;
624
625 /*
626 * Do we have permission?
627 */
628 if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
629 return STG_E_ACCESSDENIED;
630
631 if (This->streamSize.s.LowPart == libNewSize.s.LowPart)
632 return S_OK;
633
634 /*
635 * This will happen if we're creating a stream
636 */
637 if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
638 {
639 if (libNewSize.s.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
640 {
641 This->smallBlockChain = SmallBlockChainStream_Construct(
642 This->parentStorage->ancestorStorage,
643 This->ownerProperty);
644 }
645 else
646 {
647 This->bigBlockChain = BlockChainStream_Construct(
648 This->parentStorage->ancestorStorage,
649 NULL,
650 This->ownerProperty);
651 }
652 }
653
654 /*
655 * Read this stream's property to see if it's small blocks or big blocks
656 */
657 Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
658 This->ownerProperty,
659 &curProperty);
660 /*
661 * Determine if we have to switch from small to big blocks or vice versa
662 */
663 if ( (This->smallBlockChain!=0) &&
664 (curProperty.size.s.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
665 {
666 if (libNewSize.s.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
667 {
668 /*
669 * Transform the small block chain into a big block chain
670 */
671 This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
672 This->parentStorage->ancestorStorage,
673 &This->smallBlockChain);
674 }
675 }
676
677 if (This->smallBlockChain!=0)
678 {
679 Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
680 }
681 else
682 {
683 Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
684 }
685
686 /*
687 * Write to the property the new information about this stream
688 */
689 Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
690 This->ownerProperty,
691 &curProperty);
692
693 curProperty.size.s.HighPart = libNewSize.s.HighPart;
694 curProperty.size.s.LowPart = libNewSize.s.LowPart;
695
696 if (Success)
697 {
698 StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
699 This->ownerProperty,
700 &curProperty);
701 }
702
703 This->streamSize = libNewSize;
704
705 return S_OK;
706}
707
708/***
709 * This method is part of the IStream interface.
710 *
711 * It will copy the 'cb' Bytes to 'pstm' IStream.
712 *
713 * See the documentation of IStream for more info.
714 */
715HRESULT WINAPI StgStreamImpl_CopyTo(
716 IStream* iface,
717 IStream* pstm, /* [unique][in] */
718 ULARGE_INTEGER cb, /* [in] */
719 ULARGE_INTEGER* pcbRead, /* [out] */
720 ULARGE_INTEGER* pcbWritten) /* [out] */
721{
722 HRESULT hr = S_OK;
723 BYTE tmpBuffer[128];
724 ULONG bytesRead, bytesWritten, copySize;
725 ULARGE_INTEGER totalBytesRead;
726 ULARGE_INTEGER totalBytesWritten;
727
728 TRACE("(%p, %p, %ld, %p, %p)\n",
729 iface, pstm, cb.s.LowPart, pcbRead, pcbWritten);
730
731 /*
732 * Sanity check
733 */
734 if ( pstm == 0 )
735 return STG_E_INVALIDPOINTER;
736
737 totalBytesRead.s.LowPart = totalBytesRead.s.HighPart = 0;
738 totalBytesWritten.s.LowPart = totalBytesWritten.s.HighPart = 0;
739
740 /*
741 * use stack to store data temporarly
742 * there is surely more performant way of doing it, for now this basic
743 * implementation will do the job
744 */
745 while ( cb.s.LowPart > 0 )
746 {
747 if ( cb.s.LowPart >= 128 )
748 copySize = 128;
749 else
750 copySize = cb.s.LowPart;
751
752 IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
753
754 totalBytesRead.s.LowPart += bytesRead;
755
756 IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
757
758 totalBytesWritten.s.LowPart += bytesWritten;
759
760 /*
761 * Check that read & write operations were succesfull
762 */
763 if (bytesRead != bytesWritten)
764 {
765 hr = STG_E_MEDIUMFULL;
766 break;
767 }
768
769 if (bytesRead!=copySize)
770 cb.s.LowPart = 0;
771 else
772 cb.s.LowPart -= bytesRead;
773 }
774
775 /*
776 * Update number of bytes read and written
777 */
778 if (pcbRead)
779 {
780 pcbRead->s.LowPart = totalBytesRead.s.LowPart;
781 pcbRead->s.HighPart = totalBytesRead.s.HighPart;
782 }
783
784 if (pcbWritten)
785 {
786 pcbWritten->s.LowPart = totalBytesWritten.s.LowPart;
787 pcbWritten->s.HighPart = totalBytesWritten.s.HighPart;
788 }
789 return hr;
790}
791
792/***
793 * This method is part of the IStream interface.
794 *
795 * For streams contained in structured storages, this method
796 * does nothing. This is what the documentation tells us.
797 *
798 * See the documentation of IStream for more info.
799 */
800HRESULT WINAPI StgStreamImpl_Commit(
801 IStream* iface,
802 DWORD grfCommitFlags) /* [in] */
803{
804 return S_OK;
805}
806
807/***
808 * This method is part of the IStream interface.
809 *
810 * For streams contained in structured storages, this method
811 * does nothing. This is what the documentation tells us.
812 *
813 * See the documentation of IStream for more info.
814 */
815HRESULT WINAPI StgStreamImpl_Revert(
816 IStream* iface)
817{
818 return S_OK;
819}
820
821HRESULT WINAPI StgStreamImpl_LockRegion(
822 IStream* iface,
823 ULARGE_INTEGER libOffset, /* [in] */
824 ULARGE_INTEGER cb, /* [in] */
825 DWORD dwLockType) /* [in] */
826{
827 FIXME("not implemented!\n");
828 return E_NOTIMPL;
829}
830
831HRESULT WINAPI StgStreamImpl_UnlockRegion(
832 IStream* iface,
833 ULARGE_INTEGER libOffset, /* [in] */
834 ULARGE_INTEGER cb, /* [in] */
835 DWORD dwLockType) /* [in] */
836{
837 FIXME("not implemented!\n");
838 return E_NOTIMPL;
839}
840
841/***
842 * This method is part of the IStream interface.
843 *
844 * This method returns information about the current
845 * stream.
846 *
847 * See the documentation of IStream for more info.
848 */
849HRESULT WINAPI StgStreamImpl_Stat(
850 IStream* iface,
851 STATSTG* pstatstg, /* [out] */
852 DWORD grfStatFlag) /* [in] */
853{
854 StgStreamImpl* const This=(StgStreamImpl*)iface;
855
856 StgProperty curProperty;
857 BOOL readSucessful;
858
859 /*
860 * Read the information from the property.
861 */
862 readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
863 This->ownerProperty,
864 &curProperty);
865
866 if (readSucessful)
867 {
868 StorageUtl_CopyPropertyToSTATSTG(pstatstg,
869 &curProperty,
870 grfStatFlag);
871
872 pstatstg->grfMode = This->grfMode;
873
874 return S_OK;
875 }
876
877 return E_FAIL;
878}
879
880HRESULT WINAPI StgStreamImpl_Clone(
881 IStream* iface,
882 IStream** ppstm) /* [out] */
883{
884 FIXME("not implemented!\n");
885 return E_NOTIMPL;
886}
Note: See TracBrowser for help on using the repository browser.