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