[rshell]
[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 *ppvObj = This;
62 IStream_AddRef(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, ulAmt;
220
221 ulLeft = ulSize > sizeof(copyBuff) ? sizeof(copyBuff) : ulSize;
222
223 /* Read */
224 hRet = IStream_fnRead(iface, copyBuff, ulLeft, &ulAmt);
225 if (pcbRead)
226 pcbRead->QuadPart += ulAmt;
227 if (FAILED(hRet) || ulAmt != ulLeft)
228 break;
229
230 /* Write */
231 hRet = IStream_fnWrite(pstm, copyBuff, ulLeft, &ulAmt);
232 if (pcbWritten)
233 pcbWritten->QuadPart += ulAmt;
234 if (FAILED(hRet) || ulAmt != 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 HRESULT hRet = S_OK;
285
286 TRACE("(%p,%p,%d)\n", This, lpStat, grfStatFlag);
287
288 if (!grfStatFlag)
289 hRet = STG_E_INVALIDPOINTER;
290 else
291 {
292 memset(&fi, 0, sizeof(fi));
293 GetFileInformationByHandle(This->hFile, &fi);
294
295 if (grfStatFlag & STATFLAG_NONAME)
296 lpStat->pwcsName = NULL;
297 else
298 lpStat->pwcsName = StrDupW(This->lpszPath);
299 lpStat->type = This->type;
300 lpStat->cbSize.u.LowPart = fi.nFileSizeLow;
301 lpStat->cbSize.u.HighPart = fi.nFileSizeHigh;
302 lpStat->mtime = fi.ftLastWriteTime;
303 lpStat->ctime = fi.ftCreationTime;
304 lpStat->atime = fi.ftLastAccessTime;
305 lpStat->grfMode = This->dwMode;
306 lpStat->grfLocksSupported = 0;
307 memcpy(&lpStat->clsid, &IID_IStream, sizeof(CLSID));
308 lpStat->grfStateBits = This->grfStateBits;
309 lpStat->reserved = 0;
310 }
311 return hRet;
312 }
313
314 /*************************************************************************
315 * IStream_fnClone
316 */
317 static HRESULT WINAPI IStream_fnClone(IStream *iface, IStream** ppstm)
318 {
319 ISHFileStream *This = impl_from_IStream(iface);
320
321 TRACE("(%p)\n",This);
322 if (ppstm)
323 *ppstm = NULL;
324 return E_NOTIMPL;
325 }
326
327 static const IStreamVtbl SHLWAPI_fsVTable =
328 {
329 IStream_fnQueryInterface,
330 IStream_fnAddRef,
331 IStream_fnRelease,
332 IStream_fnRead,
333 IStream_fnWrite,
334 IStream_fnSeek,
335 IStream_fnSetSize,
336 IStream_fnCopyTo,
337 IStream_fnCommit,
338 IStream_fnRevert,
339 IStream_fnLockUnlockRegion,
340 IStream_fnLockUnlockRegion,
341 IStream_fnStat,
342 IStream_fnClone
343 };
344
345 /**************************************************************************
346 * IStream_Create
347 *
348 * Internal helper: Create and initialise a new file stream object.
349 */
350 static IStream *IStream_Create(LPCWSTR lpszPath, HANDLE hFile, DWORD dwMode)
351 {
352 ISHFileStream* fileStream;
353
354 fileStream = HeapAlloc(GetProcessHeap(), 0, sizeof(ISHFileStream));
355
356 if (fileStream)
357 {
358 fileStream->IStream_iface.lpVtbl = &SHLWAPI_fsVTable;
359 fileStream->ref = 1;
360 fileStream->hFile = hFile;
361 fileStream->dwMode = dwMode;
362 fileStream->lpszPath = StrDupW(lpszPath);
363 fileStream->type = 0; /* FIXME */
364 fileStream->grfStateBits = 0; /* FIXME */
365 }
366 TRACE ("Returning %p\n", fileStream);
367 return &fileStream->IStream_iface;
368 }
369
370 /*************************************************************************
371 * SHCreateStreamOnFileEx [SHLWAPI.@]
372 *
373 * Create a stream on a file.
374 *
375 * PARAMS
376 * lpszPath [I] Path of file to create stream on
377 * dwMode [I] Mode to create stream in
378 * dwAttributes [I] Attributes of the file
379 * bCreate [I] Whether to create the file if it doesn't exist
380 * lpTemplate [I] Reserved, must be NULL
381 * lppStream [O] Destination for created stream
382 *
383 * RETURNS
384 * Success: S_OK. lppStream contains the new stream object
385 * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code
386 *
387 * NOTES
388 * This function is available in Unicode only.
389 */
390 HRESULT WINAPI SHCreateStreamOnFileEx(LPCWSTR lpszPath, DWORD dwMode,
391 DWORD dwAttributes, BOOL bCreate,
392 IStream *lpTemplate, IStream **lppStream)
393 {
394 DWORD dwAccess, dwShare, dwCreate;
395 HANDLE hFile;
396
397 TRACE("(%s,%d,0x%08X,%d,%p,%p)\n", debugstr_w(lpszPath), dwMode,
398 dwAttributes, bCreate, lpTemplate, lppStream);
399
400 if (!lpszPath || !lppStream || lpTemplate)
401 return E_INVALIDARG;
402
403 *lppStream = NULL;
404
405 /* Access */
406 switch (STGM_ACCESS_MODE(dwMode))
407 {
408 case STGM_READWRITE:
409 dwAccess = GENERIC_READ|GENERIC_WRITE;
410 break;
411 case STGM_WRITE:
412 dwAccess = GENERIC_WRITE;
413 break;
414 case STGM_READ:
415 dwAccess = GENERIC_READ;
416 break;
417 default:
418 return E_INVALIDARG;
419 }
420
421 /* Sharing */
422 switch (STGM_SHARE_MODE(dwMode))
423 {
424 case 0:
425 dwShare = FILE_SHARE_READ|FILE_SHARE_WRITE;
426 break;
427 case STGM_SHARE_DENY_READ:
428 dwShare = FILE_SHARE_WRITE;
429 break;
430 case STGM_SHARE_DENY_WRITE:
431 dwShare = FILE_SHARE_READ;
432 break;
433 case STGM_SHARE_EXCLUSIVE:
434 dwShare = 0;
435 break;
436 case STGM_SHARE_DENY_NONE:
437 dwShare = FILE_SHARE_READ|FILE_SHARE_WRITE;
438 break;
439 default:
440 return E_INVALIDARG;
441 }
442
443 switch(STGM_CREATE_MODE(dwMode))
444 {
445 case STGM_FAILIFTHERE:
446 dwCreate = bCreate ? CREATE_NEW : OPEN_EXISTING;
447 break;
448 case STGM_CREATE:
449 dwCreate = CREATE_ALWAYS;
450 break;
451 default:
452 return E_INVALIDARG;
453 }
454
455 /* Open HANDLE to file */
456 hFile = CreateFileW(lpszPath, dwAccess, dwShare, NULL, dwCreate,
457 dwAttributes, 0);
458
459 if(hFile == INVALID_HANDLE_VALUE)
460 return HRESULT_FROM_WIN32(GetLastError());
461
462 *lppStream = IStream_Create(lpszPath, hFile, dwMode);
463
464 if(!*lppStream)
465 {
466 CloseHandle(hFile);
467 return E_OUTOFMEMORY;
468 }
469 return S_OK;
470 }
471
472 /*************************************************************************
473 * SHCreateStreamOnFileW [SHLWAPI.@]
474 *
475 * See SHCreateStreamOnFileA.
476 */
477 HRESULT WINAPI SHCreateStreamOnFileW(LPCWSTR lpszPath, DWORD dwMode,
478 IStream **lppStream)
479 {
480 TRACE("(%s,%d,%p)\n", debugstr_w(lpszPath), dwMode, lppStream);
481
482 if (!lpszPath || !lppStream)
483 return E_INVALIDARG;
484
485 if ((dwMode & (STGM_CONVERT|STGM_DELETEONRELEASE|STGM_TRANSACTED)) != 0)
486 return E_INVALIDARG;
487
488 return SHCreateStreamOnFileEx(lpszPath, dwMode, 0, FALSE, NULL, lppStream);
489 }
490
491 /*************************************************************************
492 * SHCreateStreamOnFileA [SHLWAPI.@]
493 *
494 * Create a stream on a file.
495 *
496 * PARAMS
497 * lpszPath [I] Path of file to create stream on
498 * dwMode [I] Mode to create stream in
499 * lppStream [O] Destination for created IStream object
500 *
501 * RETURNS
502 * Success: S_OK. lppStream contains the new IStream object
503 * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code
504 */
505 HRESULT WINAPI SHCreateStreamOnFileA(LPCSTR lpszPath, DWORD dwMode,
506 IStream **lppStream)
507 {
508 WCHAR szPath[MAX_PATH];
509
510 TRACE("(%s,%d,%p)\n", debugstr_a(lpszPath), dwMode, lppStream);
511
512 if (!lpszPath)
513 return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
514
515 MultiByteToWideChar(CP_ACP, 0, lpszPath, -1, szPath, MAX_PATH);
516 return SHCreateStreamOnFileW(szPath, dwMode, lppStream);
517 }
518
519 /*************************************************************************
520 * @ [SHLWAPI.184]
521 *
522 * Call IStream_Read() on a stream.
523 *
524 * PARAMS
525 * lpStream [I] IStream object
526 * lpvDest [O] Destination for data read
527 * ulSize [I] Size of data to read
528 *
529 * RETURNS
530 * Success: S_OK. ulSize bytes have been read from the stream into lpvDest.
531 * Failure: An HRESULT error code, or E_FAIL if the read succeeded but the
532 * number of bytes read does not match.
533 */
534 HRESULT WINAPI SHIStream_Read(IStream *lpStream, LPVOID lpvDest, ULONG ulSize)
535 {
536 ULONG ulRead;
537 HRESULT hRet;
538
539 TRACE("(%p,%p,%d)\n", lpStream, lpvDest, ulSize);
540
541 hRet = IStream_Read(lpStream, lpvDest, ulSize, &ulRead);
542
543 if (SUCCEEDED(hRet) && ulRead != ulSize)
544 hRet = E_FAIL;
545 return hRet;
546 }
547
548 /*************************************************************************
549 * @ [SHLWAPI.166]
550 *
551 * Determine if a stream has 0 length.
552 *
553 * PARAMS
554 * lpStream [I] IStream object
555 *
556 * RETURNS
557 * TRUE: If the stream has 0 length
558 * FALSE: Otherwise.
559 */
560 BOOL WINAPI SHIsEmptyStream(IStream *lpStream)
561 {
562 STATSTG statstg;
563 BOOL bRet = TRUE;
564
565 TRACE("(%p)\n", lpStream);
566
567 memset(&statstg, 0, sizeof(statstg));
568
569 if(SUCCEEDED(IStream_Stat(lpStream, &statstg, 1)))
570 {
571 if(statstg.cbSize.QuadPart)
572 bRet = FALSE; /* Non-Zero */
573 }
574 else
575 {
576 DWORD dwDummy;
577
578 /* Try to read from the stream */
579 if(SUCCEEDED(SHIStream_Read(lpStream, &dwDummy, sizeof(dwDummy))))
580 {
581 LARGE_INTEGER zero;
582 zero.QuadPart = 0;
583
584 IStream_Seek(lpStream, zero, 0, NULL);
585 bRet = FALSE; /* Non-Zero */
586 }
587 }
588 return bRet;
589 }
590
591 /*************************************************************************
592 * @ [SHLWAPI.212]
593 *
594 * Call IStream_Write() on a stream.
595 *
596 * PARAMS
597 * lpStream [I] IStream object
598 * lpvSrc [I] Source for data to write
599 * ulSize [I] Size of data
600 *
601 * RETURNS
602 * Success: S_OK. ulSize bytes have been written to the stream from lpvSrc.
603 * Failure: An HRESULT error code, or E_FAIL if the write succeeded but the
604 * number of bytes written does not match.
605 */
606 HRESULT WINAPI SHIStream_Write(IStream *lpStream, LPCVOID lpvSrc, ULONG ulSize)
607 {
608 ULONG ulWritten;
609 HRESULT hRet;
610
611 TRACE("(%p,%p,%d)\n", lpStream, lpvSrc, ulSize);
612
613 hRet = IStream_Write(lpStream, lpvSrc, ulSize, &ulWritten);
614
615 if (SUCCEEDED(hRet) && ulWritten != ulSize)
616 hRet = E_FAIL;
617
618 return hRet;
619 }
620
621 /*************************************************************************
622 * @ [SHLWAPI.213]
623 *
624 * Seek to the start of a stream.
625 *
626 * PARAMS
627 * lpStream [I] IStream object
628 *
629 * RETURNS
630 * Success: S_OK. The current position within the stream is updated
631 * Failure: An HRESULT error code.
632 */
633 HRESULT WINAPI IStream_Reset(IStream *lpStream)
634 {
635 LARGE_INTEGER zero;
636 TRACE("(%p)\n", lpStream);
637 zero.QuadPart = 0;
638 return IStream_Seek(lpStream, zero, 0, NULL);
639 }
640
641 /*************************************************************************
642 * @ [SHLWAPI.214]
643 *
644 * Get the size of a stream.
645 *
646 * PARAMS
647 * lpStream [I] IStream object
648 * lpulSize [O] Destination for size
649 *
650 * RETURNS
651 * Success: S_OK. lpulSize contains the size of the stream.
652 * Failure: An HRESULT error code.
653 */
654 HRESULT WINAPI IStream_Size(IStream *lpStream, ULARGE_INTEGER* lpulSize)
655 {
656 STATSTG statstg;
657 HRESULT hRet;
658
659 TRACE("(%p,%p)\n", lpStream, lpulSize);
660
661 memset(&statstg, 0, sizeof(statstg));
662
663 hRet = IStream_Stat(lpStream, &statstg, 1);
664
665 if (SUCCEEDED(hRet) && lpulSize)
666 *lpulSize = statstg.cbSize;
667 return hRet;
668 }