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