Implemented body of HGLOBALStreamImpl::Clone.
[wine] / dlls / ole32 / memlockbytes.c
1 /******************************************************************************
2  *
3  * Global memory implementation of ILockBytes.
4  *
5  * Copyright 1999 Thuy Nguyen
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23
24 #include <string.h>
25
26 #include "windef.h"
27 #include "objbase.h"
28 #include "ole2.h"
29 #include "winbase.h"
30 #include "winerror.h"
31
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(ole);
35
36 /******************************************************************************
37  * HGLOBALLockBytesImpl definition.
38  *
39  * This class imlements the ILockBytes inteface and represents a byte array
40  * object supported by an HGLOBAL pointer.
41  */
42 struct HGLOBALLockBytesImpl
43 {
44   /*
45    * Needs to be the first item in the stuct
46    * since we want to cast this in an ILockBytes pointer
47    */
48   ICOM_VFIELD(ILockBytes);
49
50   /*
51    * Reference count
52    */
53   ULONG        ref;
54
55   /*
56    * Support for the LockBytes object
57    */
58   HGLOBAL supportHandle;
59
60   /*
61    * This flag is TRUE if the HGLOBAL is destroyed when the object
62    * is finally released.
63    */
64   BOOL    deleteOnRelease;
65
66   /*
67    * Helper variable that contains the size of the byte array
68    */
69   ULARGE_INTEGER     byteArraySize;
70 };
71
72 typedef struct HGLOBALLockBytesImpl HGLOBALLockBytesImpl;
73
74 /*
75  * Method definition for the HGLOBALLockBytesImpl class.
76  */
77 HGLOBALLockBytesImpl* HGLOBALLockBytesImpl_Construct(
78     HGLOBAL  hGlobal,
79     BOOL     fDeleteOnRelease);
80
81 void HGLOBALLockBytesImpl_Destroy(HGLOBALLockBytesImpl* This);
82
83 HRESULT WINAPI HGLOBALLockBytesImpl_QueryInterface(
84     ILockBytes*   iface,
85     REFIID        riid,        /* [in] */
86     void**        ppvObject);  /* [iid_is][out] */
87
88 ULONG WINAPI HGLOBALLockBytesImpl_AddRef(
89     ILockBytes*   iface);
90
91 ULONG WINAPI HGLOBALLockBytesImpl_Release(
92     ILockBytes*   iface);
93
94 HRESULT WINAPI HGLOBALLockBytesImpl_ReadAt(
95     ILockBytes*    iface,
96     ULARGE_INTEGER ulOffset,  /* [in] */
97     void*          pv,        /* [length_is][size_is][out] */
98     ULONG          cb,        /* [in] */
99     ULONG*         pcbRead);  /* [out] */
100
101 HRESULT WINAPI HGLOBALLockBytesImpl_WriteAt(
102     ILockBytes*    iface,
103     ULARGE_INTEGER ulOffset,    /* [in] */
104     const void*    pv,          /* [size_is][in] */
105     ULONG          cb,          /* [in] */
106     ULONG*         pcbWritten); /* [out] */
107
108 HRESULT WINAPI HGLOBALLockBytesImpl_Flush(
109     ILockBytes*     iface);
110
111 HRESULT WINAPI HGLOBALLockBytesImpl_SetSize(
112     ILockBytes*     iface,
113     ULARGE_INTEGER  libNewSize);  /* [in] */
114
115 HRESULT WINAPI HGLOBALLockBytesImpl_LockRegion(
116     ILockBytes*    iface,
117     ULARGE_INTEGER libOffset,   /* [in] */
118     ULARGE_INTEGER cb,          /* [in] */
119     DWORD          dwLockType); /* [in] */
120
121 HRESULT WINAPI HGLOBALLockBytesImpl_UnlockRegion(
122     ILockBytes*    iface,
123     ULARGE_INTEGER libOffset,   /* [in] */
124     ULARGE_INTEGER cb,          /* [in] */
125     DWORD          dwLockType); /* [in] */
126
127 HRESULT WINAPI HGLOBALLockBytesImpl_Stat(
128     ILockBytes*    iface,
129     STATSTG*       pstatstg,     /* [out] */
130     DWORD          grfStatFlag); /* [in]  */
131
132 /*
133  * Virtual function table for the HGLOBALLockBytesImpl class.
134  */
135 static ICOM_VTABLE(ILockBytes) HGLOBALLockBytesImpl_Vtbl =
136 {
137     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
138     HGLOBALLockBytesImpl_QueryInterface,
139     HGLOBALLockBytesImpl_AddRef,
140     HGLOBALLockBytesImpl_Release,
141     HGLOBALLockBytesImpl_ReadAt,
142     HGLOBALLockBytesImpl_WriteAt,
143     HGLOBALLockBytesImpl_Flush,
144     HGLOBALLockBytesImpl_SetSize,
145     HGLOBALLockBytesImpl_LockRegion,
146     HGLOBALLockBytesImpl_UnlockRegion,
147     HGLOBALLockBytesImpl_Stat,
148 };
149
150 /******************************************************************************
151  *           CreateILockBytesOnHGlobal     [OLE32.57]
152  */
153 HRESULT WINAPI CreateILockBytesOnHGlobal(HGLOBAL      hGlobal,
154                                          BOOL         fDeleteOnRelease,
155                                          LPLOCKBYTES* ppLkbyt)
156 {
157   HGLOBALLockBytesImpl* newLockBytes;
158
159   newLockBytes = HGLOBALLockBytesImpl_Construct(hGlobal, fDeleteOnRelease);
160
161   if (newLockBytes != NULL)
162   {
163     return IUnknown_QueryInterface((IUnknown*)newLockBytes,
164                                    &IID_ILockBytes,
165                                    (void**)ppLkbyt);
166   }
167
168   return E_OUTOFMEMORY;
169 }
170
171 /******************************************************************************
172  *           GetHGlobalFromILockBytes     [OLE32.70]
173  */
174 HRESULT WINAPI GetHGlobalFromILockBytes(ILockBytes* plkbyt, HGLOBAL* phglobal)
175 {
176   HGLOBALLockBytesImpl* const pMemLockBytes = (HGLOBALLockBytesImpl*)plkbyt;
177   STATSTG stbuf;
178   HRESULT hres;
179   ULARGE_INTEGER start;
180   ULONG xread;
181
182   *phglobal = 0;
183   if (ICOM_VTBL(pMemLockBytes) == &HGLOBALLockBytesImpl_Vtbl) {
184     *phglobal = pMemLockBytes->supportHandle;
185     if (*phglobal == 0)
186       return E_INVALIDARG;
187     return S_OK;
188   }
189   /* It is not our lockbytes implementation, so use a more generic way */
190   hres = ILockBytes_Stat(plkbyt,&stbuf,0);
191   if (hres != S_OK) {
192      ERR("Cannot ILockBytes_Stat, %lx\n",hres);
193      return hres;
194   }
195   FIXME("cbSize is %ld\n",stbuf.cbSize.s.LowPart);
196   *phglobal = GlobalAlloc( GMEM_MOVEABLE|GMEM_SHARE, stbuf.cbSize.s.LowPart);
197   if (!*phglobal)
198     return E_INVALIDARG;
199   memset(&start,0,sizeof(start));
200   hres = ILockBytes_ReadAt(plkbyt, start, GlobalLock(*phglobal), stbuf.cbSize.s.LowPart, &xread);
201   GlobalUnlock(*phglobal);
202   if (hres != S_OK) {
203     FIXME("%p->ReadAt failed with %lx\n",plkbyt,hres);
204     return hres;
205   }
206   if (stbuf.cbSize.s.LowPart != xread) {
207     FIXME("Read size is not requested size %ld vs %ld?\n",stbuf.cbSize.s.LowPart, xread);
208   }
209   return S_OK;
210 }
211
212 /******************************************************************************
213  *
214  * HGLOBALLockBytesImpl implementation
215  *
216  */
217
218 /******************************************************************************
219  * This is the constructor for the HGLOBALLockBytesImpl class.
220  *
221  * Params:
222  *    hGlobal          - Handle that will support the stream. can be NULL.
223  *    fDeleteOnRelease - Flag set to TRUE if the HGLOBAL will be released
224  *                       when the IStream object is destroyed.
225  */
226 HGLOBALLockBytesImpl* HGLOBALLockBytesImpl_Construct(HGLOBAL hGlobal,
227                                                      BOOL    fDeleteOnRelease)
228 {
229   HGLOBALLockBytesImpl* newLockBytes;
230   newLockBytes = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALLockBytesImpl));
231
232   if (newLockBytes!=0)
233   {
234     /*
235      * Set up the virtual function table and reference count.
236      */
237     ICOM_VTBL(newLockBytes) = &HGLOBALLockBytesImpl_Vtbl;
238     newLockBytes->ref    = 0;
239
240     /*
241      * Initialize the support.
242      */
243     newLockBytes->supportHandle = hGlobal;
244     newLockBytes->deleteOnRelease = fDeleteOnRelease;
245
246     /*
247      * This method will allocate a handle if one is not supplied.
248      */
249     if (newLockBytes->supportHandle == 0)
250     {
251       newLockBytes->supportHandle = GlobalAlloc(GMEM_MOVEABLE |
252                                                 GMEM_NODISCARD,
253                                                 0);
254     }
255
256     /*
257      * Initialize the size of the array to the size of the handle.
258      */
259     newLockBytes->byteArraySize.s.HighPart = 0;
260     newLockBytes->byteArraySize.s.LowPart  = GlobalSize(
261                                               newLockBytes->supportHandle);
262   }
263
264   return newLockBytes;
265 }
266
267 /******************************************************************************
268  * This is the destructor of the HGLOBALStreamImpl class.
269  *
270  * This method will clean-up all the resources used-up by the given
271  * HGLOBALLockBytesImpl class. The pointer passed-in to this function will be
272  * freed and will not be valid anymore.
273  */
274 void HGLOBALLockBytesImpl_Destroy(HGLOBALLockBytesImpl* This)
275 {
276   /*
277    * Release the HGlobal if the constructor asked for that.
278    */
279   if (This->deleteOnRelease)
280   {
281     GlobalFree(This->supportHandle);
282     This->supportHandle = 0;
283   }
284
285   /*
286    * Finally, free the memory used-up by the class.
287    */
288   HeapFree(GetProcessHeap(), 0, This);
289 }
290
291 /******************************************************************************
292  * This implements the IUnknown method QueryInterface for this
293  * class
294  */
295 HRESULT WINAPI HGLOBALLockBytesImpl_QueryInterface(
296       ILockBytes*  iface,
297       REFIID       riid,        /* [in] */
298       void**       ppvObject)   /* [iid_is][out] */
299 {
300   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
301
302   /*
303    * Perform a sanity check on the parameters.
304    */
305   if (ppvObject==0)
306     return E_INVALIDARG;
307
308   /*
309    * Initialize the return parameter.
310    */
311   *ppvObject = 0;
312
313   /*
314    * Compare the riid with the interface IDs implemented by this object.
315    */
316   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
317   {
318     *ppvObject = (ILockBytes*)This;
319   }
320   else if (memcmp(&IID_ILockBytes, riid, sizeof(IID_ILockBytes)) == 0)
321   {
322     *ppvObject = (ILockBytes*)This;
323   }
324
325   /*
326    * Check that we obtained an interface.
327    */
328   if ((*ppvObject)==0)
329     return E_NOINTERFACE;
330
331   /*
332    * Query Interface always increases the reference count by one when it is
333    * successful
334    */
335   HGLOBALLockBytesImpl_AddRef(iface);
336
337   return S_OK;
338 }
339
340 /******************************************************************************
341  * This implements the IUnknown method AddRef for this
342  * class
343  */
344 ULONG WINAPI HGLOBALLockBytesImpl_AddRef(ILockBytes* iface)
345 {
346   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
347
348   This->ref++;
349
350   return This->ref;
351 }
352
353 /******************************************************************************
354  * This implements the IUnknown method Release for this
355  * class
356  */
357 ULONG WINAPI HGLOBALLockBytesImpl_Release(ILockBytes* iface)
358 {
359   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
360
361   ULONG newRef;
362
363   This->ref--;
364
365   newRef = This->ref;
366
367   /*
368    * If the reference count goes down to 0, perform suicide.
369    */
370   if (newRef==0)
371   {
372     HGLOBALLockBytesImpl_Destroy(This);
373   }
374
375   return newRef;
376 }
377
378 /******************************************************************************
379  * This method is part of the ILockBytes interface.
380  *
381  * It reads a block of information from the byte array at the specified
382  * offset.
383  *
384  * See the documentation of ILockBytes for more info.
385  */
386 HRESULT WINAPI HGLOBALLockBytesImpl_ReadAt(
387       ILockBytes*    iface,
388       ULARGE_INTEGER ulOffset,  /* [in] */
389       void*          pv,        /* [length_is][size_is][out] */
390       ULONG          cb,        /* [in] */
391       ULONG*         pcbRead)   /* [out] */
392 {
393   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
394
395   void* supportBuffer;
396   ULONG bytesReadBuffer = 0;
397   ULONG bytesToReadFromBuffer;
398
399   /*
400    * If the caller is not interested in the number of bytes read,
401    * we use another buffer to avoid "if" statements in the code.
402    */
403   if (pcbRead == 0)
404     pcbRead = &bytesReadBuffer;
405
406   /*
407    * Make sure the offset is valid.
408    */
409   if (ulOffset.s.LowPart > This->byteArraySize.s.LowPart)
410     return E_FAIL;
411
412   /*
413    * Using the known size of the array, calculate the number of bytes
414    * to read.
415    */
416   bytesToReadFromBuffer = min(This->byteArraySize.s.LowPart -
417                               ulOffset.s.LowPart, cb);
418
419   /*
420    * Lock the buffer in position and copy the data.
421    */
422   supportBuffer = GlobalLock(This->supportHandle);
423
424   memcpy(pv,
425          (char *) supportBuffer + ulOffset.s.LowPart,
426          bytesToReadFromBuffer);
427
428   /*
429    * Return the number of bytes read.
430    */
431   *pcbRead = bytesToReadFromBuffer;
432
433   /*
434    * Cleanup
435    */
436   GlobalUnlock(This->supportHandle);
437
438   /*
439    * The function returns S_OK if the specified number of bytes were read
440    * or the end of the array was reached.
441    * It returns STG_E_READFAULT if the number of bytes to read does not equal
442    * the number of bytes actually read.
443    */
444   if(*pcbRead == cb)
445     return S_OK;
446
447   return STG_E_READFAULT;
448 }
449
450 /******************************************************************************
451  * This method is part of the ILockBytes interface.
452  *
453  * It writes the specified bytes at the specified offset.
454  * position. If the array is too small, it will be resized.
455  *
456  * See the documentation of ILockBytes for more info.
457  */
458 HRESULT WINAPI HGLOBALLockBytesImpl_WriteAt(
459       ILockBytes*    iface,
460       ULARGE_INTEGER ulOffset,    /* [in] */
461       const void*    pv,          /* [size_is][in] */
462       ULONG          cb,          /* [in] */
463       ULONG*         pcbWritten)  /* [out] */
464 {
465   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
466
467   void*          supportBuffer;
468   ULARGE_INTEGER newSize;
469   ULONG          bytesWritten = 0;
470
471   /*
472    * If the caller is not interested in the number of bytes written,
473    * we use another buffer to avoid "if" statements in the code.
474    */
475   if (pcbWritten == 0)
476     pcbWritten = &bytesWritten;
477
478   if (cb == 0)
479   {
480     return S_OK;
481   }
482   else
483   {
484     newSize.s.HighPart = 0;
485     newSize.s.LowPart = ulOffset.s.LowPart + cb;
486   }
487
488   /*
489    * Verify if we need to grow the stream
490    */
491   if (newSize.s.LowPart > This->byteArraySize.s.LowPart)
492   {
493     /* grow stream */
494     if (HGLOBALLockBytesImpl_SetSize(iface, newSize) == STG_E_MEDIUMFULL)
495       return STG_E_MEDIUMFULL;
496   }
497
498   /*
499    * Lock the buffer in position and copy the data.
500    */
501   supportBuffer = GlobalLock(This->supportHandle);
502
503   memcpy((char *) supportBuffer + ulOffset.s.LowPart, pv, cb);
504
505   /*
506    * Return the number of bytes written.
507    */
508   *pcbWritten = cb;
509
510   /*
511    * Cleanup
512    */
513   GlobalUnlock(This->supportHandle);
514
515   return S_OK;
516 }
517
518 /******************************************************************************
519  * This method is part of the ILockBytes interface.
520  *
521  * See the documentation of ILockBytes for more info.
522  */
523 HRESULT WINAPI HGLOBALLockBytesImpl_Flush(ILockBytes* iface)
524 {
525   return S_OK;
526 }
527
528 /******************************************************************************
529  * This method is part of the ILockBytes interface.
530  *
531  * It will change the size of the byte array.
532  *
533  * See the documentation of ILockBytes for more info.
534  */
535 HRESULT WINAPI HGLOBALLockBytesImpl_SetSize(
536       ILockBytes*     iface,
537       ULARGE_INTEGER  libNewSize)   /* [in] */
538 {
539   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
540
541   /*
542    * As documented.
543    */
544   if (libNewSize.s.HighPart != 0)
545     return STG_E_INVALIDFUNCTION;
546
547   if (This->byteArraySize.s.LowPart == libNewSize.s.LowPart)
548     return S_OK;
549
550   /*
551    * Re allocate the HGlobal to fit the new size of the stream.
552    */
553   This->supportHandle = GlobalReAlloc(This->supportHandle,
554                                       libNewSize.s.LowPart,
555                                       0);
556
557   if (This->supportHandle == 0)
558     return STG_E_MEDIUMFULL;
559
560   This->byteArraySize.s.LowPart = libNewSize.s.LowPart;
561
562   return S_OK;
563 }
564
565 /******************************************************************************
566  * This method is part of the ILockBytes interface.
567  *
568  * The global memory implementation of ILockBytes does not support locking.
569  *
570  * See the documentation of ILockBytes for more info.
571  */
572 HRESULT WINAPI HGLOBALLockBytesImpl_LockRegion(
573       ILockBytes*    iface,
574       ULARGE_INTEGER libOffset,   /* [in] */
575       ULARGE_INTEGER cb,          /* [in] */
576       DWORD          dwLockType)  /* [in] */
577 {
578   return STG_E_INVALIDFUNCTION;
579 }
580
581 /******************************************************************************
582  * This method is part of the ILockBytes interface.
583  *
584  * The global memory implementation of ILockBytes does not support locking.
585  *
586  * See the documentation of ILockBytes for more info.
587  */
588 HRESULT WINAPI HGLOBALLockBytesImpl_UnlockRegion(
589       ILockBytes*    iface,
590       ULARGE_INTEGER libOffset,   /* [in] */
591       ULARGE_INTEGER cb,          /* [in] */
592       DWORD          dwLockType)  /* [in] */
593 {
594   return STG_E_INVALIDFUNCTION;
595 }
596
597 /******************************************************************************
598  * This method is part of the ILockBytes interface.
599  *
600  * This method returns information about the current
601  * byte array object.
602  *
603  * See the documentation of ILockBytes for more info.
604  */
605 HRESULT WINAPI HGLOBALLockBytesImpl_Stat(
606       ILockBytes*  iface,
607       STATSTG*     pstatstg,     /* [out] */
608       DWORD        grfStatFlag)  /* [in] */
609 {
610   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
611
612   memset(pstatstg, 0, sizeof(STATSTG));
613
614   pstatstg->pwcsName = NULL;
615   pstatstg->type     = STGTY_LOCKBYTES;
616   pstatstg->cbSize   = This->byteArraySize;
617
618   return S_OK;
619 }