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