ole32: Implement saving of the data cache.
[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 HGLOBALLockBytesImpl* HGLOBALLockBytesImpl_Construct(
85     HGLOBAL  hGlobal,
86     BOOL     fDeleteOnRelease);
87
88 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,0);
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 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 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 (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
290   {
291     *ppvObject = (ILockBytes*)This;
292   }
293   else if (memcmp(&IID_ILockBytes, riid, sizeof(IID_ILockBytes)) == 0)
294   {
295     *ppvObject = (ILockBytes*)This;
296   }
297
298   /*
299    * Check that we obtained an interface.
300    */
301   if ((*ppvObject)==0)
302     return E_NOINTERFACE;
303
304   /*
305    * Query Interface always increases the reference count by one when it is
306    * successful
307    */
308   IUnknown_AddRef(iface);
309
310   return S_OK;
311 }
312
313 /******************************************************************************
314  * This implements the IUnknown method AddRef for this
315  * class
316  */
317 static ULONG WINAPI HGLOBALLockBytesImpl_AddRef(ILockBytes* iface)
318 {
319   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
320   return InterlockedIncrement(&This->ref);
321 }
322
323 /******************************************************************************
324  * This implements the IUnknown method Release for this
325  * class
326  */
327 static ULONG WINAPI HGLOBALLockBytesImpl_Release(ILockBytes* iface)
328 {
329   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
330   ULONG ref;
331
332   ref = InterlockedDecrement(&This->ref);
333
334   /*
335    * If the reference count goes down to 0, perform suicide.
336    */
337   if (ref==0)
338   {
339     HGLOBALLockBytesImpl_Destroy(This);
340   }
341
342   return ref;
343 }
344
345 /******************************************************************************
346  * This method is part of the ILockBytes interface.
347  *
348  * It reads a block of information from the byte array at the specified
349  * offset.
350  *
351  * See the documentation of ILockBytes for more info.
352  */
353 static HRESULT WINAPI HGLOBALLockBytesImpl_ReadAt(
354       ILockBytes*    iface,
355       ULARGE_INTEGER ulOffset,  /* [in] */
356       void*          pv,        /* [length_is][size_is][out] */
357       ULONG          cb,        /* [in] */
358       ULONG*         pcbRead)   /* [out] */
359 {
360   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
361
362   void* supportBuffer;
363   ULONG bytesReadBuffer = 0;
364   ULONG bytesToReadFromBuffer;
365
366   /*
367    * If the caller is not interested in the number of bytes read,
368    * we use another buffer to avoid "if" statements in the code.
369    */
370   if (pcbRead == 0)
371     pcbRead = &bytesReadBuffer;
372
373   /*
374    * Make sure the offset is valid.
375    */
376   if (ulOffset.u.LowPart > This->byteArraySize.u.LowPart)
377     return E_FAIL;
378
379   /*
380    * Using the known size of the array, calculate the number of bytes
381    * to read.
382    */
383   bytesToReadFromBuffer = min(This->byteArraySize.u.LowPart -
384                               ulOffset.u.LowPart, cb);
385
386   /*
387    * Lock the buffer in position and copy the data.
388    */
389   supportBuffer = GlobalLock(This->supportHandle);
390
391   memcpy(pv,
392          (char *) supportBuffer + ulOffset.u.LowPart,
393          bytesToReadFromBuffer);
394
395   /*
396    * Return the number of bytes read.
397    */
398   *pcbRead = bytesToReadFromBuffer;
399
400   /*
401    * Cleanup
402    */
403   GlobalUnlock(This->supportHandle);
404
405   /*
406    * The function returns S_OK if the specified number of bytes were read
407    * or the end of the array was reached.
408    * It returns STG_E_READFAULT if the number of bytes to read does not equal
409    * the number of bytes actually read.
410    */
411   if(*pcbRead == cb)
412     return S_OK;
413
414   return STG_E_READFAULT;
415 }
416
417 /******************************************************************************
418  * This method is part of the ILockBytes interface.
419  *
420  * It writes the specified bytes at the specified offset.
421  * position. If the array is too small, it will be resized.
422  *
423  * See the documentation of ILockBytes for more info.
424  */
425 static HRESULT WINAPI HGLOBALLockBytesImpl_WriteAt(
426       ILockBytes*    iface,
427       ULARGE_INTEGER ulOffset,    /* [in] */
428       const void*    pv,          /* [size_is][in] */
429       ULONG          cb,          /* [in] */
430       ULONG*         pcbWritten)  /* [out] */
431 {
432   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
433
434   void*          supportBuffer;
435   ULARGE_INTEGER newSize;
436   ULONG          bytesWritten = 0;
437
438   /*
439    * If the caller is not interested in the number of bytes written,
440    * we use another buffer to avoid "if" statements in the code.
441    */
442   if (pcbWritten == 0)
443     pcbWritten = &bytesWritten;
444
445   if (cb == 0)
446   {
447     return S_OK;
448   }
449   else
450   {
451     newSize.u.HighPart = 0;
452     newSize.u.LowPart = ulOffset.u.LowPart + cb;
453   }
454
455   /*
456    * Verify if we need to grow the stream
457    */
458   if (newSize.u.LowPart > This->byteArraySize.u.LowPart)
459   {
460     /* grow stream */
461     if (HGLOBALLockBytesImpl_SetSize(iface, newSize) == STG_E_MEDIUMFULL)
462       return STG_E_MEDIUMFULL;
463   }
464
465   /*
466    * Lock the buffer in position and copy the data.
467    */
468   supportBuffer = GlobalLock(This->supportHandle);
469
470   memcpy((char *) supportBuffer + ulOffset.u.LowPart, pv, cb);
471
472   /*
473    * Return the number of bytes written.
474    */
475   *pcbWritten = cb;
476
477   /*
478    * Cleanup
479    */
480   GlobalUnlock(This->supportHandle);
481
482   return S_OK;
483 }
484
485 /******************************************************************************
486  * This method is part of the ILockBytes interface.
487  *
488  * See the documentation of ILockBytes for more info.
489  */
490 static HRESULT WINAPI HGLOBALLockBytesImpl_Flush(ILockBytes* iface)
491 {
492   return S_OK;
493 }
494
495 /******************************************************************************
496  * This method is part of the ILockBytes interface.
497  *
498  * It will change the size of the byte array.
499  *
500  * See the documentation of ILockBytes for more info.
501  */
502 static HRESULT WINAPI HGLOBALLockBytesImpl_SetSize(
503       ILockBytes*     iface,
504       ULARGE_INTEGER  libNewSize)   /* [in] */
505 {
506   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
507   HGLOBAL supportHandle;
508
509   /*
510    * As documented.
511    */
512   if (libNewSize.u.HighPart != 0)
513     return STG_E_INVALIDFUNCTION;
514
515   if (This->byteArraySize.u.LowPart == libNewSize.u.LowPart)
516     return S_OK;
517
518   /*
519    * Re allocate the HGlobal to fit the new size of the stream.
520    */
521   supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
522
523   if (supportHandle == 0)
524     return STG_E_MEDIUMFULL;
525
526   This->supportHandle = supportHandle;
527   This->byteArraySize.u.LowPart = libNewSize.u.LowPart;
528
529   return S_OK;
530 }
531
532 /******************************************************************************
533  * This method is part of the ILockBytes interface.
534  *
535  * The global memory implementation of ILockBytes does not support locking.
536  *
537  * See the documentation of ILockBytes for more info.
538  */
539 static HRESULT WINAPI HGLOBALLockBytesImpl_LockRegion(
540       ILockBytes*    iface,
541       ULARGE_INTEGER libOffset,   /* [in] */
542       ULARGE_INTEGER cb,          /* [in] */
543       DWORD          dwLockType)  /* [in] */
544 {
545   return STG_E_INVALIDFUNCTION;
546 }
547
548 /******************************************************************************
549  * This method is part of the ILockBytes interface.
550  *
551  * The global memory implementation of ILockBytes does not support locking.
552  *
553  * See the documentation of ILockBytes for more info.
554  */
555 static HRESULT WINAPI HGLOBALLockBytesImpl_UnlockRegion(
556       ILockBytes*    iface,
557       ULARGE_INTEGER libOffset,   /* [in] */
558       ULARGE_INTEGER cb,          /* [in] */
559       DWORD          dwLockType)  /* [in] */
560 {
561   return STG_E_INVALIDFUNCTION;
562 }
563
564 /******************************************************************************
565  * This method is part of the ILockBytes interface.
566  *
567  * This method returns information about the current
568  * byte array object.
569  *
570  * See the documentation of ILockBytes for more info.
571  */
572 static HRESULT WINAPI HGLOBALLockBytesImpl_Stat(
573       ILockBytes*  iface,
574       STATSTG*     pstatstg,     /* [out] */
575       DWORD        grfStatFlag)  /* [in] */
576 {
577   HGLOBALLockBytesImpl* const This=(HGLOBALLockBytesImpl*)iface;
578
579   memset(pstatstg, 0, sizeof(STATSTG));
580
581   pstatstg->pwcsName = NULL;
582   pstatstg->type     = STGTY_LOCKBYTES;
583   pstatstg->cbSize   = This->byteArraySize;
584
585   return S_OK;
586 }
587
588 /*
589  * Virtual function table for the HGLOBALLockBytesImpl class.
590  */
591 static const ILockBytesVtbl HGLOBALLockBytesImpl_Vtbl =
592 {
593     HGLOBALLockBytesImpl_QueryInterface,
594     HGLOBALLockBytesImpl_AddRef,
595     HGLOBALLockBytesImpl_Release,
596     HGLOBALLockBytesImpl_ReadAt,
597     HGLOBALLockBytesImpl_WriteAt,
598     HGLOBALLockBytesImpl_Flush,
599     HGLOBALLockBytesImpl_SetSize,
600     HGLOBALLockBytesImpl_LockRegion,
601     HGLOBALLockBytesImpl_UnlockRegion,
602     HGLOBALLockBytesImpl_Stat,
603 };