msvcr80: Add some 64-bit only exports.
[wine] / dlls / ole32 / hglobalstream.c
1 /*
2  * HGLOBAL Stream implementation
3  *
4  * This file contains the implementation of the stream interface
5  * for streams contained supported by an HGLOBAL pointer.
6  *
7  * Copyright 1999 Francis Beaudet
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #define COBJMACROS
33 #define NONAMELESSUNION
34 #define NONAMELESSSTRUCT
35
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winuser.h"
39 #include "objbase.h"
40 #include "ole2.h"
41 #include "winerror.h"
42 #include "winternl.h"
43
44 #include "wine/debug.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(storage);
47
48 /****************************************************************************
49  * HGLOBALStreamImpl definition.
50  *
51  * This class implements the IStream interface and represents a stream
52  * supported by an HGLOBAL pointer.
53  */
54 typedef struct
55 {
56   IStream IStream_iface;
57   LONG ref;
58
59   /* support for the stream */
60   HGLOBAL supportHandle;
61
62   /* if TRUE the HGLOBAL is destroyed when the stream is finally released */
63   BOOL deleteOnRelease;
64
65   /* size of the stream */
66   ULARGE_INTEGER streamSize;
67
68   /* current position of the cursor */
69   ULARGE_INTEGER currentPosition;
70 } HGLOBALStreamImpl;
71
72 static inline HGLOBALStreamImpl *impl_from_IStream(IStream *iface)
73 {
74   return CONTAINING_RECORD(iface, HGLOBALStreamImpl, IStream_iface);
75 }
76
77 static HRESULT WINAPI HGLOBALStreamImpl_QueryInterface(
78                   IStream*     iface,
79                   REFIID         riid,        /* [in] */
80                   void**         ppvObject)   /* [iid_is][out] */
81 {
82   HGLOBALStreamImpl* This = impl_from_IStream(iface);
83
84   if (ppvObject==0)
85     return E_INVALIDARG;
86
87   *ppvObject = 0;
88
89   if (IsEqualIID(&IID_IUnknown, riid) ||
90       IsEqualIID(&IID_ISequentialStream, riid) ||
91       IsEqualIID(&IID_IStream, riid))
92   {
93     *ppvObject = This;
94   }
95
96   if ((*ppvObject)==0)
97     return E_NOINTERFACE;
98
99   IStream_AddRef(iface);
100
101   return S_OK;
102 }
103
104 static ULONG WINAPI HGLOBALStreamImpl_AddRef(IStream* iface)
105 {
106   HGLOBALStreamImpl* This = impl_from_IStream(iface);
107   return InterlockedIncrement(&This->ref);
108 }
109
110 static ULONG WINAPI HGLOBALStreamImpl_Release(
111                 IStream* iface)
112 {
113   HGLOBALStreamImpl* This= impl_from_IStream(iface);
114   ULONG ref = InterlockedDecrement(&This->ref);
115
116   if (!ref)
117   {
118     if (This->deleteOnRelease)
119     {
120       GlobalFree(This->supportHandle);
121       This->supportHandle = NULL;
122     }
123
124     HeapFree(GetProcessHeap(), 0, This);
125   }
126
127   return ref;
128 }
129
130 /***
131  * This method is part of the ISequentialStream interface.
132  *
133  * If reads a block of information from the stream at the current
134  * position. It then moves the current position at the end of the
135  * read block
136  *
137  * See the documentation of ISequentialStream for more info.
138  */
139 static HRESULT WINAPI HGLOBALStreamImpl_Read(
140                   IStream*     iface,
141                   void*          pv,        /* [length_is][size_is][out] */
142                   ULONG          cb,        /* [in] */
143                   ULONG*         pcbRead)   /* [out] */
144 {
145   HGLOBALStreamImpl* This = impl_from_IStream(iface);
146
147   void* supportBuffer;
148   ULONG bytesReadBuffer;
149   ULONG bytesToReadFromBuffer;
150
151   TRACE("(%p, %p, %d, %p)\n", iface,
152         pv, cb, pcbRead);
153
154   /*
155    * If the caller is not interested in the number of bytes read,
156    * we use another buffer to avoid "if" statements in the code.
157    */
158   if (pcbRead==0)
159     pcbRead = &bytesReadBuffer;
160
161   /*
162    * Using the known size of the stream, calculate the number of bytes
163    * to read from the block chain
164    */
165   bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
166
167   /*
168    * Lock the buffer in position and copy the data.
169    */
170   supportBuffer = GlobalLock(This->supportHandle);
171   if (!supportBuffer)
172   {
173       WARN("read from invalid hglobal %p\n", This->supportHandle);
174       *pcbRead = 0;
175       return S_OK;
176   }
177
178   memcpy(pv, (char *) supportBuffer+This->currentPosition.u.LowPart, bytesToReadFromBuffer);
179
180   /*
181    * Move the current position to the new position
182    */
183   This->currentPosition.u.LowPart+=bytesToReadFromBuffer;
184
185   /*
186    * Return the number of bytes read.
187    */
188   *pcbRead = bytesToReadFromBuffer;
189
190   /*
191    * Cleanup
192    */
193   GlobalUnlock(This->supportHandle);
194
195   /*
196    * Always returns S_OK even if the end of the stream is reached before the
197    * buffer is filled
198    */
199
200   return S_OK;
201 }
202
203 /***
204  * This method is part of the ISequentialStream interface.
205  *
206  * It writes a block of information to the stream at the current
207  * position. It then moves the current position at the end of the
208  * written block. If the stream is too small to fit the block,
209  * the stream is grown to fit.
210  *
211  * See the documentation of ISequentialStream for more info.
212  */
213 static HRESULT WINAPI HGLOBALStreamImpl_Write(
214                   IStream*     iface,
215                   const void*    pv,          /* [size_is][in] */
216                   ULONG          cb,          /* [in] */
217                   ULONG*         pcbWritten)  /* [out] */
218 {
219   HGLOBALStreamImpl* This = impl_from_IStream(iface);
220
221   void*          supportBuffer;
222   ULARGE_INTEGER newSize;
223   ULONG          bytesWritten = 0;
224
225   TRACE("(%p, %p, %d, %p)\n", iface, pv, cb, pcbWritten);
226
227   /*
228    * If the caller is not interested in the number of bytes written,
229    * we use another buffer to avoid "if" statements in the code.
230    */
231   if (pcbWritten == 0)
232     pcbWritten = &bytesWritten;
233
234   if (cb == 0)
235     goto out;
236
237   *pcbWritten = 0;
238
239   newSize.u.HighPart = 0;
240   newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
241
242   /*
243    * Verify if we need to grow the stream
244    */
245   if (newSize.u.LowPart > This->streamSize.u.LowPart)
246   {
247     /* grow stream */
248     HRESULT hr = IStream_SetSize(iface, newSize);
249     if (FAILED(hr))
250     {
251       ERR("IStream_SetSize failed with error 0x%08x\n", hr);
252       return hr;
253     }
254   }
255
256   /*
257    * Lock the buffer in position and copy the data.
258    */
259   supportBuffer = GlobalLock(This->supportHandle);
260   if (!supportBuffer)
261   {
262       WARN("write to invalid hglobal %p\n", This->supportHandle);
263       return S_OK;
264   }
265
266   memcpy((char *) supportBuffer+This->currentPosition.u.LowPart, pv, cb);
267
268   /*
269    * Move the current position to the new position
270    */
271   This->currentPosition.u.LowPart+=cb;
272
273   /*
274    * Cleanup
275    */
276   GlobalUnlock(This->supportHandle);
277
278 out:
279   /*
280    * Return the number of bytes read.
281    */
282   *pcbWritten = cb;
283
284   return S_OK;
285 }
286
287 /***
288  * This method is part of the IStream interface.
289  *
290  * It will move the current stream pointer according to the parameters
291  * given.
292  *
293  * See the documentation of IStream for more info.
294  */
295 static HRESULT WINAPI HGLOBALStreamImpl_Seek(
296                   IStream*      iface,
297                   LARGE_INTEGER   dlibMove,         /* [in] */
298                   DWORD           dwOrigin,         /* [in] */
299                   ULARGE_INTEGER* plibNewPosition) /* [out] */
300 {
301   HGLOBALStreamImpl* This = impl_from_IStream(iface);
302
303   ULARGE_INTEGER newPosition = This->currentPosition;
304   HRESULT hr = S_OK;
305
306   TRACE("(%p, %x%08x, %d, %p)\n", iface, dlibMove.u.HighPart,
307         dlibMove.u.LowPart, dwOrigin, plibNewPosition);
308
309   /*
310    * The file pointer is moved depending on the given "function"
311    * parameter.
312    */
313   switch (dwOrigin)
314   {
315     case STREAM_SEEK_SET:
316       newPosition.u.HighPart = 0;
317       newPosition.u.LowPart = 0;
318       break;
319     case STREAM_SEEK_CUR:
320       break;
321     case STREAM_SEEK_END:
322       newPosition = This->streamSize;
323       break;
324     default:
325       hr = STG_E_SEEKERROR;
326       goto end;
327   }
328
329   /*
330    * Move the actual file pointer
331    * If the file pointer ends-up after the end of the stream, the next Write operation will
332    * make the file larger. This is how it is documented.
333    */
334   newPosition.u.HighPart = 0;
335   newPosition.u.LowPart += dlibMove.QuadPart;
336
337   if (dlibMove.u.LowPart >= 0x80000000 &&
338       newPosition.u.LowPart >= dlibMove.u.LowPart)
339   {
340     /* We tried to seek backwards and went past the start. */
341     hr = STG_E_SEEKERROR;
342     goto end;
343   }
344
345   This->currentPosition = newPosition;
346
347 end:
348   if (plibNewPosition) *plibNewPosition = This->currentPosition;
349
350   return hr;
351 }
352
353 /***
354  * This method is part of the IStream interface.
355  *
356  * It will change the size of a stream.
357  *
358  * TODO: Switch from small blocks to big blocks and vice versa.
359  *
360  * See the documentation of IStream for more info.
361  */
362 static HRESULT WINAPI HGLOBALStreamImpl_SetSize(
363                                      IStream*      iface,
364                                      ULARGE_INTEGER  libNewSize)   /* [in] */
365 {
366   HGLOBALStreamImpl* This = impl_from_IStream(iface);
367   HGLOBAL supportHandle;
368
369   TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart);
370
371   /*
372    * HighPart is ignored as shown in tests
373    */
374
375   if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
376     return S_OK;
377
378   /*
379    * Re allocate the HGlobal to fit the new size of the stream.
380    */
381   supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
382
383   if (supportHandle == 0)
384     return E_OUTOFMEMORY;
385
386   This->supportHandle = supportHandle;
387   This->streamSize.u.LowPart = libNewSize.u.LowPart;
388
389   return S_OK;
390 }
391
392 /***
393  * This method is part of the IStream interface.
394  *
395  * It will copy the 'cb' Bytes to 'pstm' IStream.
396  *
397  * See the documentation of IStream for more info.
398  */
399 static HRESULT WINAPI HGLOBALStreamImpl_CopyTo(
400                                     IStream*      iface,
401                                     IStream*      pstm,         /* [unique][in] */
402                                     ULARGE_INTEGER  cb,           /* [in] */
403                                     ULARGE_INTEGER* pcbRead,      /* [out] */
404                                     ULARGE_INTEGER* pcbWritten)   /* [out] */
405 {
406   HRESULT        hr = S_OK;
407   BYTE           tmpBuffer[128];
408   ULONG          bytesRead, bytesWritten, copySize;
409   ULARGE_INTEGER totalBytesRead;
410   ULARGE_INTEGER totalBytesWritten;
411
412   TRACE("(%p, %p, %d, %p, %p)\n", iface, pstm,
413         cb.u.LowPart, pcbRead, pcbWritten);
414
415   if ( pstm == 0 )
416     return STG_E_INVALIDPOINTER;
417
418   totalBytesRead.QuadPart = 0;
419   totalBytesWritten.QuadPart = 0;
420
421   while ( cb.QuadPart > 0 )
422   {
423     if ( cb.QuadPart >= sizeof(tmpBuffer) )
424       copySize = sizeof(tmpBuffer);
425     else
426       copySize = cb.u.LowPart;
427
428     hr = IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
429     if (FAILED(hr))
430         break;
431
432     totalBytesRead.QuadPart += bytesRead;
433
434     if (bytesRead)
435     {
436         hr = IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
437         if (FAILED(hr))
438             break;
439
440         totalBytesWritten.QuadPart += bytesWritten;
441     }
442
443     if (bytesRead!=copySize)
444       cb.QuadPart = 0;
445     else
446       cb.QuadPart -= bytesRead;
447   }
448
449   if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
450   if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
451
452   return hr;
453 }
454
455 /***
456  * This method is part of the IStream interface.
457  *
458  * For streams supported by HGLOBALS, this function does nothing.
459  * This is what the documentation tells us.
460  *
461  * See the documentation of IStream for more info.
462  */
463 static HRESULT WINAPI HGLOBALStreamImpl_Commit(
464                   IStream*      iface,
465                   DWORD         grfCommitFlags)  /* [in] */
466 {
467   return S_OK;
468 }
469
470 /***
471  * This method is part of the IStream interface.
472  *
473  * For streams supported by HGLOBALS, this function does nothing.
474  * This is what the documentation tells us.
475  *
476  * See the documentation of IStream for more info.
477  */
478 static HRESULT WINAPI HGLOBALStreamImpl_Revert(
479                   IStream* iface)
480 {
481   return S_OK;
482 }
483
484 /***
485  * This method is part of the IStream interface.
486  *
487  * For streams supported by HGLOBALS, this function does nothing.
488  * This is what the documentation tells us.
489  *
490  * See the documentation of IStream for more info.
491  */
492 static HRESULT WINAPI HGLOBALStreamImpl_LockRegion(
493                   IStream*       iface,
494                   ULARGE_INTEGER libOffset,   /* [in] */
495                   ULARGE_INTEGER cb,          /* [in] */
496                   DWORD          dwLockType)  /* [in] */
497 {
498   return STG_E_INVALIDFUNCTION;
499 }
500
501 /*
502  * This method is part of the IStream interface.
503  *
504  * For streams supported by HGLOBALS, this function does nothing.
505  * This is what the documentation tells us.
506  *
507  * See the documentation of IStream for more info.
508  */
509 static HRESULT WINAPI HGLOBALStreamImpl_UnlockRegion(
510                   IStream*       iface,
511                   ULARGE_INTEGER libOffset,   /* [in] */
512                   ULARGE_INTEGER cb,          /* [in] */
513                   DWORD          dwLockType)  /* [in] */
514 {
515   return S_OK;
516 }
517
518 /***
519  * This method is part of the IStream interface.
520  *
521  * This method returns information about the current
522  * stream.
523  *
524  * See the documentation of IStream for more info.
525  */
526 static HRESULT WINAPI HGLOBALStreamImpl_Stat(
527                   IStream*     iface,
528                   STATSTG*     pstatstg,     /* [out] */
529                   DWORD        grfStatFlag)  /* [in] */
530 {
531   HGLOBALStreamImpl* This = impl_from_IStream(iface);
532
533   memset(pstatstg, 0, sizeof(STATSTG));
534
535   pstatstg->pwcsName = NULL;
536   pstatstg->type     = STGTY_STREAM;
537   pstatstg->cbSize   = This->streamSize;
538
539   return S_OK;
540 }
541
542 static HRESULT WINAPI HGLOBALStreamImpl_Clone(
543                   IStream*     iface,
544                   IStream**    ppstm) /* [out] */
545 {
546   HGLOBALStreamImpl* This = impl_from_IStream(iface);
547   ULARGE_INTEGER dummy;
548   LARGE_INTEGER offset;
549   HRESULT hr;
550
551   TRACE(" Cloning %p (deleteOnRelease=%d seek position=%ld)\n",iface,This->deleteOnRelease,(long)This->currentPosition.QuadPart);
552   hr = CreateStreamOnHGlobal(This->supportHandle, FALSE, ppstm);
553   if(FAILED(hr))
554     return hr;
555   offset.QuadPart = (LONGLONG)This->currentPosition.QuadPart;
556   IStream_Seek(*ppstm, offset, STREAM_SEEK_SET, &dummy);
557   return S_OK;
558 }
559
560 static const IStreamVtbl HGLOBALStreamImplVtbl =
561 {
562     HGLOBALStreamImpl_QueryInterface,
563     HGLOBALStreamImpl_AddRef,
564     HGLOBALStreamImpl_Release,
565     HGLOBALStreamImpl_Read,
566     HGLOBALStreamImpl_Write,
567     HGLOBALStreamImpl_Seek,
568     HGLOBALStreamImpl_SetSize,
569     HGLOBALStreamImpl_CopyTo,
570     HGLOBALStreamImpl_Commit,
571     HGLOBALStreamImpl_Revert,
572     HGLOBALStreamImpl_LockRegion,
573     HGLOBALStreamImpl_UnlockRegion,
574     HGLOBALStreamImpl_Stat,
575     HGLOBALStreamImpl_Clone
576 };
577
578 /***********************************************************************
579  *           CreateStreamOnHGlobal     [OLE32.@]
580  */
581 HRESULT WINAPI CreateStreamOnHGlobal(
582                 HGLOBAL   hGlobal,
583                 BOOL      fDeleteOnRelease,
584                 LPSTREAM* ppstm)
585 {
586   HGLOBALStreamImpl* This;
587
588   if (!ppstm)
589     return E_INVALIDARG;
590
591   This = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl));
592   if (!This) return E_OUTOFMEMORY;
593
594   This->IStream_iface.lpVtbl = &HGLOBALStreamImplVtbl;
595   This->ref = 1;
596
597   /* initialize the support */
598   This->supportHandle = hGlobal;
599   This->deleteOnRelease = fDeleteOnRelease;
600
601   /* allocate a handle if one is not supplied */
602   if (!This->supportHandle)
603     This->supportHandle = GlobalAlloc(GMEM_MOVEABLE|GMEM_NODISCARD|GMEM_SHARE, 0);
604
605   /* start at the beginning */
606   This->currentPosition.u.HighPart = 0;
607   This->currentPosition.u.LowPart = 0;
608
609   /* initialize the size of the stream to the size of the handle */
610   This->streamSize.u.HighPart = 0;
611   This->streamSize.u.LowPart = GlobalSize(This->supportHandle);
612
613   *ppstm = &This->IStream_iface;
614
615   return S_OK;
616 }
617
618 /***********************************************************************
619  *           GetHGlobalFromStream     [OLE32.@]
620  */
621 HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)
622 {
623   HGLOBALStreamImpl* pStream;
624
625   if (pstm == NULL)
626     return E_INVALIDARG;
627
628   pStream = (HGLOBALStreamImpl*) pstm;
629
630   /*
631    * Verify that the stream object was created with CreateStreamOnHGlobal.
632    */
633   if (pStream->IStream_iface.lpVtbl == &HGLOBALStreamImplVtbl)
634     *phglobal = pStream->supportHandle;
635   else
636   {
637     *phglobal = 0;
638     return E_INVALIDARG;
639   }
640
641   return S_OK;
642 }