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