shlwapi/tests: Add more entries for PathIsURL.
[wine] / dlls / shlwapi / tests / istream.c
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
32 /* Function pointers for the SHCreateStreamOnFile functions */
33 static HMODULE hShlwapi;
34 static HRESULT (WINAPI *pSHCreateStreamOnFileA)(LPCSTR file, DWORD mode, IStream **stream);
35 static HRESULT (WINAPI *pSHCreateStreamOnFileW)(LPCWSTR file, DWORD mode, IStream **stream);
36 static HRESULT (WINAPI *pSHCreateStreamOnFileEx)(LPCWSTR file, DWORD mode, DWORD attributes, BOOL create, IStream *template, IStream **stream);
37
38
39 static void test_IStream_invalid_operations(IStream * stream, DWORD mode)
40 {
41     HRESULT ret;
42     IStream * clone;
43     ULONG refcount;
44     ULARGE_INTEGER uzero;
45     ULARGE_INTEGER uret;
46     LARGE_INTEGER zero;
47     ULONG count;
48     char data[256];
49
50     U(uzero).HighPart = 0;
51     U(uzero).LowPart = 0;
52     U(uret).HighPart = 0;
53     U(uret).LowPart = 0;
54     U(zero).HighPart = 0;
55     U(zero).LowPart = 0;
56
57     /* IStream::Read */
58
59     /* IStream_Read from the COBJMACROS is undefined by shlwapi.h, replaced by the IStream_Read helper function. */
60
61     ret = stream->lpVtbl->Read(stream, NULL, 0, &count);
62     ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
63
64     ret = stream->lpVtbl->Read(stream, data, 5, NULL);
65     ok(ret == S_FALSE || ret == S_OK, "expected S_FALSE or S_OK, got 0x%08x\n", ret);
66
67     ret = stream->lpVtbl->Read(stream, data, 0, NULL);
68     ok(ret == S_OK, "expected S_OK, got 0x%08x\n", ret);
69
70     ret = stream->lpVtbl->Read(stream, data, 3, &count);
71     ok(ret == S_FALSE || ret == S_OK, "expected S_FALSE or S_OK, got 0x%08x\n", ret);
72
73     /* IStream::Write */
74
75     /* IStream_Write from the COBJMACROS is undefined by shlwapi.h, replaced by the IStream_Write helper function. */
76
77     ret = stream->lpVtbl->Write(stream, NULL, 0, &count);
78     if (mode == STGM_READ)
79         ok(ret == STG_E_ACCESSDENIED /* XP */ || ret == S_OK /* 2000 */,
80            "expected STG_E_ACCESSDENIED or S_OK, got 0x%08x\n", ret);
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 /* XP */ || ret == S_OK /* 2000 */,
88            "expected STG_E_ACCESSDENIED or S_OK, 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 /* XP */ || ret == S_OK /* 2000 */,
96            "expected STG_E_ACCESSDENIED or S_OK, 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 /* XP */ || ret == S_OK /* 2000 */,
104            "expected STG_E_ACCESSDENIED or S_OK, 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 /* XP */ || ret == S_OK /* 2000 */,
112            "expected STG_E_ACCESSDENIED or S_OK, 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 /* XP */ || ret == S_OK /* 2000 */,
123        "expected E_INVALIDARG or S_OK, 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 /* XP */ || ret == E_NOTIMPL /* 2000 */,
176        "expected STG_E_INVALIDPOINTER or E_NOTIMPL, got 0x%08x\n", ret);
177
178     /* IStream::Clone */
179
180     ret = IStream_Clone(stream, NULL);
181     ok(ret == E_NOTIMPL, "expected E_NOTIMPL, got 0x%08x\n", ret);
182
183     clone = NULL;
184     ret = IStream_Clone(stream, &clone);
185     ok(ret == E_NOTIMPL, "expected E_NOTIMPL, got 0x%08x\n", ret);
186     ok(clone == NULL, "expected a NULL IStream object, got %p\n", stream);
187
188     if (clone) {
189         refcount = IStream_Release(clone);
190         ok(refcount == 0, "expected 0, got %d\n", refcount);
191     }
192 }
193
194
195 static void test_SHCreateStreamOnFileA(DWORD mode, DWORD stgm)
196 {
197     IStream * stream;
198     HRESULT ret;
199     ULONG refcount;
200     char test_file[MAX_PATH];
201     static const CHAR testA_txt[] = "\\testA.txt";
202
203     trace("SHCreateStreamOnFileA: testing mode %d, STGM flags %08x\n", mode, stgm);
204
205     /* Don't used a fixed path for the testA.txt file */
206     GetTempPathA(MAX_PATH, test_file);
207     lstrcatA(test_file, testA_txt);
208
209     /* invalid arguments */
210
211     stream = NULL;
212     /* NT: ERROR_PATH_NOT_FOUND, 9x: ERROR_BAD_PATHNAME */
213     ret = (*pSHCreateStreamOnFileA)(NULL, mode | stgm, &stream);
214     ok(ret == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) ||
215         ret == HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME),
216         "SHCreateStreamOnFileA: expected HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)"
217         "or HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME), got 0x%08x\n", ret);
218     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
219
220 #if 0 /* This test crashes on WinXP SP2 */
221     ret = (*pSHCreateStreamOnFileA)(test_file, mode | stgm, NULL);
222     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileA: expected E_INVALIDARG, got 0x%08x\n", ret);
223 #endif
224
225     stream = NULL;
226     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_CONVERT | stgm, &stream);
227     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileA: expected E_INVALIDARG, got 0x%08x\n", ret);
228     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
229
230     stream = NULL;
231     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_DELETEONRELEASE | stgm, &stream);
232     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileA: expected E_INVALIDARG, got 0x%08x\n", ret);
233     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
234
235     stream = NULL;
236     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_TRANSACTED | stgm, &stream);
237     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileA: expected E_INVALIDARG, got 0x%08x\n", ret);
238     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
239
240     /* file does not exist */
241
242     stream = NULL;
243     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_FAILIFTHERE | stgm, &stream);
244     ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "SHCreateStreamOnFileA: expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got 0x%08x\n", ret);
245     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
246
247     stream = NULL;
248     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_CREATE | stgm, &stream);
249     ok(ret == S_OK, "SHCreateStreamOnFileA: expected S_OK, got 0x%08x\n", ret);
250     ok(stream != NULL, "SHCreateStreamOnFileA: expected a valid IStream object, got NULL\n");
251
252     if (stream) {
253         test_IStream_invalid_operations(stream, mode);
254
255         refcount = IStream_Release(stream);
256         ok(refcount == 0, "SHCreateStreamOnFileA: expected 0, got %d\n", refcount);
257     }
258
259     /* NOTE: don't delete the file, as it will be used for the file exists tests. */
260
261     /* file exists */
262
263     stream = NULL;
264     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_FAILIFTHERE | stgm, &stream);
265     ok(ret == S_OK, "SHCreateStreamOnFileA: expected S_OK, got 0x%08x\n", ret);
266     ok(stream != NULL, "SHCreateStreamOnFileA: expected a valid IStream object, got NULL\n");
267
268     if (stream) {
269         test_IStream_invalid_operations(stream, mode);
270
271         refcount = IStream_Release(stream);
272         ok(refcount == 0, "SHCreateStreamOnFileA: expected 0, got %d\n", refcount);
273     }
274
275     stream = NULL;
276     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_CREATE | stgm, &stream);
277     ok(ret == S_OK, "SHCreateStreamOnFileA: expected S_OK, got 0x%08x\n", ret);
278     ok(stream != NULL, "SHCreateStreamOnFileA: expected a valid IStream object, got NULL\n");
279
280     if (stream) {
281         test_IStream_invalid_operations(stream, mode);
282
283         refcount = IStream_Release(stream);
284         ok(refcount == 0, "SHCreateStreamOnFileA: expected 0, got %d\n", refcount);
285
286         ok(DeleteFileA(test_file), "SHCreateStreamOnFileA: could not delete file '%s', got error %d\n", test_file, GetLastError());
287     }
288 }
289
290
291 static void test_SHCreateStreamOnFileW(DWORD mode, DWORD stgm)
292 {
293     IStream * stream;
294     HRESULT ret;
295     ULONG refcount;
296     WCHAR test_file[MAX_PATH];
297     CHAR  test_fileA[MAX_PATH];
298     static const CHAR testW_txt[] = "\\testW.txt";
299
300     trace("SHCreateStreamOnFileW: testing mode %d, STGM flags %08x\n", mode, stgm);
301
302     /* Don't used a fixed path for the testW.txt file */
303     GetTempPathA(MAX_PATH, test_fileA);
304     lstrcatA(test_fileA, testW_txt);
305     MultiByteToWideChar(CP_ACP, 0, test_fileA, -1, test_file, MAX_PATH);
306
307     /* invalid arguments */
308
309     if (0)
310     {
311         /* Crashes on NT4 */
312         stream = NULL;
313         ret = (*pSHCreateStreamOnFileW)(NULL, mode | stgm, &stream);
314         ok(ret == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) || /* XP */
315            ret == E_INVALIDARG /* Vista */,
316           "SHCreateStreamOnFileW: expected HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) or E_INVALIDARG, got 0x%08x\n", ret);
317         ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
318     }
319
320     if (0)
321     {
322         /* This test crashes on WinXP SP2 */
323             ret = (*pSHCreateStreamOnFileW)(test_file, mode | stgm, NULL);
324             ok(ret == E_INVALIDARG, "SHCreateStreamOnFileW: expected E_INVALIDARG, got 0x%08x\n", ret);
325     }
326
327     stream = NULL;
328     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_CONVERT | stgm, &stream);
329     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileW: expected E_INVALIDARG, got 0x%08x\n", ret);
330     ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
331
332     stream = NULL;
333     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_DELETEONRELEASE | stgm, &stream);
334     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileW: expected E_INVALIDARG, got 0x%08x\n", ret);
335     ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
336
337     stream = NULL;
338     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_TRANSACTED | stgm, &stream);
339     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileW: expected E_INVALIDARG, got 0x%08x\n", ret);
340     ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
341
342     /* file does not exist */
343
344     stream = NULL;
345     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_FAILIFTHERE | stgm, &stream);
346     ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "SHCreateStreamOnFileW: expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got 0x%08x\n", ret);
347     ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
348
349     stream = NULL;
350     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_CREATE | stgm, &stream);
351     ok(ret == S_OK, "SHCreateStreamOnFileW: expected S_OK, got 0x%08x\n", ret);
352     ok(stream != NULL, "SHCreateStreamOnFileW: expected a valid IStream object, got NULL\n");
353
354     if (stream) {
355         test_IStream_invalid_operations(stream, mode);
356
357         refcount = IStream_Release(stream);
358         ok(refcount == 0, "SHCreateStreamOnFileW: expected 0, got %d\n", refcount);
359     }
360
361     /* NOTE: don't delete the file, as it will be used for the file exists tests. */
362
363     /* file exists */
364
365     stream = NULL;
366     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_FAILIFTHERE | stgm, &stream);
367     ok(ret == S_OK, "SHCreateStreamOnFileW: expected S_OK, got 0x%08x\n", ret);
368     ok(stream != NULL, "SHCreateStreamOnFileW: expected a valid IStream object, got NULL\n");
369
370     if (stream) {
371         test_IStream_invalid_operations(stream, mode);
372
373         refcount = IStream_Release(stream);
374         ok(refcount == 0, "SHCreateStreamOnFileW: expected 0, got %d\n", refcount);
375     }
376
377     stream = NULL;
378     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_CREATE | stgm, &stream);
379     ok(ret == S_OK, "SHCreateStreamOnFileW: expected S_OK, got 0x%08x\n", ret);
380     ok(stream != NULL, "SHCreateStreamOnFileW: expected a valid IStream object, got NULL\n");
381
382     if (stream) {
383         test_IStream_invalid_operations(stream, mode);
384
385         refcount = IStream_Release(stream);
386         ok(refcount == 0, "SHCreateStreamOnFileW: expected 0, got %d\n", refcount);
387
388         ok(DeleteFileA(test_fileA),
389             "SHCreateStreamOnFileW: could not delete the test file, got error %d\n",
390             GetLastError());
391     }
392 }
393
394
395 static void test_SHCreateStreamOnFileEx(DWORD mode, DWORD stgm)
396 {
397     IStream * stream;
398     IStream * template = NULL;
399     HRESULT ret;
400     ULONG refcount;
401     WCHAR test_file[MAX_PATH];
402     CHAR  test_fileA[MAX_PATH];
403     static const CHAR testEx_txt[] = "\\testEx.txt";
404
405     trace("SHCreateStreamOnFileEx: testing mode %d, STGM flags %08x\n", mode, stgm);
406
407     /* Don't used a fixed path for the testEx.txt file */
408     GetTempPathA(MAX_PATH, test_fileA);
409     lstrcatA(test_fileA, testEx_txt);
410     MultiByteToWideChar(CP_ACP, 0, test_fileA, -1, test_file, MAX_PATH);
411
412     /* invalid arguments */
413
414     if (0)
415     {
416         /* Crashes on NT4 */
417         stream = NULL;
418         ret = (*pSHCreateStreamOnFileEx)(NULL, mode, 0, FALSE, NULL, &stream);
419         ok(ret == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) || /* XP */
420            ret == E_INVALIDARG /* Vista */,
421           "SHCreateStreamOnFileEx: expected HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) or E_INVALIDARG, got 0x%08x\n", ret);
422         ok(stream == NULL, "SHCreateStreamOnFileEx: expected a NULL IStream object, got %p\n", stream);
423     }
424
425     stream = NULL;
426     ret = (*pSHCreateStreamOnFileEx)(test_file, mode, 0, FALSE, template, &stream);
427     ok( ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
428         ret == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
429         "SHCreateStreamOnFileEx: expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) or "
430         "HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got 0x%08x\n", ret);
431
432     ok(stream == NULL, "SHCreateStreamOnFileEx: expected a NULL IStream object, got %p\n", stream);
433
434     if (0)
435     {
436         /* This test crashes on WinXP SP2 */
437         ret = (*pSHCreateStreamOnFileEx)(test_file, mode, 0, FALSE, NULL, NULL);
438         ok(ret == E_INVALIDARG, "SHCreateStreamOnFileEx: expected E_INVALIDARG, got 0x%08x\n", ret);
439     }
440
441     /* file does not exist */
442
443     stream = NULL;
444     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_FAILIFTHERE | stgm, 0, FALSE, NULL, &stream);
445     if ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED && mode == STGM_READ) {
446         ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) /* XP */ || ret == E_INVALIDARG /* Vista */,
447           "SHCreateStreamOnFileEx: expected E_INVALIDARG or HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got 0x%08x\n", ret);
448
449         if (ret == E_INVALIDARG) {
450             skip("SHCreateStreamOnFileEx: STGM_TRANSACTED not supported in this configuration.\n");
451             return;
452         }
453     } else {
454         ok( ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
455             ret == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
456             "SHCreateStreamOnFileEx: expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) or "
457             "HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got 0x%08x\n", ret);
458     }
459     ok(stream == NULL, "SHCreateStreamOnFileEx: expected a NULL IStream object, got %p\n", stream);
460
461     stream = NULL;
462     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_FAILIFTHERE | stgm, 0, TRUE, NULL, &stream);
463     /* not supported on win9x */
464     if (broken(ret == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) && stream == NULL)) {
465         skip("Not supported\n");
466         DeleteFileA(test_fileA);
467         return;
468     }
469
470     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
471     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
472
473     if (stream) {
474         test_IStream_invalid_operations(stream, mode);
475
476         refcount = IStream_Release(stream);
477         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
478
479         ok(DeleteFileA(test_fileA),
480             "SHCreateStreamOnFileEx: could not delete the test file, got error %d\n",
481             GetLastError());
482     }
483
484     stream = NULL;
485     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_CREATE | stgm, 0, FALSE, NULL, &stream);
486     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
487     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
488
489     if (stream) {
490         test_IStream_invalid_operations(stream, mode);
491
492         refcount = IStream_Release(stream);
493         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
494
495         ok(DeleteFileA(test_fileA),
496             "SHCreateStreamOnFileEx: could not delete the test file, got error %d\n",
497             GetLastError());
498     }
499
500     stream = NULL;
501     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_CREATE | stgm, 0, TRUE, NULL, &stream);
502     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
503     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
504
505     if (stream) {
506         test_IStream_invalid_operations(stream, mode);
507
508         refcount = IStream_Release(stream);
509         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
510     }
511
512     /* NOTE: don't delete the file, as it will be used for the file exists tests. */
513
514     /* file exists */
515
516     stream = NULL;
517     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_FAILIFTHERE | stgm, 0, FALSE, NULL, &stream);
518     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
519     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
520
521     if (stream) {
522         test_IStream_invalid_operations(stream, mode);
523
524         refcount = IStream_Release(stream);
525         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
526     }
527
528     stream = NULL;
529     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_FAILIFTHERE | stgm, 0, TRUE, NULL, &stream);
530     ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS), "SHCreateStreamOnFileEx: expected HRESULT_FROM_WIN32(ERROR_FILE_EXISTS), got 0x%08x\n", ret);
531     ok(stream == NULL, "SHCreateStreamOnFileEx: expected a NULL IStream object, got %p\n", stream);
532
533     stream = NULL;
534     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_CREATE | stgm, 0, FALSE, NULL, &stream);
535     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
536     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
537
538     if (stream) {
539         test_IStream_invalid_operations(stream, mode);
540
541         refcount = IStream_Release(stream);
542         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
543     }
544
545     stream = NULL;
546     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_CREATE | stgm, 0, TRUE, NULL, &stream);
547     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
548     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
549
550     if (stream) {
551         test_IStream_invalid_operations(stream, mode);
552
553         refcount = IStream_Release(stream);
554         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
555     }
556
557     ok(DeleteFileA(test_fileA),
558         "SHCreateStreamOnFileEx: could not delete the test file, got error %d\n",
559         GetLastError());
560 }
561
562
563 START_TEST(istream)
564 {
565     static const DWORD stgm_access[] = {
566         STGM_READ,
567         STGM_WRITE,
568         STGM_READWRITE
569     };
570
571     static const DWORD stgm_sharing[] = {
572         0,
573         STGM_SHARE_DENY_NONE,
574         STGM_SHARE_DENY_READ,
575         STGM_SHARE_DENY_WRITE,
576         STGM_SHARE_EXCLUSIVE
577     };
578
579     static const DWORD stgm_flags[] = {
580         0,
581         STGM_CONVERT,
582         STGM_DELETEONRELEASE,
583         STGM_CONVERT | STGM_DELETEONRELEASE,
584         STGM_TRANSACTED | STGM_CONVERT,
585         STGM_TRANSACTED | STGM_DELETEONRELEASE,
586         STGM_TRANSACTED | STGM_CONVERT | STGM_DELETEONRELEASE
587     };
588
589     int i, j, k;
590
591     hShlwapi = GetModuleHandleA("shlwapi.dll");
592
593     pSHCreateStreamOnFileA = (void*)GetProcAddress(hShlwapi, "SHCreateStreamOnFileA");
594     pSHCreateStreamOnFileW = (void*)GetProcAddress(hShlwapi, "SHCreateStreamOnFileW");
595     pSHCreateStreamOnFileEx = (void*)GetProcAddress(hShlwapi, "SHCreateStreamOnFileEx");
596
597     if (!pSHCreateStreamOnFileA)
598         skip("SHCreateStreamOnFileA not found.\n");
599
600     if (!pSHCreateStreamOnFileW)
601         skip("SHCreateStreamOnFileW not found.\n");
602
603     if (!pSHCreateStreamOnFileEx)
604         skip("SHCreateStreamOnFileEx not found.\n");
605
606     for (i = 0; i != sizeof(stgm_access)/sizeof(stgm_access[0]); i++) {
607         for (j = 0; j != sizeof(stgm_sharing)/sizeof(stgm_sharing[0]); j ++) {
608             if (pSHCreateStreamOnFileA)
609                 test_SHCreateStreamOnFileA(stgm_access[i], stgm_sharing[j]);
610
611             if (pSHCreateStreamOnFileW)
612                 test_SHCreateStreamOnFileW(stgm_access[i], stgm_sharing[j]);
613
614             if (pSHCreateStreamOnFileEx) {
615                 for (k = 0; k != sizeof(stgm_flags)/sizeof(stgm_flags[0]); k++)
616                     test_SHCreateStreamOnFileEx(stgm_access[i], stgm_sharing[j] | stgm_flags[k]);
617             }
618         }
619     }
620 }