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