Authors: Rob Shearman <rob@codeweavers.com>, Mike Hearn <mh@codeweavers.com>
[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 <assert.h>
25 #include <stdarg.h>
26 #include <string.h>
27
28 #define COBJMACROS
29 #define NONAMELESSUNION
30 #define NONAMELESSSTRUCT
31
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winuser.h"
35 #include "objbase.h"
36 #include "ole2.h"
37 #include "winerror.h"
38
39 #include "wine/debug.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(ole);
42
43 /******************************************************************************
44  * HGLOBALLockBytesImpl definition.
45  *
46  * This class imlements the ILockBytes inteface and represents a byte array
47  * object supported by an HGLOBAL pointer.
48  */
49 struct HGLOBALLockBytesImpl
50 {
51   /*
52    * Needs to be the first item in the stuct
53    * since we want to cast this in an ILockBytes pointer
54    */
55   ILockBytesVtbl *lpVtbl;
56
57   /*
58    * Reference count
59    */
60   ULONG        ref;
61
62   /*
63    * Support for the LockBytes object
64    */
65   HGLOBAL supportHandle;
66
67   /*
68    * This flag is TRUE if the HGLOBAL is destroyed when the object
69    * is finally released.
70    */
71   BOOL    deleteOnRelease;
72
73   /*
74    * Helper variable that contains the size of the byte array
75    */
76   ULARGE_INTEGER     byteArraySize;
77 };
78
79 typedef struct HGLOBALLockBytesImpl HGLOBALLockBytesImpl;
80
81 /*
82  * Method definition for the HGLOBALLockBytesImpl class.
83  */
84 HGLOBALLockBytesImpl* HGLOBALLockBytesImpl_Construct(
85     HGLOBAL  hGlobal,
86     BOOL     fDeleteOnRelease);
87
88 void HGLOBALLockBytesImpl_Destroy(HGLOBALLockBytesImpl* This);
89
90 HRESULT WINAPI HGLOBALLockBytesImpl_QueryInterface(
91     ILockBytes*   iface,
92     REFIID        riid,        /* [in] */
93     void**        ppvObject);  /* [iid_is][out] */
94
95 ULONG WINAPI HGLOBALLockBytesImpl_AddRef(
96     ILockBytes*   iface);
97
98 ULONG WINAPI HGLOBALLockBytesImpl_Release(
99     ILockBytes*   iface);
100
101 HRESULT WINAPI HGLOBALLockBytesImpl_ReadAt(
102     ILockBytes*    iface,
103     ULARGE_INTEGER ulOffset,  /* [in] */
104     void*          pv,        /* [length_is][size_is][out] */
105     ULONG          cb,        /* [in] */
106     ULONG*         pcbRead);  /* [out] */
107
108 HRESULT WINAPI HGLOBALLockBytesImpl_WriteAt(
109     ILockBytes*    iface,
110     ULARGE_INTEGER ulOffset,    /* [in] */
111     const void*    pv,          /* [size_is][in] */
112     ULONG          cb,          /* [in] */
113     ULONG*         pcbWritten); /* [out] */
114
115 HRESULT WINAPI HGLOBALLockBytesImpl_Flush(
116     ILockBytes*     iface);
117
118 HRESULT WINAPI HGLOBALLockBytesImpl_SetSize(
119     ILockBytes*     iface,
120     ULARGE_INTEGER  libNewSize);  /* [in] */
121
122 HRESULT WINAPI HGLOBALLockBytesImpl_LockRegion(
123     ILockBytes*    iface,
124     ULARGE_INTEGER libOffset,   /* [in] */
125     ULARGE_INTEGER cb,          /* [in] */
126     DWORD          dwLockType); /* [in] */
127
128 HRESULT WINAPI HGLOBALLockBytesImpl_UnlockRegion(
129     ILockBytes*    iface,
130     ULARGE_INTEGER libOffset,   /* [in] */
131     ULARGE_INTEGER cb,          /* [in] */
132     DWORD          dwLockType); /* [in] */
133
134 HRESULT WINAPI HGLOBALLockBytesImpl_Stat(
135     ILockBytes*    iface,
136     STATSTG*       pstatstg,     /* [out] */
137     DWORD          grfStatFlag); /* [in]  */
138
139 /*
140  * Virtual function table for the HGLOBALLockBytesImpl class.
141  */
142 static ILockBytesVtbl HGLOBALLockBytesImpl_Vtbl =
143 {
144     HGLOBALLockBytesImpl_QueryInterface,
145     HGLOBALLockBytesImpl_AddRef,
146     HGLOBALLockBytesImpl_Release,
147     HGLOBALLockBytesImpl_ReadAt,
148     HGLOBALLockBytesImpl_WriteAt,
149     HGLOBALLockBytesImpl_Flush,
150     HGLOBALLockBytesImpl_SetSize,
151     HGLOBALLockBytesImpl_LockRegion,
152     HGLOBALLockBytesImpl_UnlockRegion,
153     HGLOBALLockBytesImpl_Stat,
154 };
155
156 /******************************************************************************
157  *           CreateILockBytesOnHGlobal     [OLE32.@]
158  */
159 HRESULT WINAPI CreateILockBytesOnHGlobal(HGLOBAL      hGlobal,
160                                          BOOL         fDeleteOnRelease,
161                                          LPLOCKBYTES* ppLkbyt)
162 {
163   HGLOBALLockBytesImpl* newLockBytes;
164
165   newLockBytes = HGLOBALLockBytesImpl_Construct(hGlobal, fDeleteOnRelease);
166
167   if (newLockBytes != NULL)
168   {
169     return IUnknown_QueryInterface((IUnknown*)newLockBytes,
170                                    &IID_ILockBytes,
171                                    (void**)ppLkbyt);
172   }
173
174   return E_OUTOFMEMORY;
175 }
176
177 /******************************************************************************
178  *           GetHGlobalFromILockBytes     [OLE32.@]
179  */
180 HRESULT WINAPI GetHGlobalFromILockBytes(ILockBytes* plkbyt, HGLOBAL* phglobal)
181 {
182   HGLOBALLockBytesImpl* const pMemLockBytes = (HGLOBALLockBytesImpl*)plkbyt;
183   STATSTG stbuf;
184   HRESULT hres;
185   ULARGE_INTEGER start;
186   ULONG xread;
187
188   *phglobal = 0;
189   if (pMemLockBytes->lpVtbl == &HGLOBALLockBytesImpl_Vtbl) {
190     *phglobal = pMemLockBytes->supportHandle;
191     if (*phglobal == 0)
192       return E_INVALIDARG;
193     return S_OK;
194   }
195   /* It is not our lockbytes implementation, so use a more generic way */
196   hres = ILockBytes_Stat(plkbyt,&stbuf,0);
197   if (hres != S_OK) {
198      ERR("Cannot ILockBytes_Stat, %lx\n",hres);
199      return hres;
200   }
201   FIXME("cbSize is %ld\n",stbuf.cbSize.u.LowPart);
202   *phglobal = GlobalAlloc( GMEM_MOVEABLE|GMEM_SHARE, stbuf.cbSize.u.LowPart);
203   if (!*phglobal)
204     return E_INVALIDARG;
205   memset(&start,0,sizeof(start));
206   hres = ILockBytes_ReadAt(plkbyt, start, GlobalLock(*phglobal), stbuf.cbSize.u.LowPart, &xread);
207   GlobalUnlock(*phglobal);
208   if (hres != S_OK) {
209     FIXME("%p->ReadAt failed with %lx\n",plkbyt,hres);
210     return hres;
211   }
212   if (stbuf.cbSize.u.LowPart != xread) {
213     FIXME("Read size is not requested size %ld vs %ld?\n",stbuf.cbSize.u.LowPart, xread);
214   }
215   return S_OK;
216 }
217
218 /******************************************************************************
219  *
220  * HGLOBALLockBytesImpl implementation
221  *
222  */
223
224 /******************************************************************************
225  * This is the constructor for the HGLOBALLockBytesImpl class.
226  *
227  * Params:
228  *    hGlobal          - Handle that will support the stream. can be NULL.
229  *    fDeleteOnRelease - Flag set to TRUE if the HGLOBAL will be released
230  *                       when the IStream object is destroyed.
231  */
232 HGLOBALLockBytesImpl* HGLOBALLockBytesImpl_Construct(HGLOBAL hGlobal,
233                                                      BOOL    fDeleteOnRelease)
234 {
235   HGLOBALLockBytesImpl* newLockBytes;
236   newLockBytes = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALLockBytesImpl));
237
238   if (newLockBytes!=0)
239   {
240     /*
241      * Set up the virtual function table and reference count.
242      */
243     newLockBytes->lpVtbl = &HGLOBALLockBytesImpl_Vtbl;
244     newLockBytes->ref    = 0;
245
246     /*
247      * Initialize the support.
248      */
249     newLockBytes->supportHandle = hGlobal;
250     newLockBytes->deleteOnRelease = fDeleteOnRelease;
251
252     /*
253      * This method will allocate a handle if one is not supplied.
254      */
255     if (newLockBytes->supportHandle == 0)
256     {
257       newLockBytes->supportHandle = GlobalAlloc(GMEM_MOVEABLE |
258                                                 GMEM_NODISCARD,
259                                                 0);
260     }
261
262     /*
263      * Initialize the size of the array to the size of the handle.
264      */
265     newLockBytes->byteArraySize.u.HighPart = 0;
266     newLockBytes->byteArraySize.u.LowPart  = GlobalSize(
267                                               newLockBytes->supportHandle);
268   }
269
270   return newLockBytes;
271 }
272
273 /******************************************************************************
274  * This is the destructor of the HGLOBALStreamImpl class.
275  *
276  * This method will clean-up all the resources used-up by the given
277  * HGLOBALLockBytesImpl class. The pointer passed-in to this function will be
278  * freed and will not be valid anymore.
279  */
280 void HGLOBALLockBytesImpl_Destroy(HGLOBALLockBytesImpl* This)
281 {
282   /*
283    * Release the HGlobal if the constructor asked for that.
284    */
285   if (This->deleteOnRelease)
286   {
287     GlobalFree(This->supportHandle);
288     This->supportHandle = 0;
289   }
290
291   /*
292    * Finally, free the memory used-up by the class.
293    */
294   HeapFree(GetProcessHeap(), 0, This);
295 }
296
297 /******************************************************************************
298  * This implements the IUnknown method QueryInterface for this
299  * class
300  */
301 HRESULT WINAPI HGLOBALLockBytesImpl_QueryInterface(
302       ILockBytes*  iface,
303       REFIID       riid,        /* [in] */
304       void**       ppvObject)   /* [iid_is][out] */
305 {
306   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
307
308   /*
309    * Perform a sanity check on the parameters.
310    */
311   if (ppvObject==0)
312     return E_INVALIDARG;
313
314   /*
315    * Initialize the return parameter.
316    */
317   *ppvObject = 0;
318
319   /*
320    * Compare the riid with the interface IDs implemented by this object.
321    */
322   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
323   {
324     *ppvObject = (ILockBytes*)This;
325   }
326   else if (memcmp(&IID_ILockBytes, riid, sizeof(IID_ILockBytes)) == 0)
327   {
328     *ppvObject = (ILockBytes*)This;
329   }
330
331   /*
332    * Check that we obtained an interface.
333    */
334   if ((*ppvObject)==0)
335     return E_NOINTERFACE;
336
337   /*
338    * Query Interface always increases the reference count by one when it is
339    * successful
340    */
341   HGLOBALLockBytesImpl_AddRef(iface);
342
343   return S_OK;
344 }
345
346 /******************************************************************************
347  * This implements the IUnknown method AddRef for this
348  * class
349  */
350 ULONG WINAPI HGLOBALLockBytesImpl_AddRef(ILockBytes* iface)
351 {
352   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
353   return InterlockedIncrement(&This->ref);
354 }
355
356 /******************************************************************************
357  * This implements the IUnknown method Release for this
358  * class
359  */
360 ULONG WINAPI HGLOBALLockBytesImpl_Release(ILockBytes* iface)
361 {
362   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
363   ULONG ref;
364
365   ref = InterlockedDecrement(&This->ref);
366
367   /*
368    * If the reference count goes down to 0, perform suicide.
369    */
370   if (ref==0)
371   {
372     HGLOBALLockBytesImpl_Destroy(This);
373   }
374
375   return ref;
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.u.LowPart > This->byteArraySize.u.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.u.LowPart -
417                               ulOffset.u.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.u.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.u.HighPart = 0;
485     newSize.u.LowPart = ulOffset.u.LowPart + cb;
486   }
487
488   /*
489    * Verify if we need to grow the stream
490    */
491   if (newSize.u.LowPart > This->byteArraySize.u.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.u.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   HGLOBAL supportHandle;
541
542   /*
543    * As documented.
544    */
545   if (libNewSize.u.HighPart != 0)
546     return STG_E_INVALIDFUNCTION;
547
548   if (This->byteArraySize.u.LowPart == libNewSize.u.LowPart)
549     return S_OK;
550
551   /*
552    * Re allocate the HGlobal to fit the new size of the stream.
553    */
554   supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
555
556   if (supportHandle == 0)
557     return STG_E_MEDIUMFULL;
558
559   This->supportHandle = supportHandle;
560   This->byteArraySize.u.LowPart = libNewSize.u.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 }