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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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