riched20: Use ME_PointFromChar to calculate the caret position.
[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   ILockBytes ILockBytes_iface;
52   LONG ref;
53
54   /*
55    * Support for the LockBytes object
56    */
57   HGLOBAL supportHandle;
58
59   /*
60    * This flag is TRUE if the HGLOBAL is destroyed when the object
61    * is finally released.
62    */
63   BOOL    deleteOnRelease;
64
65   /*
66    * Helper variable that contains the size of the byte array
67    */
68   ULARGE_INTEGER     byteArraySize;
69 };
70
71 typedef struct HGLOBALLockBytesImpl HGLOBALLockBytesImpl;
72
73 static inline HGLOBALLockBytesImpl *impl_from_ILockBytes( ILockBytes *iface )
74 {
75     return CONTAINING_RECORD(iface, HGLOBALLockBytesImpl, ILockBytes_iface);
76 }
77
78 static const ILockBytesVtbl HGLOBALLockBytesImpl_Vtbl;
79
80 /******************************************************************************
81  *           CreateILockBytesOnHGlobal     [OLE32.@]
82  *
83  * Create a byte array object which is intended to be the compound file foundation.
84  * This object supports a COM implementation of the ILockBytes interface.
85  *
86  * PARAMS
87  *  global            [ I] Global memory handle
88  *  delete_on_release [ I] Whether the handle should be freed when the object is released.
89  *  ret               [ O] Address of ILockBytes pointer that receives
90  *                         the interface pointer to the new byte array object.
91  *
92  * RETURNS
93  *  Success: S_OK
94  *
95  * NOTES
96  *  The supplied ILockBytes pointer can be used by the StgCreateDocfileOnILockBytes
97  *  function to build a compound file on top of this byte array object.
98  *  The ILockBytes interface instance calls the GlobalReAlloc function to grow
99  *  the memory block as required.
100  */
101 HRESULT WINAPI CreateILockBytesOnHGlobal(HGLOBAL global, BOOL delete_on_release, ILockBytes **ret)
102 {
103   HGLOBALLockBytesImpl* lockbytes;
104
105   lockbytes = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALLockBytesImpl));
106   if (!lockbytes) return E_OUTOFMEMORY;
107
108   lockbytes->ILockBytes_iface.lpVtbl = &HGLOBALLockBytesImpl_Vtbl;
109   lockbytes->ref = 1;
110
111   /*
112    * Initialize the support.
113    */
114   lockbytes->supportHandle = global;
115   lockbytes->deleteOnRelease = delete_on_release;
116
117   /*
118    * This method will allocate a handle if one is not supplied.
119    */
120   if (lockbytes->supportHandle == 0)
121     lockbytes->supportHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, 0);
122
123   /*
124    * Initialize the size of the array to the size of the handle.
125    */
126   lockbytes->byteArraySize.u.HighPart = 0;
127   lockbytes->byteArraySize.u.LowPart  = GlobalSize(lockbytes->supportHandle);
128
129   *ret = &lockbytes->ILockBytes_iface;
130
131   return S_OK;
132 }
133
134 /******************************************************************************
135  *           GetHGlobalFromILockBytes     [OLE32.@]
136  *
137  * Retrieve a global memory handle to a byte array object created
138  * using the CreateILockBytesOnHGlobal function.
139  *
140  * PARAMS
141  *  plkbyt   [ I]  Pointer to the ILockBytes interface on byte array object
142  *  phglobal [ O]  Address to store a global memory handle
143  * RETURNS
144  *  S_OK          if *phglobal has a correct value
145  *  E_INVALIDARG  if any parameters are invalid
146  *  
147  */
148 HRESULT WINAPI GetHGlobalFromILockBytes(ILockBytes* iface, HGLOBAL* phglobal)
149 {
150   HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);
151   STATSTG stbuf;
152   HRESULT hres;
153   ULARGE_INTEGER start;
154   ULONG xread;
155
156   *phglobal = 0;
157   if (This->ILockBytes_iface.lpVtbl == &HGLOBALLockBytesImpl_Vtbl) {
158     *phglobal = This->supportHandle;
159     if (*phglobal == 0)
160       return E_INVALIDARG;
161     return S_OK;
162   }
163   /* It is not our lockbytes implementation, so use a more generic way */
164   hres = ILockBytes_Stat(iface,&stbuf,STATFLAG_NONAME);
165   if (hres != S_OK) {
166      ERR("Cannot ILockBytes_Stat, %x\n",hres);
167      return hres;
168   }
169   TRACE("cbSize is %s\n", wine_dbgstr_longlong(stbuf.cbSize.QuadPart));
170   *phglobal = GlobalAlloc( GMEM_MOVEABLE|GMEM_SHARE, stbuf.cbSize.u.LowPart);
171   if (!*phglobal)
172     return E_INVALIDARG;
173   memset(&start,0,sizeof(start));
174   hres = ILockBytes_ReadAt(iface, start, GlobalLock(*phglobal), stbuf.cbSize.u.LowPart, &xread);
175   GlobalUnlock(*phglobal);
176   if (hres != S_OK) {
177     FIXME("%p->ReadAt failed with %x\n",iface,hres);
178     return hres;
179   }
180   if (stbuf.cbSize.u.LowPart != xread) {
181     FIXME("Read size is not requested size %d vs %d?\n",stbuf.cbSize.u.LowPart, xread);
182   }
183   return S_OK;
184 }
185
186 /******************************************************************************
187  *
188  * HGLOBALLockBytesImpl implementation
189  *
190  */
191
192 /******************************************************************************
193  * This implements the IUnknown method QueryInterface for this
194  * class
195  */
196 static HRESULT WINAPI HGLOBALLockBytesImpl_QueryInterface(
197       ILockBytes*  iface,
198       REFIID       riid,        /* [in] */
199       void**       ppvObject)   /* [iid_is][out] */
200 {
201   HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);
202
203   if (ppvObject==0)
204     return E_INVALIDARG;
205
206   *ppvObject = 0;
207
208   if (IsEqualIID(riid, &IID_IUnknown) ||
209       IsEqualIID(riid, &IID_ILockBytes))
210   {
211     *ppvObject = &This->ILockBytes_iface;
212   }
213   else
214     return E_NOINTERFACE;
215
216   ILockBytes_AddRef(iface);
217
218   return S_OK;
219 }
220
221 /******************************************************************************
222  * This implements the IUnknown method AddRef for this
223  * class
224  */
225 static ULONG WINAPI HGLOBALLockBytesImpl_AddRef(ILockBytes* iface)
226 {
227   HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);
228   return InterlockedIncrement(&This->ref);
229 }
230
231 /******************************************************************************
232  * This implements the IUnknown method Release for this
233  * class
234  */
235 static ULONG WINAPI HGLOBALLockBytesImpl_Release(ILockBytes* iface)
236 {
237   HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);
238   ULONG ref;
239
240   ref = InterlockedDecrement(&This->ref);
241   if (!ref)
242   {
243     if (This->deleteOnRelease)
244     {
245       GlobalFree(This->supportHandle);
246       This->supportHandle = 0;
247     }
248     HeapFree(GetProcessHeap(), 0, This);
249   }
250
251   return ref;
252 }
253
254 /******************************************************************************
255  * This method is part of the ILockBytes interface.
256  *
257  * It reads a block of information from the byte array at the specified
258  * offset.
259  *
260  * See the documentation of ILockBytes for more info.
261  */
262 static HRESULT WINAPI HGLOBALLockBytesImpl_ReadAt(
263       ILockBytes*    iface,
264       ULARGE_INTEGER ulOffset,  /* [in] */
265       void*          pv,        /* [length_is][size_is][out] */
266       ULONG          cb,        /* [in] */
267       ULONG*         pcbRead)   /* [out] */
268 {
269   HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);
270
271   void* supportBuffer;
272   ULONG bytesReadBuffer = 0;
273   ULONG bytesToReadFromBuffer;
274
275   /*
276    * If the caller is not interested in the number of bytes read,
277    * we use another buffer to avoid "if" statements in the code.
278    */
279   if (pcbRead == 0)
280     pcbRead = &bytesReadBuffer;
281
282   /*
283    * Make sure the offset is valid.
284    */
285   if (ulOffset.u.LowPart > This->byteArraySize.u.LowPart)
286     return E_FAIL;
287
288   /*
289    * Using the known size of the array, calculate the number of bytes
290    * to read.
291    */
292   bytesToReadFromBuffer = min(This->byteArraySize.u.LowPart -
293                               ulOffset.u.LowPart, cb);
294
295   /*
296    * Lock the buffer in position and copy the data.
297    */
298   supportBuffer = GlobalLock(This->supportHandle);
299
300   memcpy(pv,
301          (char *) supportBuffer + ulOffset.u.LowPart,
302          bytesToReadFromBuffer);
303
304   /*
305    * Return the number of bytes read.
306    */
307   *pcbRead = bytesToReadFromBuffer;
308
309   /*
310    * Cleanup
311    */
312   GlobalUnlock(This->supportHandle);
313
314   /*
315    * The function returns S_OK if the specified number of bytes were read
316    * or the end of the array was reached.
317    * It returns STG_E_READFAULT if the number of bytes to read does not equal
318    * the number of bytes actually read.
319    */
320   if(*pcbRead == cb)
321     return S_OK;
322
323   return STG_E_READFAULT;
324 }
325
326 /******************************************************************************
327  * This method is part of the ILockBytes interface.
328  *
329  * It writes the specified bytes at the specified offset.
330  * position. If the array is too small, it will be resized.
331  *
332  * See the documentation of ILockBytes for more info.
333  */
334 static HRESULT WINAPI HGLOBALLockBytesImpl_WriteAt(
335       ILockBytes*    iface,
336       ULARGE_INTEGER ulOffset,    /* [in] */
337       const void*    pv,          /* [size_is][in] */
338       ULONG          cb,          /* [in] */
339       ULONG*         pcbWritten)  /* [out] */
340 {
341   HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);
342
343   void*          supportBuffer;
344   ULARGE_INTEGER newSize;
345   ULONG          bytesWritten = 0;
346
347   /*
348    * If the caller is not interested in the number of bytes written,
349    * we use another buffer to avoid "if" statements in the code.
350    */
351   if (pcbWritten == 0)
352     pcbWritten = &bytesWritten;
353
354   if (cb == 0)
355   {
356     return S_OK;
357   }
358   else
359   {
360     newSize.u.HighPart = 0;
361     newSize.u.LowPart = ulOffset.u.LowPart + cb;
362   }
363
364   /*
365    * Verify if we need to grow the stream
366    */
367   if (newSize.u.LowPart > This->byteArraySize.u.LowPart)
368   {
369     /* grow stream */
370     if (ILockBytes_SetSize(iface, newSize) == STG_E_MEDIUMFULL)
371       return STG_E_MEDIUMFULL;
372   }
373
374   /*
375    * Lock the buffer in position and copy the data.
376    */
377   supportBuffer = GlobalLock(This->supportHandle);
378
379   memcpy((char *) supportBuffer + ulOffset.u.LowPart, pv, cb);
380
381   /*
382    * Return the number of bytes written.
383    */
384   *pcbWritten = cb;
385
386   /*
387    * Cleanup
388    */
389   GlobalUnlock(This->supportHandle);
390
391   return S_OK;
392 }
393
394 /******************************************************************************
395  * This method is part of the ILockBytes interface.
396  *
397  * See the documentation of ILockBytes for more info.
398  */
399 static HRESULT WINAPI HGLOBALLockBytesImpl_Flush(ILockBytes* iface)
400 {
401   return S_OK;
402 }
403
404 /******************************************************************************
405  * This method is part of the ILockBytes interface.
406  *
407  * It will change the size of the byte array.
408  *
409  * See the documentation of ILockBytes for more info.
410  */
411 static HRESULT WINAPI HGLOBALLockBytesImpl_SetSize(
412       ILockBytes*     iface,
413       ULARGE_INTEGER  libNewSize)   /* [in] */
414 {
415   HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);
416   HGLOBAL supportHandle;
417
418   /*
419    * As documented.
420    */
421   if (libNewSize.u.HighPart != 0)
422     return STG_E_INVALIDFUNCTION;
423
424   if (This->byteArraySize.u.LowPart == libNewSize.u.LowPart)
425     return S_OK;
426
427   /*
428    * Re allocate the HGlobal to fit the new size of the stream.
429    */
430   supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
431
432   if (supportHandle == 0)
433     return STG_E_MEDIUMFULL;
434
435   This->supportHandle = supportHandle;
436   This->byteArraySize.u.LowPart = libNewSize.u.LowPart;
437
438   return S_OK;
439 }
440
441 /******************************************************************************
442  * This method is part of the ILockBytes interface.
443  *
444  * The global memory implementation of ILockBytes does not support locking.
445  *
446  * See the documentation of ILockBytes for more info.
447  */
448 static HRESULT WINAPI HGLOBALLockBytesImpl_LockRegion(
449       ILockBytes*    iface,
450       ULARGE_INTEGER libOffset,   /* [in] */
451       ULARGE_INTEGER cb,          /* [in] */
452       DWORD          dwLockType)  /* [in] */
453 {
454   return STG_E_INVALIDFUNCTION;
455 }
456
457 /******************************************************************************
458  * This method is part of the ILockBytes interface.
459  *
460  * The global memory implementation of ILockBytes does not support locking.
461  *
462  * See the documentation of ILockBytes for more info.
463  */
464 static HRESULT WINAPI HGLOBALLockBytesImpl_UnlockRegion(
465       ILockBytes*    iface,
466       ULARGE_INTEGER libOffset,   /* [in] */
467       ULARGE_INTEGER cb,          /* [in] */
468       DWORD          dwLockType)  /* [in] */
469 {
470   return STG_E_INVALIDFUNCTION;
471 }
472
473 /******************************************************************************
474  * This method is part of the ILockBytes interface.
475  *
476  * This method returns information about the current
477  * byte array object.
478  *
479  * See the documentation of ILockBytes for more info.
480  */
481 static HRESULT WINAPI HGLOBALLockBytesImpl_Stat(
482       ILockBytes*  iface,
483       STATSTG*     pstatstg,     /* [out] */
484       DWORD        grfStatFlag)  /* [in] */
485 {
486   HGLOBALLockBytesImpl* This = impl_from_ILockBytes(iface);
487
488   memset(pstatstg, 0, sizeof(STATSTG));
489
490   pstatstg->pwcsName = NULL;
491   pstatstg->type     = STGTY_LOCKBYTES;
492   pstatstg->cbSize   = This->byteArraySize;
493
494   return S_OK;
495 }
496
497 /*
498  * Virtual function table for the HGLOBALLockBytesImpl class.
499  */
500 static const ILockBytesVtbl HGLOBALLockBytesImpl_Vtbl =
501 {
502     HGLOBALLockBytesImpl_QueryInterface,
503     HGLOBALLockBytesImpl_AddRef,
504     HGLOBALLockBytesImpl_Release,
505     HGLOBALLockBytesImpl_ReadAt,
506     HGLOBALLockBytesImpl_WriteAt,
507     HGLOBALLockBytesImpl_Flush,
508     HGLOBALLockBytesImpl_SetSize,
509     HGLOBALLockBytesImpl_LockRegion,
510     HGLOBALLockBytesImpl_UnlockRegion,
511     HGLOBALLockBytesImpl_Stat,
512 };