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