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