1 /* Unit test suite for SHLWAPI ShCreateStreamOnFile functions.
2  *
3  * Copyright 2008 Reece H. Dunn
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #define WIN32_NO_STATUS
21 #define _INC_WINDOWS
22 #define COM_NO_WINDOWS_H
23 
24 #define COBJMACROS
25 
26 //#include <stdarg.h>
27 //#include <stdio.h>
28 
29 #include <wine/test.h>
30 //#include "windef.h"
31 //#include "winbase.h"
32 #include <winnls.h>
33 #include <winreg.h>
34 #include <objbase.h>
35 #include <shlwapi.h>
36 
37 static void test_IStream_invalid_operations(IStream * stream, DWORD mode)
38 {
39     HRESULT ret;
40     IStream * clone;
41     ULONG refcount;
42     ULARGE_INTEGER uzero;
43     ULARGE_INTEGER uret;
44     LARGE_INTEGER zero;
45     ULONG count;
46     char data[256];
47 
48     U(uzero).HighPart = 0;
49     U(uzero).LowPart = 0;
50     U(uret).HighPart = 0;
51     U(uret).LowPart = 0;
52     U(zero).HighPart = 0;
53     U(zero).LowPart = 0;
54 
55     /* IStream::Read */
56 
57     /* IStream_Read from the COBJMACROS is undefined by shlwapi.h, replaced by the IStream_Read helper function. */
58 
59     ret = stream->lpVtbl->Read(stream, NULL, 0, &count);
60     ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
61 
62     ret = stream->lpVtbl->Read(stream, data, 5, NULL);
63     ok(ret == S_FALSE || ret == S_OK, "expected S_FALSE or S_OK, got 0x%08x\n", ret);
64 
65     ret = stream->lpVtbl->Read(stream, data, 0, NULL);
66     ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
67 
68     ret = stream->lpVtbl->Read(stream, data, 3, &count);
69     ok(ret == S_FALSE || ret == S_OK, "expected S_FALSE or S_OK, got 0x%08x\n", ret);
70 
71     /* IStream::Write */
72 
73     /* IStream_Write from the COBJMACROS is undefined by shlwapi.h, replaced by the IStream_Write helper function. */
74 
75     ret = stream->lpVtbl->Write(stream, NULL, 0, &count);
76     if (mode == STGM_READ)
77     {
78         ok(ret == STG_E_ACCESSDENIED /* XP */ || broken(ret == S_OK) /* Win2000 + IE5 */,
79            "expected STG_E_ACCESSDENIED, got 0x%08x\n", ret);
80     }
81     else
82         ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
83 
84     strcpy(data, "Hello");
85     ret = stream->lpVtbl->Write(stream, data, 5, NULL);
86     if (mode == STGM_READ)
87         ok(ret == STG_E_ACCESSDENIED,
88            "expected STG_E_ACCESSDENIED, got 0x%08x\n", ret);
89     else
90         ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
91 
92     strcpy(data, "Hello");
93     ret = stream->lpVtbl->Write(stream, data, 0, NULL);
94     if (mode == STGM_READ)
95         ok(ret == STG_E_ACCESSDENIED,
96            "expected STG_E_ACCESSDENIED, got 0x%08x\n", ret);
97     else
98         ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
99 
100     strcpy(data, "Hello");
101     ret = stream->lpVtbl->Write(stream, data, 0, &count);
102     if (mode == STGM_READ)
103         ok(ret == STG_E_ACCESSDENIED,
104            "expected STG_E_ACCESSDENIED, got 0x%08x\n", ret);
105     else
106         ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
107 
108     strcpy(data, "Hello");
109     ret = stream->lpVtbl->Write(stream, data, 3, &count);
110     if (mode == STGM_READ)
111         ok(ret == STG_E_ACCESSDENIED,
112            "expected STG_E_ACCESSDENIED, got 0x%08x\n", ret);
113     else
114         ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
115 
116     /* IStream::Seek */
117 
118     ret = IStream_Seek(stream, zero, STREAM_SEEK_SET, NULL);
119     ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
120 
121     ret = IStream_Seek(stream, zero, 20, NULL);
122     ok(ret == E_INVALIDARG,
123        "expected E_INVALIDARG, got 0x%08x\n", ret);
124 
125     /* IStream::CopyTo */
126 
127     ret = IStream_CopyTo(stream, NULL, uzero, &uret, &uret);
128     ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
129 
130     clone = NULL;
131     ret = IStream_CopyTo(stream, clone, uzero, &uret, &uret);
132     ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
133 
134     ret = IStream_CopyTo(stream, stream, uzero, &uret, &uret);
135     ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
136 
137     ret = IStream_CopyTo(stream, stream, uzero, &uret, NULL);
138     ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
139 
140     ret = IStream_CopyTo(stream, stream, uzero, NULL, &uret);
141     ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
142 
143     /* IStream::Commit */
144 
145     ret = IStream_Commit(stream, STGC_DEFAULT);
146     ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
147 
148     /* IStream::Revert */
149 
150     ret = IStream_Revert(stream);
151     ok(ret == E_NOTIMPL, "expected E_NOTIMPL, got 0x%08x\n", ret);
152 
153     /* IStream::LockRegion */
154 
155     ret = IStream_LockRegion(stream, uzero, uzero, 0);
156     ok(ret == E_NOTIMPL /* XP */ || ret == S_OK /* Vista */,
157       "expected E_NOTIMPL or S_OK, got 0x%08x\n", ret);
158 
159     /* IStream::UnlockRegion */
160 
161     if (ret == E_NOTIMPL) /* XP */ {
162         ret = IStream_UnlockRegion(stream, uzero, uzero, 0);
163         ok(ret == E_NOTIMPL, "expected E_NOTIMPL, got 0x%08x\n", ret);
164     } else /* Vista */ {
165         ret = IStream_UnlockRegion(stream, uzero, uzero, 0);
166         ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
167 
168         ret = IStream_UnlockRegion(stream, uzero, uzero, 0);
169         ok(ret == STG_E_LOCKVIOLATION, "expected STG_E_LOCKVIOLATION, got 0x%08x\n", ret);
170     }
171 
172     /* IStream::Stat */
173 
174     ret = IStream_Stat(stream, NULL, 0);
175     ok(ret == STG_E_INVALIDPOINTER,
176        "expected STG_E_INVALIDPOINTER or E_NOTIMPL, got 0x%08x\n", ret);
177 
178     /* IStream::Clone */
179 
180     /* Passing a NULL pointer for the second IStream::Clone param crashes on Win7 */
181 
182     clone = NULL;
183     ret = IStream_Clone(stream, &clone);
184     ok(ret == E_NOTIMPL, "expected E_NOTIMPL, got 0x%08x\n", ret);
185     ok(clone == NULL, "expected a NULL IStream object, got %p\n", stream);
186 
187     if (clone) {
188         refcount = IStream_Release(clone);
189         ok(refcount == 0, "expected 0, got %d\n", refcount);
190     }
191 }
192 
193 
194 static void test_stream_read_write(IStream *stream, DWORD mode)
195 {
196     static const LARGE_INTEGER start;
197     HRESULT ret;
198     unsigned char buf[16];
199     DWORD written, count;
200 
201     /* IStream_Read/Write from the COBJMACROS is undefined by shlwapi.h */
202 
203     written = 0xdeadbeaf;
204     ret = stream->lpVtbl->Write(stream, "\x5e\xa7", 2, &written);
205     if (mode == STGM_WRITE || mode == STGM_READWRITE)
206     {
207         ok(ret == S_OK, "IStream_Write error %#x (access %#x)\n", ret, mode);
208         ok(written == 2, "expected 2, got %u\n", written);
209     }
210     else
211     {
212         ok(ret == STG_E_ACCESSDENIED || broken(ret == S_OK) /* win2000 */, "expected STG_E_ACCESSDENIED, got %#x (access %#x)\n", ret, mode);
213         ok(written == 0xdeadbeaf || broken(written == 2) /* win2000 */, "expected 0xdeadbeaf, got %#x\n", written);
214         written = 0;
215         if (ret == S_OK) return; /* no point in further testing */
216     }
217 
218     ret = stream->lpVtbl->Seek(stream, start, STREAM_SEEK_SET, NULL);
219     ok(ret == S_OK, "Seek error %#x\n", ret);
220 
221     count = 0xdeadbeaf;
222     ret = stream->lpVtbl->Read(stream, buf, 2, &count);
223     if (written != 0)
224     {
225         ok(ret == S_OK || broken(ret == S_FALSE) /* win2000 */, "IStream_Read error %#x (access %#x, written %u)\n", ret, mode, written);
226         if (ret == S_OK && (mode == STGM_WRITE || mode == STGM_READWRITE))
227         {
228             ok(count == 2, "expected 2, got %u\n", count);
229             ok(buf[0] == 0x5e && buf[1] == 0xa7, "expected 5ea7, got %02x%02x\n", buf[0], buf[1]);
230         }
231         else
232             ok(count == 0, "expected 0, got %u\n", count);
233     }
234     else
235     {
236         ok(ret == S_FALSE, "expected S_FALSE, got %#x (access %#x, written %u)\n", ret, mode, written);
237         ok(count == 0, "expected 0, got %u\n", count);
238     }
239 
240     ret = stream->lpVtbl->Seek(stream, start, STREAM_SEEK_SET, NULL);
241     ok(ret == S_OK, "Seek error %#x\n", ret);
242 
243     count = 0xdeadbeaf;
244     ret = stream->lpVtbl->Read(stream, buf, 0, &count);
245     ok(ret == S_OK, "IStream_Read error %#x (access %#x, written %u)\n", ret, mode, written);
246     ok(count == 0, "expected 0, got %u\n", count);
247 
248     count = 0xdeadbeaf;
249     ret = stream->lpVtbl->Read(stream, buf, sizeof(buf), &count);
250     ok(ret == S_FALSE, "expected S_FALSE, got %#x (access %#x, written %u)\n", ret, mode, written);
251     ok(count == written, "expected %u, got %u\n", written, count);
252     if (count)
253         ok(buf[0] == 0x5e && buf[1] == 0xa7, "expected 5ea7, got %02x%02x\n", buf[0], buf[1]);
254 }
255 
256 static void test_SHCreateStreamOnFileA(DWORD mode, DWORD stgm)
257 {
258     IStream * stream;
259     HRESULT ret;
260     ULONG refcount;
261     char test_file[MAX_PATH];
262     static const CHAR testA_txt[] = "\\testA.txt";
263 
264     trace("SHCreateStreamOnFileA: testing mode %d, STGM flags %08x\n", mode, stgm);
265 
266     /* Don't used a fixed path for the testA.txt file */
267     GetTempPathA(MAX_PATH, test_file);
268     lstrcatA(test_file, testA_txt);
269 
270     /* invalid arguments */
271 
272     stream = NULL;
273     ret = SHCreateStreamOnFileA(NULL, mode | stgm, &stream);
274     if (ret == E_INVALIDARG) /* Win98 SE */ {
275         win_skip("Not supported\n");
276         return;
277     }
278 
279     ok(ret == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) /* NT */ ||
280        ret == HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME) /* 9x */,
281        "SHCreateStreamOnFileA: expected HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) "
282        "or HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME), got 0x%08x\n", ret);
283     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
284 
285 if (0) /* This test crashes on WinXP SP2 */
286 {
287     ret = SHCreateStreamOnFileA(test_file, mode | stgm, NULL);
288     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileA: expected E_INVALIDARG, got 0x%08x\n", ret);
289 }
290 
291     stream = NULL;
292     ret = SHCreateStreamOnFileA(test_file, mode | STGM_CONVERT | stgm, &stream);
293     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileA: expected E_INVALIDARG, got 0x%08x\n", ret);
294     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
295 
296     stream = NULL;
297     ret = SHCreateStreamOnFileA(test_file, mode | STGM_DELETEONRELEASE | stgm, &stream);
298     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileA: expected E_INVALIDARG, got 0x%08x\n", ret);
299     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
300 
301     stream = NULL;
302     ret = SHCreateStreamOnFileA(test_file, mode | STGM_TRANSACTED | stgm, &stream);
303     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileA: expected E_INVALIDARG, got 0x%08x\n", ret);
304     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
305 
306     /* file does not exist */
307 
308     stream = NULL;
309     ret = SHCreateStreamOnFileA(test_file, mode | STGM_FAILIFTHERE | stgm, &stream);
310     ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "SHCreateStreamOnFileA: expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got 0x%08x\n", ret);
311     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
312 
313     stream = NULL;
314     ret = SHCreateStreamOnFileA(test_file, mode | STGM_CREATE | stgm, &stream);
315     ok(ret == S_OK, "SHCreateStreamOnFileA: expected S_OK, got 0x%08x\n", ret);
316     ok(stream != NULL, "SHCreateStreamOnFileA: expected a valid IStream object, got NULL\n");
317 
318     if (stream) {
319         test_IStream_invalid_operations(stream, mode);
320 
321         refcount = IStream_Release(stream);
322         ok(refcount == 0, "SHCreateStreamOnFileA: expected 0, got %d\n", refcount);
323     }
324 
325     /* NOTE: don't delete the file, as it will be used for the file exists tests. */
326 
327     /* file exists */
328 
329     stream = NULL;
330     ret = SHCreateStreamOnFileA(test_file, mode | STGM_FAILIFTHERE | stgm, &stream);
331     ok(ret == S_OK, "SHCreateStreamOnFileA: expected S_OK, got 0x%08x\n", ret);
332     ok(stream != NULL, "SHCreateStreamOnFileA: expected a valid IStream object, got NULL\n");
333 
334     if (stream) {
335         test_IStream_invalid_operations(stream, mode);
336 
337         refcount = IStream_Release(stream);
338         ok(refcount == 0, "SHCreateStreamOnFileA: expected 0, got %d\n", refcount);
339     }
340 
341     stream = NULL;
342     ret = SHCreateStreamOnFileA(test_file, mode | STGM_CREATE | stgm, &stream);
343     ok(ret == S_OK, "SHCreateStreamOnFileA: expected S_OK, got 0x%08x\n", ret);
344     ok(stream != NULL, "SHCreateStreamOnFileA: expected a valid IStream object, got NULL\n");
345 
346     if (stream) {
347         BOOL delret;
348 
349         test_stream_read_write(stream, mode);
350         test_IStream_invalid_operations(stream, mode);
351 
352         refcount = IStream_Release(stream);
353         ok(refcount == 0, "SHCreateStreamOnFileA: expected 0, got %d\n", refcount);
354 
355         delret = DeleteFileA(test_file);
356         ok(delret, "SHCreateStreamOnFileA: could not delete file '%s', got error %d\n",
357            test_file, GetLastError());
358     }
359 }
360 
361 
362 static void test_SHCreateStreamOnFileW(DWORD mode, DWORD stgm)
363 {
364     IStream * stream;
365     HRESULT ret;
366     ULONG refcount;
367     WCHAR test_file[MAX_PATH];
368     CHAR  test_fileA[MAX_PATH];
369     static const CHAR testW_txt[] = "\\testW.txt";
370 
371     trace("SHCreateStreamOnFileW: testing mode %d, STGM flags %08x\n", mode, stgm);
372 
373     /* Don't used a fixed path for the testW.txt file */
374     GetTempPathA(MAX_PATH, test_fileA);
375     lstrcatA(test_fileA, testW_txt);
376     MultiByteToWideChar(CP_ACP, 0, test_fileA, -1, test_file, MAX_PATH);
377 
378     /* invalid arguments */
379 
380     if (0)
381     {
382         /* Crashes on NT4 */
383         stream = NULL;
384         ret = SHCreateStreamOnFileW(NULL, mode | stgm, &stream);
385         ok(ret == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) || /* XP */
386            ret == E_INVALIDARG /* Vista */,
387           "SHCreateStreamOnFileW: expected HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) or E_INVALIDARG, got 0x%08x\n", ret);
388         ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
389     }
390 
391     if (0)
392     {
393         /* This test crashes on WinXP SP2 */
394             ret = SHCreateStreamOnFileW(test_file, mode | stgm, NULL);
395             ok(ret == E_INVALIDARG, "SHCreateStreamOnFileW: expected E_INVALIDARG, got 0x%08x\n", ret);
396     }
397 
398     stream = NULL;
399     ret = SHCreateStreamOnFileW(test_file, mode | STGM_CONVERT | stgm, &stream);
400     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileW: expected E_INVALIDARG, got 0x%08x\n", ret);
401     ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
402 
403     stream = NULL;
404     ret = SHCreateStreamOnFileW(test_file, mode | STGM_DELETEONRELEASE | stgm, &stream);
405     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileW: expected E_INVALIDARG, got 0x%08x\n", ret);
406     ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
407 
408     stream = NULL;
409     ret = SHCreateStreamOnFileW(test_file, mode | STGM_TRANSACTED | stgm, &stream);
410     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileW: expected E_INVALIDARG, got 0x%08x\n", ret);
411     ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
412 
413     /* file does not exist */
414 
415     stream = NULL;
416     ret = SHCreateStreamOnFileW(test_file, mode | STGM_FAILIFTHERE | stgm, &stream);
417     if (ret == E_INVALIDARG) /* Win98 SE */ {
418         win_skip("Not supported\n");
419         return;
420     }
421 
422     ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "SHCreateStreamOnFileW: expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got 0x%08x\n", ret);
423     ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
424 
425     stream = NULL;
426     ret = SHCreateStreamOnFileW(test_file, mode | STGM_CREATE | stgm, &stream);
427     ok(ret == S_OK, "SHCreateStreamOnFileW: expected S_OK, got 0x%08x\n", ret);
428     ok(stream != NULL, "SHCreateStreamOnFileW: expected a valid IStream object, got NULL\n");
429 
430     if (stream) {
431         test_IStream_invalid_operations(stream, mode);
432 
433         refcount = IStream_Release(stream);
434         ok(refcount == 0, "SHCreateStreamOnFileW: expected 0, got %d\n", refcount);
435     }
436 
437     /* NOTE: don't delete the file, as it will be used for the file exists tests. */
438 
439     /* file exists */
440 
441     stream = NULL;
442     ret = SHCreateStreamOnFileW(test_file, mode | STGM_FAILIFTHERE | stgm, &stream);
443     ok(ret == S_OK, "SHCreateStreamOnFileW: expected S_OK, got 0x%08x\n", ret);
444     ok(stream != NULL, "SHCreateStreamOnFileW: expected a valid IStream object, got NULL\n");
445 
446     if (stream) {
447         test_IStream_invalid_operations(stream, mode);
448 
449         refcount = IStream_Release(stream);
450         ok(refcount == 0, "SHCreateStreamOnFileW: expected 0, got %d\n", refcount);
451     }
452 
453     stream = NULL;
454     ret = SHCreateStreamOnFileW(test_file, mode | STGM_CREATE | stgm, &stream);
455     ok(ret == S_OK, "SHCreateStreamOnFileW: expected S_OK, got 0x%08x\n", ret);
456     ok(stream != NULL, "SHCreateStreamOnFileW: expected a valid IStream object, got NULL\n");
457 
458     if (stream) {
459         BOOL delret;
460 
461         test_stream_read_write(stream, mode);
462         test_IStream_invalid_operations(stream, mode);
463 
464         refcount = IStream_Release(stream);
465         ok(refcount == 0, "SHCreateStreamOnFileW: expected 0, got %d\n", refcount);
466 
467         delret = DeleteFileA(test_fileA);
468         ok(delret, "SHCreateStreamOnFileW: could not delete the test file, got error %d\n",
469            GetLastError());
470     }
471 }
472 
473 
474 static void test_SHCreateStreamOnFileEx(DWORD mode, DWORD stgm)
475 {
476     IStream * stream;
477     IStream * template = NULL;
478     HRESULT ret;
479     ULONG refcount;
480     WCHAR test_file[MAX_PATH];
481     CHAR  test_fileA[MAX_PATH];
482     static const CHAR testEx_txt[] = "\\testEx.txt";
483     BOOL delret;
484 
485     if (winetest_debug > 1)
486         trace("SHCreateStreamOnFileEx: testing mode %d, STGM flags %08x\n", mode, stgm);
487 
488     /* Don't used a fixed path for the testEx.txt file */
489     GetTempPathA(MAX_PATH, test_fileA);
490     lstrcatA(test_fileA, testEx_txt);
491     MultiByteToWideChar(CP_ACP, 0, test_fileA, -1, test_file, MAX_PATH);
492 
493     /* invalid arguments */
494 
495     if (0)
496     {
497         /* Crashes on NT4 */
498         stream = NULL;
499         ret = SHCreateStreamOnFileEx(NULL, mode, 0, FALSE, NULL, &stream);
500         ok(ret == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) || /* XP */
501            ret == E_INVALIDARG /* Vista */,
502           "SHCreateStreamOnFileEx: expected HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) or E_INVALIDARG, got 0x%08x\n", ret);
503         ok(stream == NULL, "SHCreateStreamOnFileEx: expected a NULL IStream object, got %p\n", stream);
504     }
505 
506     stream = NULL;
507     ret = SHCreateStreamOnFileEx(test_file, mode, 0, FALSE, template, &stream);
508     if (ret == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) {
509         win_skip("File probably locked by Anti-Virus/Spam software, trying again\n");
510         Sleep(1000);
511         ret = SHCreateStreamOnFileEx(test_file, mode, 0, FALSE, template, &stream);
512     }
513     ok( ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
514         ret == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
515         "SHCreateStreamOnFileEx: expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) or "
516         "HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got 0x%08x\n", ret);
517 
518     ok(stream == NULL, "SHCreateStreamOnFileEx: expected a NULL IStream object, got %p\n", stream);
519 
520     if (0)
521     {
522         /* This test crashes on WinXP SP2 */
523         ret = SHCreateStreamOnFileEx(test_file, mode, 0, FALSE, NULL, NULL);
524         ok(ret == E_INVALIDARG, "SHCreateStreamOnFileEx: expected E_INVALIDARG, got 0x%08x\n", ret);
525     }
526 
527     /* file does not exist */
528 
529     stream = NULL;
530     ret = SHCreateStreamOnFileEx(test_file, mode | STGM_FAILIFTHERE | stgm, 0, FALSE, NULL, &stream);
531     if ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED && mode == STGM_READ) {
532         ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) /* XP */ || ret == E_INVALIDARG /* Vista */,
533           "SHCreateStreamOnFileEx: expected E_INVALIDARG or HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got 0x%08x\n", ret);
534 
535         if (ret == E_INVALIDARG) {
536             skip("SHCreateStreamOnFileEx: STGM_TRANSACTED not supported in this configuration.\n");
537             return;
538         }
539     } else {
540         ok( ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
541             ret == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
542             "SHCreateStreamOnFileEx: expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) or "
543             "HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got 0x%08x\n", ret);
544     }
545     ok(stream == NULL, "SHCreateStreamOnFileEx: expected a NULL IStream object, got %p\n", stream);
546 
547     stream = NULL;
548     ret = SHCreateStreamOnFileEx(test_file, mode | STGM_FAILIFTHERE | stgm, 0, TRUE, NULL, &stream);
549     /* not supported on win9x */
550     if (broken(ret == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) && stream == NULL)) {
551         skip("Not supported\n");
552         DeleteFileA(test_fileA);
553         return;
554     }
555 
556     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
557     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
558 
559     if (stream) {
560         test_IStream_invalid_operations(stream, mode);
561 
562         refcount = IStream_Release(stream);
563         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
564 
565         delret = DeleteFileA(test_fileA);
566         ok(delret, "SHCreateStreamOnFileEx: could not delete the test file, got error %d\n",
567            GetLastError());
568     }
569 
570     stream = NULL;
571     ret = SHCreateStreamOnFileEx(test_file, mode | STGM_CREATE | stgm, 0, FALSE, NULL, &stream);
572     if (ret == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) {
573         win_skip("File probably locked by Anti-Virus/Spam software, trying again\n");
574         Sleep(1000);
575         ret = SHCreateStreamOnFileEx(test_file, mode | STGM_CREATE | stgm, 0, FALSE, NULL, &stream);
576     }
577     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
578     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
579 
580     if (stream) {
581         test_IStream_invalid_operations(stream, mode);
582 
583         refcount = IStream_Release(stream);
584         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
585 
586         delret = DeleteFileA(test_fileA);
587         ok(delret, "SHCreateStreamOnFileEx: could not delete the test file, got error %d\n",
588            GetLastError());
589     }
590 
591     stream = NULL;
592     ret = SHCreateStreamOnFileEx(test_file, mode | STGM_CREATE | stgm, 0, TRUE, NULL, &stream);
593     if (ret == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) {
594         win_skip("File probably locked by Anti-Virus/Spam software, trying again\n");
595         Sleep(1000);
596         ret = SHCreateStreamOnFileEx(test_file, mode | STGM_CREATE | stgm, 0, TRUE, NULL, &stream);
597     }
598     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
599     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
600 
601     if (stream) {
602         test_IStream_invalid_operations(stream, mode);
603 
604         refcount = IStream_Release(stream);
605         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
606     }
607 
608     /* NOTE: don't delete the file, as it will be used for the file exists tests. */
609 
610     /* file exists */
611 
612     stream = NULL;
613     ret = SHCreateStreamOnFileEx(test_file, mode | STGM_FAILIFTHERE | stgm, 0, FALSE, NULL, &stream);
614     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
615     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
616 
617     if (stream) {
618         test_IStream_invalid_operations(stream, mode);
619 
620         refcount = IStream_Release(stream);
621         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
622     }
623 
624     stream = NULL;
625     ret = SHCreateStreamOnFileEx(test_file, mode | STGM_FAILIFTHERE | stgm, 0, TRUE, NULL, &stream);
626     ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS), "SHCreateStreamOnFileEx: expected HRESULT_FROM_WIN32(ERROR_FILE_EXISTS), got 0x%08x\n", ret);
627     ok(stream == NULL, "SHCreateStreamOnFileEx: expected a NULL IStream object, got %p\n", stream);
628 
629     stream = NULL;
630     ret = SHCreateStreamOnFileEx(test_file, mode | STGM_CREATE | stgm, 0, FALSE, NULL, &stream);
631     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
632     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
633 
634     if (stream) {
635         test_IStream_invalid_operations(stream, mode);
636 
637         refcount = IStream_Release(stream);
638         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
639     }
640 
641     stream = NULL;
642     ret = SHCreateStreamOnFileEx(test_file, mode | STGM_CREATE | stgm, 0, TRUE, NULL, &stream);
643     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
644     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
645 
646     if (stream) {
647         test_IStream_invalid_operations(stream, mode);
648 
649         refcount = IStream_Release(stream);
650         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
651     }
652 
653     delret = DeleteFileA(test_fileA);
654     ok(delret, "SHCreateStreamOnFileEx: could not delete the test file, got error %d\n",
655        GetLastError());
656 }
657 
658 
659 static void test_SHCreateStreamOnFileEx_CopyTo(void)
660 {
661     HRESULT ret;
662     IStream *src, *dst;
663     WCHAR tmpPath[MAX_PATH];
664     WCHAR srcFileName[MAX_PATH];
665     WCHAR dstFileName[MAX_PATH];
666     ULARGE_INTEGER count, read, written;
667     LARGE_INTEGER distance;
668     static const char srcContents[1];
669     static const WCHAR prefix[] = { 'T', 'S', 'T', 0 };
670 
671     GetTempPathW(MAX_PATH, tmpPath);
672     GetTempFileNameW(tmpPath, prefix, 0, srcFileName);
673     GetTempFileNameW(tmpPath, prefix, 0, dstFileName);
674 
675     ret = SHCreateStreamOnFileEx(srcFileName, STGM_CREATE | STGM_READWRITE | STGM_DELETEONRELEASE, FILE_ATTRIBUTE_TEMPORARY, FALSE, NULL, &src);
676     ok(SUCCEEDED(ret), "SHCreateStreamOnFileEx failed with ret=0x%08x\n", ret);
677 
678     written.QuadPart = 0;
679     ret = IStream_Write(src, srcContents, sizeof(srcContents), &U(written).LowPart);
680     ok(SUCCEEDED(ret), "ISequentialStream_Write failed with ret=0x%08x\n", ret);
681 
682     distance.QuadPart = 0;
683     ret = IStream_Seek(src, distance, STREAM_SEEK_SET, &written);
684     ok(SUCCEEDED(ret), "ISequentialStream_Seek failed with ret=0x%08x\n", ret);
685 
686     ret = SHCreateStreamOnFileEx(dstFileName, STGM_CREATE | STGM_READWRITE | STGM_DELETEONRELEASE, FILE_ATTRIBUTE_TEMPORARY, FALSE, NULL, &dst);
687     ok(SUCCEEDED(ret), "SHCreateStreamOnFileEx failed with ret=0x%08x\n", ret);
688 
689     /* Test using a count larger than the source file, so that the Read operation will fall short */
690     count.QuadPart = 2;
691 
692     ret = IStream_CopyTo(src, dst, count, &read, &written);
693     ok(SUCCEEDED(ret), "CopyTo failed with ret=0x%08x\n", ret);
694 
695     ok(read.QuadPart == 1, "read does not match size: %d != 1\n", U(read).LowPart);
696     ok(written.QuadPart == 1, "written does not match size: %d != 1\n", U(written).LowPart);
697 
698     IStream_Release(dst);
699     IStream_Release(src);
700     DeleteFileW( srcFileName );
701     DeleteFileW( dstFileName );
702 }
703 
704 
705 START_TEST(istream)
706 {
707     static const DWORD stgm_access[] = {
708         STGM_READ,
709         STGM_WRITE,
710         STGM_READWRITE
711     };
712 
713     static const DWORD stgm_sharing[] = {
714         0,
715         STGM_SHARE_DENY_NONE,
716         STGM_SHARE_DENY_READ,
717         STGM_SHARE_DENY_WRITE,
718         STGM_SHARE_EXCLUSIVE
719     };
720 
721     static const DWORD stgm_flags[] = {
722         0,
723         STGM_CONVERT,
724         STGM_DELETEONRELEASE,
725         STGM_CONVERT | STGM_DELETEONRELEASE,
726         STGM_TRANSACTED | STGM_CONVERT,
727         STGM_TRANSACTED | STGM_DELETEONRELEASE,
728         STGM_TRANSACTED | STGM_CONVERT | STGM_DELETEONRELEASE
729     };
730 
731     int i, j, k;
732 
733     for (i = 0; i != sizeof(stgm_access)/sizeof(stgm_access[0]); i++) {
734         for (j = 0; j != sizeof(stgm_sharing)/sizeof(stgm_sharing[0]); j ++) {
735             test_SHCreateStreamOnFileA(stgm_access[i], stgm_sharing[j]);
736             test_SHCreateStreamOnFileW(stgm_access[i], stgm_sharing[j]);
737 
738             for (k = 0; k != sizeof(stgm_flags)/sizeof(stgm_flags[0]); k++)
739                 test_SHCreateStreamOnFileEx(stgm_access[i], stgm_sharing[j] | stgm_flags[k]);
740         }
741     }
742 
743     test_SHCreateStreamOnFileEx_CopyTo();
744 }
745