Update the address of the Free Software Foundation.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 struct
53    * since we want to cast this in an ILockBytes pointer
54    */
55   const ILockBytesVtbl *lpVtbl;
56
57   /*
58    * Reference count
59    */
60   LONG        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 const 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  * Create a byte array object which is intended to be the compound file foundation.
160  * This object supports a COM implementation of the ILockBytes interface.
161  *
162  * PARAMS
163  *  hGlobal           [ I] Global memory handle
164  *  fDeleteOnRelease  [ I] Whether the handle should be freed when the object is released. 
165  *  ppLkbyt           [ O] Address of ILockBytes pointer that receives
166  *                         the interface pointer to the new byte array object.
167  *
168  * RETURNS
169  *  Success: S_OK
170  *
171  * NOTES
172  *  The supplied ILockBytes pointer can be used by the StgCreateDocfileOnILockBytes
173  *  function to build a compound file on top of this byte array object.
174  *  The ILockBytes interface instance calls the GlobalReAlloc function to grow
175  *  the memory block as required.
176  */
177 HRESULT WINAPI CreateILockBytesOnHGlobal(HGLOBAL      hGlobal,
178                                          BOOL         fDeleteOnRelease,
179                                          LPLOCKBYTES* ppLkbyt)
180 {
181   HGLOBALLockBytesImpl* newLockBytes;
182
183   newLockBytes = HGLOBALLockBytesImpl_Construct(hGlobal, fDeleteOnRelease);
184
185   if (newLockBytes != NULL)
186   {
187     return IUnknown_QueryInterface((IUnknown*)newLockBytes,
188                                    &IID_ILockBytes,
189                                    (void**)ppLkbyt);
190   }
191
192   return E_OUTOFMEMORY;
193 }
194
195 /******************************************************************************
196  *           GetHGlobalFromILockBytes     [OLE32.@]
197  *
198  * Retrieve a global memory handle to a byte array object created
199  * using the CreateILockBytesOnHGlobal function.
200  *
201  * PARAMS
202  *  plkbyt   [ I]  Pointer to the ILockBytes interface on byte array object
203  *  phglobal [ O]  Address to store a global memory handle
204  * RETURNS
205  *  S_OK          if *phglobal has a correct value
206  *  E_INVALIDARG  if any parameters are invalid
207  *  
208  */
209 HRESULT WINAPI GetHGlobalFromILockBytes(ILockBytes* plkbyt, HGLOBAL* phglobal)
210 {
211   HGLOBALLockBytesImpl* const pMemLockBytes = (HGLOBALLockBytesImpl*)plkbyt;
212   STATSTG stbuf;
213   HRESULT hres;
214   ULARGE_INTEGER start;
215   ULONG xread;
216
217   *phglobal = 0;
218   if (pMemLockBytes->lpVtbl == &HGLOBALLockBytesImpl_Vtbl) {
219     *phglobal = pMemLockBytes->supportHandle;
220     if (*phglobal == 0)
221       return E_INVALIDARG;
222     return S_OK;
223   }
224   /* It is not our lockbytes implementation, so use a more generic way */
225   hres = ILockBytes_Stat(plkbyt,&stbuf,0);
226   if (hres != S_OK) {
227      ERR("Cannot ILockBytes_Stat, %lx\n",hres);
228      return hres;
229   }
230   FIXME("cbSize is %ld\n",stbuf.cbSize.u.LowPart);
231   *phglobal = GlobalAlloc( GMEM_MOVEABLE|GMEM_SHARE, stbuf.cbSize.u.LowPart);
232   if (!*phglobal)
233     return E_INVALIDARG;
234   memset(&start,0,sizeof(start));
235   hres = ILockBytes_ReadAt(plkbyt, start, GlobalLock(*phglobal), stbuf.cbSize.u.LowPart, &xread);
236   GlobalUnlock(*phglobal);
237   if (hres != S_OK) {
238     FIXME("%p->ReadAt failed with %lx\n",plkbyt,hres);
239     return hres;
240   }
241   if (stbuf.cbSize.u.LowPart != xread) {
242     FIXME("Read size is not requested size %ld vs %ld?\n",stbuf.cbSize.u.LowPart, xread);
243   }
244   return S_OK;
245 }
246
247 /******************************************************************************
248  *
249  * HGLOBALLockBytesImpl implementation
250  *
251  */
252
253 /******************************************************************************
254  * This is the constructor for the HGLOBALLockBytesImpl class.
255  *
256  * PARAMS
257  *    hGlobal          [ I] Handle that will support the stream. can be NULL.
258  *    fDeleteOnRelease [ I] Flag set to TRUE if the HGLOBAL will be released
259  *                          when the IStream object is destroyed.
260  */
261 HGLOBALLockBytesImpl* HGLOBALLockBytesImpl_Construct(HGLOBAL hGlobal,
262                                                      BOOL    fDeleteOnRelease)
263 {
264   HGLOBALLockBytesImpl* newLockBytes;
265   newLockBytes = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALLockBytesImpl));
266
267   if (newLockBytes!=0)
268   {
269     /*
270      * Set up the virtual function table and reference count.
271      */
272     newLockBytes->lpVtbl = &HGLOBALLockBytesImpl_Vtbl;
273     newLockBytes->ref    = 0;
274
275     /*
276      * Initialize the support.
277      */
278     newLockBytes->supportHandle = hGlobal;
279     newLockBytes->deleteOnRelease = fDeleteOnRelease;
280
281     /*
282      * This method will allocate a handle if one is not supplied.
283      */
284     if (newLockBytes->supportHandle == 0)
285     {
286       newLockBytes->supportHandle = GlobalAlloc(GMEM_MOVEABLE |
287                                                 GMEM_NODISCARD,
288                                                 0);
289     }
290
291     /*
292      * Initialize the size of the array to the size of the handle.
293      */
294     newLockBytes->byteArraySize.u.HighPart = 0;
295     newLockBytes->byteArraySize.u.LowPart  = GlobalSize(
296                                               newLockBytes->supportHandle);
297   }
298
299   return newLockBytes;
300 }
301
302 /******************************************************************************
303  * This is the destructor of the HGLOBALStreamImpl class.
304  *
305  * This method will clean-up all the resources used-up by the given
306  * HGLOBALLockBytesImpl class. The pointer passed-in to this function will be
307  * freed and will not be valid anymore.
308  */
309 void HGLOBALLockBytesImpl_Destroy(HGLOBALLockBytesImpl* This)
310 {
311   /*
312    * Release the HGlobal if the constructor asked for that.
313    */
314   if (This->deleteOnRelease)
315   {
316     GlobalFree(This->supportHandle);
317     This->supportHandle = 0;
318   }
319
320   /*
321    * Finally, free the memory used-up by the class.
322    */
323   HeapFree(GetProcessHeap(), 0, This);
324 }
325
326 /******************************************************************************
327  * This implements the IUnknown method QueryInterface for this
328  * class
329  */
330 HRESULT WINAPI HGLOBALLockBytesImpl_QueryInterface(
331       ILockBytes*  iface,
332       REFIID       riid,        /* [in] */
333       void**       ppvObject)   /* [iid_is][out] */
334 {
335   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
336
337   /*
338    * Perform a sanity check on the parameters.
339    */
340   if (ppvObject==0)
341     return E_INVALIDARG;
342
343   /*
344    * Initialize the return parameter.
345    */
346   *ppvObject = 0;
347
348   /*
349    * Compare the riid with the interface IDs implemented by this object.
350    */
351   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
352   {
353     *ppvObject = (ILockBytes*)This;
354   }
355   else if (memcmp(&IID_ILockBytes, riid, sizeof(IID_ILockBytes)) == 0)
356   {
357     *ppvObject = (ILockBytes*)This;
358   }
359
360   /*
361    * Check that we obtained an interface.
362    */
363   if ((*ppvObject)==0)
364     return E_NOINTERFACE;
365
366   /*
367    * Query Interface always increases the reference count by one when it is
368    * successful
369    */
370   HGLOBALLockBytesImpl_AddRef(iface);
371
372   return S_OK;
373 }
374
375 /******************************************************************************
376  * This implements the IUnknown method AddRef for this
377  * class
378  */
379 ULONG WINAPI HGLOBALLockBytesImpl_AddRef(ILockBytes* iface)
380 {
381   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
382   return InterlockedIncrement(&This->ref);
383 }
384
385 /******************************************************************************
386  * This implements the IUnknown method Release for this
387  * class
388  */
389 ULONG WINAPI HGLOBALLockBytesImpl_Release(ILockBytes* iface)
390 {
391   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
392   ULONG ref;
393
394   ref = InterlockedDecrement(&This->ref);
395
396   /*
397    * If the reference count goes down to 0, perform suicide.
398    */
399   if (ref==0)
400   {
401     HGLOBALLockBytesImpl_Destroy(This);
402   }
403
404   return ref;
405 }
406
407 /******************************************************************************
408  * This method is part of the ILockBytes interface.
409  *
410  * It reads a block of information from the byte array at the specified
411  * offset.
412  *
413  * See the documentation of ILockBytes for more info.
414  */
415 HRESULT WINAPI HGLOBALLockBytesImpl_ReadAt(
416       ILockBytes*    iface,
417       ULARGE_INTEGER ulOffset,  /* [in] */
418       void*          pv,        /* [length_is][size_is][out] */
419       ULONG          cb,        /* [in] */
420       ULONG*         pcbRead)   /* [out] */
421 {
422   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
423
424   void* supportBuffer;
425   ULONG bytesReadBuffer = 0;
426   ULONG bytesToReadFromBuffer;
427
428   /*
429    * If the caller is not interested in the number of bytes read,
430    * we use another buffer to avoid "if" statements in the code.
431    */
432   if (pcbRead == 0)
433     pcbRead = &bytesReadBuffer;
434
435   /*
436    * Make sure the offset is valid.
437    */
438   if (ulOffset.u.LowPart > This->byteArraySize.u.LowPart)
439     return E_FAIL;
440
441   /*
442    * Using the known size of the array, calculate the number of bytes
443    * to read.
444    */
445   bytesToReadFromBuffer = min(This->byteArraySize.u.LowPart -
446                               ulOffset.u.LowPart, cb);
447
448   /*
449    * Lock the buffer in position and copy the data.
450    */
451   supportBuffer = GlobalLock(This->supportHandle);
452
453   memcpy(pv,
454          (char *) supportBuffer + ulOffset.u.LowPart,
455          bytesToReadFromBuffer);
456
457   /*
458    * Return the number of bytes read.
459    */
460   *pcbRead = bytesToReadFromBuffer;
461
462   /*
463    * Cleanup
464    */
465   GlobalUnlock(This->supportHandle);
466
467   /*
468    * The function returns S_OK if the specified number of bytes were read
469    * or the end of the array was reached.
470    * It returns STG_E_READFAULT if the number of bytes to read does not equal
471    * the number of bytes actually read.
472    */
473   if(*pcbRead == cb)
474     return S_OK;
475
476   return STG_E_READFAULT;
477 }
478
479 /******************************************************************************
480  * This method is part of the ILockBytes interface.
481  *
482  * It writes the specified bytes at the specified offset.
483  * position. If the array is too small, it will be resized.
484  *
485  * See the documentation of ILockBytes for more info.
486  */
487 HRESULT WINAPI HGLOBALLockBytesImpl_WriteAt(
488       ILockBytes*    iface,
489       ULARGE_INTEGER ulOffset,    /* [in] */
490       const void*    pv,          /* [size_is][in] */
491       ULONG          cb,          /* [in] */
492       ULONG*         pcbWritten)  /* [out] */
493 {
494   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
495
496   void*          supportBuffer;
497   ULARGE_INTEGER newSize;
498   ULONG          bytesWritten = 0;
499
500   /*
501    * If the caller is not interested in the number of bytes written,
502    * we use another buffer to avoid "if" statements in the code.
503    */
504   if (pcbWritten == 0)
505     pcbWritten = &bytesWritten;
506
507   if (cb == 0)
508   {
509     return S_OK;
510   }
511   else
512   {
513     newSize.u.HighPart = 0;
514     newSize.u.LowPart = ulOffset.u.LowPart + cb;
515   }
516
517   /*
518    * Verify if we need to grow the stream
519    */
520   if (newSize.u.LowPart > This->byteArraySize.u.LowPart)
521   {
522     /* grow stream */
523     if (HGLOBALLockBytesImpl_SetSize(iface, newSize) == STG_E_MEDIUMFULL)
524       return STG_E_MEDIUMFULL;
525   }
526
527   /*
528    * Lock the buffer in position and copy the data.
529    */
530   supportBuffer = GlobalLock(This->supportHandle);
531
532   memcpy((char *) supportBuffer + ulOffset.u.LowPart, pv, cb);
533
534   /*
535    * Return the number of bytes written.
536    */
537   *pcbWritten = cb;
538
539   /*
540    * Cleanup
541    */
542   GlobalUnlock(This->supportHandle);
543
544   return S_OK;
545 }
546
547 /******************************************************************************
548  * This method is part of the ILockBytes interface.
549  *
550  * See the documentation of ILockBytes for more info.
551  */
552 HRESULT WINAPI HGLOBALLockBytesImpl_Flush(ILockBytes* iface)
553 {
554   return S_OK;
555 }
556
557 /******************************************************************************
558  * This method is part of the ILockBytes interface.
559  *
560  * It will change the size of the byte array.
561  *
562  * See the documentation of ILockBytes for more info.
563  */
564 HRESULT WINAPI HGLOBALLockBytesImpl_SetSize(
565       ILockBytes*     iface,
566       ULARGE_INTEGER  libNewSize)   /* [in] */
567 {
568   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
569   HGLOBAL supportHandle;
570
571   /*
572    * As documented.
573    */
574   if (libNewSize.u.HighPart != 0)
575     return STG_E_INVALIDFUNCTION;
576
577   if (This->byteArraySize.u.LowPart == libNewSize.u.LowPart)
578     return S_OK;
579
580   /*
581    * Re allocate the HGlobal to fit the new size of the stream.
582    */
583   supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
584
585   if (supportHandle == 0)
586     return STG_E_MEDIUMFULL;
587
588   This->supportHandle = supportHandle;
589   This->byteArraySize.u.LowPart = libNewSize.u.LowPart;
590
591   return S_OK;
592 }
593
594 /******************************************************************************
595  * This method is part of the ILockBytes interface.
596  *
597  * The global memory implementation of ILockBytes does not support locking.
598  *
599  * See the documentation of ILockBytes for more info.
600  */
601 HRESULT WINAPI HGLOBALLockBytesImpl_LockRegion(
602       ILockBytes*    iface,
603       ULARGE_INTEGER libOffset,   /* [in] */
604       ULARGE_INTEGER cb,          /* [in] */
605       DWORD          dwLockType)  /* [in] */
606 {
607   return STG_E_INVALIDFUNCTION;
608 }
609
610 /******************************************************************************
611  * This method is part of the ILockBytes interface.
612  *
613  * The global memory implementation of ILockBytes does not support locking.
614  *
615  * See the documentation of ILockBytes for more info.
616  */
617 HRESULT WINAPI HGLOBALLockBytesImpl_UnlockRegion(
618       ILockBytes*    iface,
619       ULARGE_INTEGER libOffset,   /* [in] */
620       ULARGE_INTEGER cb,          /* [in] */
621       DWORD          dwLockType)  /* [in] */
622 {
623   return STG_E_INVALIDFUNCTION;
624 }
625
626 /******************************************************************************
627  * This method is part of the ILockBytes interface.
628  *
629  * This method returns information about the current
630  * byte array object.
631  *
632  * See the documentation of ILockBytes for more info.
633  */
634 HRESULT WINAPI HGLOBALLockBytesImpl_Stat(
635       ILockBytes*  iface,
636       STATSTG*     pstatstg,     /* [out] */
637       DWORD        grfStatFlag)  /* [in] */
638 {
639   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
640
641   memset(pstatstg, 0, sizeof(STATSTG));
642
643   pstatstg->pwcsName = NULL;
644   pstatstg->type     = STGTY_LOCKBYTES;
645   pstatstg->cbSize   = This->byteArraySize;
646
647   return S_OK;
648 }