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

Last change on this file since 5280 was 5026, checked in by sandervl, 25 years ago

Wine resync

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