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