1 /* 2 * SHLWAPI IStream functions 3 * 4 * Copyright 2002 Jon Griffiths 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 #include <stdarg.h> 21 #include <string.h> 22 23 #define COBJMACROS 24 #define NONAMELESSUNION 25 26 #include "windef.h" 27 #include "winbase.h" 28 #include "winerror.h" 29 #include "winnls.h" 30 #define NO_SHLWAPI_REG 31 #define NO_SHLWAPI_PATH 32 #include "shlwapi.h" 33 #include "wine/debug.h" 34 35 WINE_DEFAULT_DEBUG_CHANNEL(shell); 36 37 #define STGM_ACCESS_MODE(stgm) ((stgm)&0x0000f) 38 #define STGM_SHARE_MODE(stgm) ((stgm)&0x000f0) 39 #define STGM_CREATE_MODE(stgm) ((stgm)&0x0f000) 40 41 /* Layout of ISHFileStream object */ 42 typedef struct 43 { 44 IStream IStream_iface; 45 LONG ref; 46 HANDLE hFile; 47 DWORD dwMode; 48 LPOLESTR lpszPath; 49 DWORD type; 50 DWORD grfStateBits; 51 } ISHFileStream; 52 53 static inline ISHFileStream *impl_from_IStream(IStream *iface) 54 { 55 return CONTAINING_RECORD(iface, ISHFileStream, IStream_iface); 56 } 57 58 static HRESULT WINAPI IStream_fnCommit(IStream*,DWORD); 59 60 61 /************************************************************************** 62 * IStream_fnQueryInterface 63 */ 64 static HRESULT WINAPI IStream_fnQueryInterface(IStream *iface, REFIID riid, LPVOID *ppvObj) 65 { 66 ISHFileStream *This = impl_from_IStream(iface); 67 68 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppvObj); 69 70 *ppvObj = NULL; 71 72 if(IsEqualIID(riid, &IID_IUnknown) || 73 IsEqualIID(riid, &IID_IStream)) 74 { 75 IStream_AddRef(iface); 76 *ppvObj = iface; 77 return S_OK; 78 } 79 return E_NOINTERFACE; 80 } 81 82 /************************************************************************** 83 * IStream_fnAddRef 84 */ 85 static ULONG WINAPI IStream_fnAddRef(IStream *iface) 86 { 87 ISHFileStream *This = impl_from_IStream(iface); 88 ULONG refCount = InterlockedIncrement(&This->ref); 89 90 TRACE("(%p)->(ref before=%u)\n",This, refCount - 1); 91 92 return refCount; 93 } 94 95 /************************************************************************** 96 * IStream_fnRelease 97 */ 98 static ULONG WINAPI IStream_fnRelease(IStream *iface) 99 { 100 ISHFileStream *This = impl_from_IStream(iface); 101 ULONG refCount = InterlockedDecrement(&This->ref); 102 103 TRACE("(%p)->(ref before=%u)\n",This, refCount + 1); 104 105 if (!refCount) 106 { 107 IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */ 108 LocalFree(This->lpszPath); 109 CloseHandle(This->hFile); 110 HeapFree(GetProcessHeap(), 0, This); 111 } 112 113 return refCount; 114 } 115 116 /************************************************************************** 117 * IStream_fnRead 118 */ 119 static HRESULT WINAPI IStream_fnRead(IStream *iface, void* pv, ULONG cb, ULONG* pcbRead) 120 { 121 ISHFileStream *This = impl_from_IStream(iface); 122 DWORD dwRead = 0; 123 124 TRACE("(%p,%p,0x%08x,%p)\n", This, pv, cb, pcbRead); 125 126 if (!ReadFile(This->hFile, pv, cb, &dwRead, NULL)) 127 { 128 WARN("error %d reading file\n", GetLastError()); 129 return S_FALSE; 130 } 131 if (pcbRead) 132 *pcbRead = dwRead; 133 return dwRead == cb ? S_OK : S_FALSE; 134 } 135 136 /************************************************************************** 137 * IStream_fnWrite 138 */ 139 static HRESULT WINAPI IStream_fnWrite(IStream *iface, const void* pv, ULONG cb, ULONG* pcbWritten) 140 { 141 ISHFileStream *This = impl_from_IStream(iface); 142 DWORD dwWritten = 0; 143 144 TRACE("(%p,%p,0x%08x,%p)\n", This, pv, cb, pcbWritten); 145 146 switch (STGM_ACCESS_MODE(This->dwMode)) 147 { 148 case STGM_WRITE: 149 case STGM_READWRITE: 150 break; 151 default: 152 return STG_E_ACCESSDENIED; 153 } 154 155 if (!WriteFile(This->hFile, pv, cb, &dwWritten, NULL)) 156 return HRESULT_FROM_WIN32(GetLastError()); 157 158 if (pcbWritten) 159 *pcbWritten = dwWritten; 160 return S_OK; 161 } 162 163 /************************************************************************** 164 * IStream_fnSeek 165 */ 166 static HRESULT WINAPI IStream_fnSeek(IStream *iface, LARGE_INTEGER dlibMove, 167 DWORD dwOrigin, ULARGE_INTEGER* pNewPos) 168 { 169 ISHFileStream *This = impl_from_IStream(iface); 170 DWORD dwPos; 171 172 TRACE("(%p,%d,%d,%p)\n", This, dlibMove.u.LowPart, dwOrigin, pNewPos); 173 174 IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */ 175 dwPos = SetFilePointer(This->hFile, dlibMove.u.LowPart, NULL, dwOrigin); 176 if( dwPos == INVALID_SET_FILE_POINTER ) 177 return HRESULT_FROM_WIN32(GetLastError()); 178 179 if (pNewPos) 180 { 181 pNewPos->u.HighPart = 0; 182 pNewPos->u.LowPart = dwPos; 183 } 184 return S_OK; 185 } 186 187 /************************************************************************** 188 * IStream_fnSetSize 189 */ 190 static HRESULT WINAPI IStream_fnSetSize(IStream *iface, ULARGE_INTEGER libNewSize) 191 { 192 ISHFileStream *This = impl_from_IStream(iface); 193 194 TRACE("(%p,%d)\n", This, libNewSize.u.LowPart); 195 196 IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */ 197 if( ! SetFilePointer( This->hFile, libNewSize.QuadPart, NULL, FILE_BEGIN ) ) 198 return E_FAIL; 199 200 if( ! SetEndOfFile( This->hFile ) ) 201 return E_FAIL; 202 203 return S_OK; 204 } 205 206 /************************************************************************** 207 * IStream_fnCopyTo 208 */ 209 static HRESULT WINAPI IStream_fnCopyTo(IStream *iface, IStream* pstm, ULARGE_INTEGER cb, 210 ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) 211 { 212 ISHFileStream *This = impl_from_IStream(iface); 213 char copyBuff[1024]; 214 ULONGLONG ulSize; 215 HRESULT hRet = S_OK; 216 217 TRACE("(%p,%p,%d,%p,%p)\n", This, pstm, cb.u.LowPart, pcbRead, pcbWritten); 218 219 if (pcbRead) 220 pcbRead->QuadPart = 0; 221 if (pcbWritten) 222 pcbWritten->QuadPart = 0; 223 224 if (!pstm) 225 return S_OK; 226 227 IStream_fnCommit(iface, 0); /* If ever buffered, this will be needed */ 228 229 /* Copy data */ 230 ulSize = cb.QuadPart; 231 while (ulSize) 232 { 233 ULONG ulLeft, ulRead, ulWritten; 234 235 ulLeft = ulSize > sizeof(copyBuff) ? sizeof(copyBuff) : ulSize; 236 237 /* Read */ 238 hRet = IStream_fnRead(iface, copyBuff, ulLeft, &ulRead); 239 if (FAILED(hRet) || ulRead == 0) 240 break; 241 if (pcbRead) 242 pcbRead->QuadPart += ulRead; 243 244 /* Write */ 245 hRet = IStream_fnWrite(pstm, copyBuff, ulRead, &ulWritten); 246 if (pcbWritten) 247 pcbWritten->QuadPart += ulWritten; 248 if (FAILED(hRet) || ulWritten != ulLeft) 249 break; 250 251 ulSize -= ulLeft; 252 } 253 return hRet; 254 } 255 256 /************************************************************************** 257 * IStream_fnCommit 258 */ 259 static HRESULT WINAPI IStream_fnCommit(IStream *iface, DWORD grfCommitFlags) 260 { 261 ISHFileStream *This = impl_from_IStream(iface); 262 263 TRACE("(%p,%d)\n", This, grfCommitFlags); 264 /* Currently unbuffered: This function is not needed */ 265 return S_OK; 266 } 267 268 /************************************************************************** 269 * IStream_fnRevert 270 */ 271 static HRESULT WINAPI IStream_fnRevert(IStream *iface) 272 { 273 ISHFileStream *This = impl_from_IStream(iface); 274 275 TRACE("(%p)\n", This); 276 return E_NOTIMPL; 277 } 278 279 /************************************************************************** 280 * IStream_fnLockUnlockRegion 281 */ 282 static HRESULT WINAPI IStream_fnLockUnlockRegion(IStream *iface, ULARGE_INTEGER libOffset, 283 ULARGE_INTEGER cb, DWORD dwLockType) 284 { 285 ISHFileStream *This = impl_from_IStream(iface); 286 TRACE("(%p,%d,%d,%d)\n", This, libOffset.u.LowPart, cb.u.LowPart, dwLockType); 287 return E_NOTIMPL; 288 } 289 290 /************************************************************************* 291 * IStream_fnStat 292 */ 293 static HRESULT WINAPI IStream_fnStat(IStream *iface, STATSTG* lpStat, 294 DWORD grfStatFlag) 295 { 296 ISHFileStream *This = impl_from_IStream(iface); 297 BY_HANDLE_FILE_INFORMATION fi; 298 299 TRACE("(%p,%p,%d)\n", This, lpStat, grfStatFlag); 300 301 if (!grfStatFlag) 302 return STG_E_INVALIDPOINTER; 303 304 memset(&fi, 0, sizeof(fi)); 305 GetFileInformationByHandle(This->hFile, &fi); 306 307 if (grfStatFlag & STATFLAG_NONAME) 308 lpStat->pwcsName = NULL; 309 else 310 lpStat->pwcsName = StrDupW(This->lpszPath); 311 lpStat->type = This->type; 312 lpStat->cbSize.u.LowPart = fi.nFileSizeLow; 313 lpStat->cbSize.u.HighPart = fi.nFileSizeHigh; 314 lpStat->mtime = fi.ftLastWriteTime; 315 lpStat->ctime = fi.ftCreationTime; 316 lpStat->atime = fi.ftLastAccessTime; 317 lpStat->grfMode = This->dwMode; 318 lpStat->grfLocksSupported = 0; 319 memcpy(&lpStat->clsid, &IID_IStream, sizeof(CLSID)); 320 lpStat->grfStateBits = This->grfStateBits; 321 lpStat->reserved = 0; 322 323 return S_OK; 324 } 325 326 /************************************************************************* 327 * IStream_fnClone 328 */ 329 static HRESULT WINAPI IStream_fnClone(IStream *iface, IStream** ppstm) 330 { 331 ISHFileStream *This = impl_from_IStream(iface); 332 333 TRACE("(%p)\n",This); 334 if (ppstm) 335 *ppstm = NULL; 336 return E_NOTIMPL; 337 } 338 339 static const IStreamVtbl SHLWAPI_fsVTable = 340 { 341 IStream_fnQueryInterface, 342 IStream_fnAddRef, 343 IStream_fnRelease, 344 IStream_fnRead, 345 IStream_fnWrite, 346 IStream_fnSeek, 347 IStream_fnSetSize, 348 IStream_fnCopyTo, 349 IStream_fnCommit, 350 IStream_fnRevert, 351 IStream_fnLockUnlockRegion, 352 IStream_fnLockUnlockRegion, 353 IStream_fnStat, 354 IStream_fnClone 355 }; 356 357 /************************************************************************** 358 * IStream_Create 359 * 360 * Internal helper: Create and initialise a new file stream object. 361 */ 362 static IStream *IStream_Create(LPCWSTR lpszPath, HANDLE hFile, DWORD dwMode) 363 { 364 ISHFileStream *fileStream; 365 366 fileStream = HeapAlloc(GetProcessHeap(), 0, sizeof(ISHFileStream)); 367 if (!fileStream) return NULL; 368 369 fileStream->IStream_iface.lpVtbl = &SHLWAPI_fsVTable; 370 fileStream->ref = 1; 371 fileStream->hFile = hFile; 372 fileStream->dwMode = dwMode; 373 fileStream->lpszPath = StrDupW(lpszPath); 374 fileStream->type = 0; /* FIXME */ 375 fileStream->grfStateBits = 0; /* FIXME */ 376 377 TRACE ("Returning %p\n", fileStream); 378 return &fileStream->IStream_iface; 379 } 380 381 /************************************************************************* 382 * SHCreateStreamOnFileEx [SHLWAPI.@] 383 * 384 * Create a stream on a file. 385 * 386 * PARAMS 387 * lpszPath [I] Path of file to create stream on 388 * dwMode [I] Mode to create stream in 389 * dwAttributes [I] Attributes of the file 390 * bCreate [I] Whether to create the file if it doesn't exist 391 * lpTemplate [I] Reserved, must be NULL 392 * lppStream [O] Destination for created stream 393 * 394 * RETURNS 395 * Success: S_OK. lppStream contains the new stream object 396 * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code 397 * 398 * NOTES 399 * This function is available in Unicode only. 400 */ 401 HRESULT WINAPI SHCreateStreamOnFileEx(LPCWSTR lpszPath, DWORD dwMode, 402 DWORD dwAttributes, BOOL bCreate, 403 IStream *lpTemplate, IStream **lppStream) 404 { 405 DWORD dwAccess, dwShare, dwCreate; 406 HANDLE hFile; 407 408 TRACE("(%s,%d,0x%08X,%d,%p,%p)\n", debugstr_w(lpszPath), dwMode, 409 dwAttributes, bCreate, lpTemplate, lppStream); 410 411 if (!lpszPath || !lppStream || lpTemplate) 412 return E_INVALIDARG; 413 414 *lppStream = NULL; 415 416 /* Access */ 417 switch (STGM_ACCESS_MODE(dwMode)) 418 { 419 case STGM_WRITE: 420 case STGM_READWRITE: 421 dwAccess = GENERIC_READ|GENERIC_WRITE; 422 break; 423 case STGM_READ: 424 dwAccess = GENERIC_READ; 425 break; 426 default: 427 return E_INVALIDARG; 428 } 429 430 /* Sharing */ 431 switch (STGM_SHARE_MODE(dwMode)) 432 { 433 case 0: 434 case STGM_SHARE_DENY_NONE: 435 dwShare = FILE_SHARE_READ|FILE_SHARE_WRITE; 436 break; 437 case STGM_SHARE_DENY_READ: 438 dwShare = FILE_SHARE_WRITE; 439 break; 440 case STGM_SHARE_DENY_WRITE: 441 dwShare = FILE_SHARE_READ; 442 break; 443 case STGM_SHARE_EXCLUSIVE: 444 dwShare = 0; 445 break; 446 default: 447 return E_INVALIDARG; 448 } 449 450 switch(STGM_CREATE_MODE(dwMode)) 451 { 452 case STGM_FAILIFTHERE: 453 dwCreate = bCreate ? CREATE_NEW : OPEN_EXISTING; 454 break; 455 case STGM_CREATE: 456 dwCreate = CREATE_ALWAYS; 457 break; 458 default: 459 return E_INVALIDARG; 460 } 461 462 /* Open HANDLE to file */ 463 hFile = CreateFileW(lpszPath, dwAccess, dwShare, NULL, dwCreate, 464 dwAttributes, 0); 465 466 if(hFile == INVALID_HANDLE_VALUE) 467 return HRESULT_FROM_WIN32(GetLastError()); 468 469 *lppStream = IStream_Create(lpszPath, hFile, dwMode); 470 471 if(!*lppStream) 472 { 473 CloseHandle(hFile); 474 return E_OUTOFMEMORY; 475 } 476 return S_OK; 477 } 478 479 /************************************************************************* 480 * SHCreateStreamOnFileW [SHLWAPI.@] 481 * 482 * See SHCreateStreamOnFileA. 483 */ 484 HRESULT WINAPI SHCreateStreamOnFileW(LPCWSTR lpszPath, DWORD dwMode, 485 IStream **lppStream) 486 { 487 TRACE("(%s,%d,%p)\n", debugstr_w(lpszPath), dwMode, lppStream); 488 489 if (!lpszPath || !lppStream) 490 return E_INVALIDARG; 491 492 if ((dwMode & (STGM_CONVERT|STGM_DELETEONRELEASE|STGM_TRANSACTED)) != 0) 493 return E_INVALIDARG; 494 495 return SHCreateStreamOnFileEx(lpszPath, dwMode, 0, FALSE, NULL, lppStream); 496 } 497 498 /************************************************************************* 499 * SHCreateStreamOnFileA [SHLWAPI.@] 500 * 501 * Create a stream on a file. 502 * 503 * PARAMS 504 * lpszPath [I] Path of file to create stream on 505 * dwMode [I] Mode to create stream in 506 * lppStream [O] Destination for created IStream object 507 * 508 * RETURNS 509 * Success: S_OK. lppStream contains the new IStream object 510 * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code 511 */ 512 HRESULT WINAPI SHCreateStreamOnFileA(LPCSTR lpszPath, DWORD dwMode, 513 IStream **lppStream) 514 { 515 WCHAR szPath[MAX_PATH]; 516 517 TRACE("(%s,%d,%p)\n", debugstr_a(lpszPath), dwMode, lppStream); 518 519 if (!lpszPath) 520 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); 521 522 MultiByteToWideChar(CP_ACP, 0, lpszPath, -1, szPath, MAX_PATH); 523 return SHCreateStreamOnFileW(szPath, dwMode, lppStream); 524 } 525 526 /************************************************************************* 527 * @ [SHLWAPI.184] 528 * 529 * Call IStream_Read() on a stream. 530 * 531 * PARAMS 532 * lpStream [I] IStream object 533 * lpvDest [O] Destination for data read 534 * ulSize [I] Size of data to read 535 * 536 * RETURNS 537 * Success: S_OK. ulSize bytes have been read from the stream into lpvDest. 538 * Failure: An HRESULT error code, or E_FAIL if the read succeeded but the 539 * number of bytes read does not match. 540 */ 541 HRESULT WINAPI SHIStream_Read(IStream *lpStream, LPVOID lpvDest, ULONG ulSize) 542 { 543 ULONG ulRead; 544 HRESULT hRet; 545 546 TRACE("(%p,%p,%d)\n", lpStream, lpvDest, ulSize); 547 548 hRet = IStream_Read(lpStream, lpvDest, ulSize, &ulRead); 549 550 if (SUCCEEDED(hRet) && ulRead != ulSize) 551 hRet = E_FAIL; 552 return hRet; 553 } 554 555 /************************************************************************* 556 * @ [SHLWAPI.166] 557 * 558 * Determine if a stream has 0 length. 559 * 560 * PARAMS 561 * lpStream [I] IStream object 562 * 563 * RETURNS 564 * TRUE: If the stream has 0 length 565 * FALSE: Otherwise. 566 */ 567 BOOL WINAPI SHIsEmptyStream(IStream *lpStream) 568 { 569 STATSTG statstg; 570 BOOL bRet = TRUE; 571 572 TRACE("(%p)\n", lpStream); 573 574 memset(&statstg, 0, sizeof(statstg)); 575 576 if(SUCCEEDED(IStream_Stat(lpStream, &statstg, 1))) 577 { 578 if(statstg.cbSize.QuadPart) 579 bRet = FALSE; /* Non-Zero */ 580 } 581 else 582 { 583 DWORD dwDummy; 584 585 /* Try to read from the stream */ 586 if(SUCCEEDED(SHIStream_Read(lpStream, &dwDummy, sizeof(dwDummy)))) 587 { 588 LARGE_INTEGER zero; 589 zero.QuadPart = 0; 590 591 IStream_Seek(lpStream, zero, 0, NULL); 592 bRet = FALSE; /* Non-Zero */ 593 } 594 } 595 return bRet; 596 } 597 598 /************************************************************************* 599 * @ [SHLWAPI.212] 600 * 601 * Call IStream_Write() on a stream. 602 * 603 * PARAMS 604 * lpStream [I] IStream object 605 * lpvSrc [I] Source for data to write 606 * ulSize [I] Size of data 607 * 608 * RETURNS 609 * Success: S_OK. ulSize bytes have been written to the stream from lpvSrc. 610 * Failure: An HRESULT error code, or E_FAIL if the write succeeded but the 611 * number of bytes written does not match. 612 */ 613 HRESULT WINAPI SHIStream_Write(IStream *lpStream, LPCVOID lpvSrc, ULONG ulSize) 614 { 615 ULONG ulWritten; 616 HRESULT hRet; 617 618 TRACE("(%p,%p,%d)\n", lpStream, lpvSrc, ulSize); 619 620 hRet = IStream_Write(lpStream, lpvSrc, ulSize, &ulWritten); 621 622 if (SUCCEEDED(hRet) && ulWritten != ulSize) 623 hRet = E_FAIL; 624 625 return hRet; 626 } 627 628 /************************************************************************* 629 * @ [SHLWAPI.213] 630 * 631 * Seek to the start of a stream. 632 * 633 * PARAMS 634 * lpStream [I] IStream object 635 * 636 * RETURNS 637 * Success: S_OK. The current position within the stream is updated 638 * Failure: An HRESULT error code. 639 */ 640 HRESULT WINAPI IStream_Reset(IStream *lpStream) 641 { 642 LARGE_INTEGER zero; 643 TRACE("(%p)\n", lpStream); 644 zero.QuadPart = 0; 645 return IStream_Seek(lpStream, zero, 0, NULL); 646 } 647 648 /************************************************************************* 649 * @ [SHLWAPI.214] 650 * 651 * Get the size of a stream. 652 * 653 * PARAMS 654 * lpStream [I] IStream object 655 * lpulSize [O] Destination for size 656 * 657 * RETURNS 658 * Success: S_OK. lpulSize contains the size of the stream. 659 * Failure: An HRESULT error code. 660 */ 661 HRESULT WINAPI IStream_Size(IStream *lpStream, ULARGE_INTEGER* lpulSize) 662 { 663 STATSTG statstg; 664 HRESULT hRet; 665 666 TRACE("(%p,%p)\n", lpStream, lpulSize); 667 668 memset(&statstg, 0, sizeof(statstg)); 669 670 hRet = IStream_Stat(lpStream, &statstg, 1); 671 672 if (SUCCEEDED(hRet) && lpulSize) 673 *lpulSize = statstg.cbSize; 674 return hRet; 675 } 676