comctl32: Update thumb position on autopage instead of deferring it.
[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     /* 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_SHCreateStreamOnFileA(DWORD mode, DWORD stgm)
195 {
196     IStream * stream;
197     HRESULT ret;
198     ULONG refcount;
199     char test_file[MAX_PATH];
200     static const CHAR testA_txt[] = "\\testA.txt";
201
202     trace("SHCreateStreamOnFileA: testing mode %d, STGM flags %08x\n", mode, stgm);
203
204     /* Don't used a fixed path for the testA.txt file */
205     GetTempPathA(MAX_PATH, test_file);
206     lstrcatA(test_file, testA_txt);
207
208     /* invalid arguments */
209
210     stream = NULL;
211     ret = (*pSHCreateStreamOnFileA)(NULL, mode | stgm, &stream);
212     if (ret == E_INVALIDARG) /* Win98 SE */ {
213         win_skip("Not supported\n");
214         return;
215     }
216
217     ok(ret == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) /* NT */ ||
218        ret == HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME) /* 9x */,
219        "SHCreateStreamOnFileA: expected HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) "
220        "or HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME), got 0x%08x\n", ret);
221     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
222
223 #if 0 /* This test crashes on WinXP SP2 */
224     ret = (*pSHCreateStreamOnFileA)(test_file, mode | stgm, NULL);
225     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileA: expected E_INVALIDARG, got 0x%08x\n", ret);
226 #endif
227
228     stream = NULL;
229     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_CONVERT | stgm, &stream);
230     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileA: expected E_INVALIDARG, got 0x%08x\n", ret);
231     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
232
233     stream = NULL;
234     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_DELETEONRELEASE | stgm, &stream);
235     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileA: expected E_INVALIDARG, got 0x%08x\n", ret);
236     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
237
238     stream = NULL;
239     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_TRANSACTED | stgm, &stream);
240     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileA: expected E_INVALIDARG, got 0x%08x\n", ret);
241     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
242
243     /* file does not exist */
244
245     stream = NULL;
246     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_FAILIFTHERE | stgm, &stream);
247     ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "SHCreateStreamOnFileA: expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got 0x%08x\n", ret);
248     ok(stream == NULL, "SHCreateStreamOnFileA: expected a NULL IStream object, got %p\n", stream);
249
250     stream = NULL;
251     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_CREATE | stgm, &stream);
252     ok(ret == S_OK, "SHCreateStreamOnFileA: expected S_OK, got 0x%08x\n", ret);
253     ok(stream != NULL, "SHCreateStreamOnFileA: expected a valid IStream object, got NULL\n");
254
255     if (stream) {
256         test_IStream_invalid_operations(stream, mode);
257
258         refcount = IStream_Release(stream);
259         ok(refcount == 0, "SHCreateStreamOnFileA: expected 0, got %d\n", refcount);
260     }
261
262     /* NOTE: don't delete the file, as it will be used for the file exists tests. */
263
264     /* file exists */
265
266     stream = NULL;
267     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_FAILIFTHERE | stgm, &stream);
268     ok(ret == S_OK, "SHCreateStreamOnFileA: expected S_OK, got 0x%08x\n", ret);
269     ok(stream != NULL, "SHCreateStreamOnFileA: expected a valid IStream object, got NULL\n");
270
271     if (stream) {
272         test_IStream_invalid_operations(stream, mode);
273
274         refcount = IStream_Release(stream);
275         ok(refcount == 0, "SHCreateStreamOnFileA: expected 0, got %d\n", refcount);
276     }
277
278     stream = NULL;
279     ret = (*pSHCreateStreamOnFileA)(test_file, mode | STGM_CREATE | stgm, &stream);
280     ok(ret == S_OK, "SHCreateStreamOnFileA: expected S_OK, got 0x%08x\n", ret);
281     ok(stream != NULL, "SHCreateStreamOnFileA: expected a valid IStream object, got NULL\n");
282
283     if (stream) {
284         BOOL delret;
285         test_IStream_invalid_operations(stream, mode);
286
287         refcount = IStream_Release(stream);
288         ok(refcount == 0, "SHCreateStreamOnFileA: expected 0, got %d\n", refcount);
289
290         delret = DeleteFileA(test_file);
291         ok(delret, "SHCreateStreamOnFileA: could not delete file '%s', got error %d\n",
292            test_file, GetLastError());
293     }
294 }
295
296
297 static void test_SHCreateStreamOnFileW(DWORD mode, DWORD stgm)
298 {
299     IStream * stream;
300     HRESULT ret;
301     ULONG refcount;
302     WCHAR test_file[MAX_PATH];
303     CHAR  test_fileA[MAX_PATH];
304     static const CHAR testW_txt[] = "\\testW.txt";
305
306     trace("SHCreateStreamOnFileW: testing mode %d, STGM flags %08x\n", mode, stgm);
307
308     /* Don't used a fixed path for the testW.txt file */
309     GetTempPathA(MAX_PATH, test_fileA);
310     lstrcatA(test_fileA, testW_txt);
311     MultiByteToWideChar(CP_ACP, 0, test_fileA, -1, test_file, MAX_PATH);
312
313     /* invalid arguments */
314
315     if (0)
316     {
317         /* Crashes on NT4 */
318         stream = NULL;
319         ret = (*pSHCreateStreamOnFileW)(NULL, mode | stgm, &stream);
320         ok(ret == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) || /* XP */
321            ret == E_INVALIDARG /* Vista */,
322           "SHCreateStreamOnFileW: expected HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) or E_INVALIDARG, got 0x%08x\n", ret);
323         ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
324     }
325
326     if (0)
327     {
328         /* This test crashes on WinXP SP2 */
329             ret = (*pSHCreateStreamOnFileW)(test_file, mode | stgm, NULL);
330             ok(ret == E_INVALIDARG, "SHCreateStreamOnFileW: expected E_INVALIDARG, got 0x%08x\n", ret);
331     }
332
333     stream = NULL;
334     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_CONVERT | stgm, &stream);
335     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileW: expected E_INVALIDARG, got 0x%08x\n", ret);
336     ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
337
338     stream = NULL;
339     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_DELETEONRELEASE | stgm, &stream);
340     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileW: expected E_INVALIDARG, got 0x%08x\n", ret);
341     ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
342
343     stream = NULL;
344     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_TRANSACTED | stgm, &stream);
345     ok(ret == E_INVALIDARG, "SHCreateStreamOnFileW: expected E_INVALIDARG, got 0x%08x\n", ret);
346     ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
347
348     /* file does not exist */
349
350     stream = NULL;
351     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_FAILIFTHERE | stgm, &stream);
352     if (ret == E_INVALIDARG) /* Win98 SE */ {
353         win_skip("Not supported\n");
354         return;
355     }
356
357     ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "SHCreateStreamOnFileW: expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got 0x%08x\n", ret);
358     ok(stream == NULL, "SHCreateStreamOnFileW: expected a NULL IStream object, got %p\n", stream);
359
360     stream = NULL;
361     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_CREATE | stgm, &stream);
362     ok(ret == S_OK, "SHCreateStreamOnFileW: expected S_OK, got 0x%08x\n", ret);
363     ok(stream != NULL, "SHCreateStreamOnFileW: expected a valid IStream object, got NULL\n");
364
365     if (stream) {
366         test_IStream_invalid_operations(stream, mode);
367
368         refcount = IStream_Release(stream);
369         ok(refcount == 0, "SHCreateStreamOnFileW: expected 0, got %d\n", refcount);
370     }
371
372     /* NOTE: don't delete the file, as it will be used for the file exists tests. */
373
374     /* file exists */
375
376     stream = NULL;
377     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_FAILIFTHERE | stgm, &stream);
378     ok(ret == S_OK, "SHCreateStreamOnFileW: expected S_OK, got 0x%08x\n", ret);
379     ok(stream != NULL, "SHCreateStreamOnFileW: expected a valid IStream object, got NULL\n");
380
381     if (stream) {
382         test_IStream_invalid_operations(stream, mode);
383
384         refcount = IStream_Release(stream);
385         ok(refcount == 0, "SHCreateStreamOnFileW: expected 0, got %d\n", refcount);
386     }
387
388     stream = NULL;
389     ret = (*pSHCreateStreamOnFileW)(test_file, mode | STGM_CREATE | stgm, &stream);
390     ok(ret == S_OK, "SHCreateStreamOnFileW: expected S_OK, got 0x%08x\n", ret);
391     ok(stream != NULL, "SHCreateStreamOnFileW: expected a valid IStream object, got NULL\n");
392
393     if (stream) {
394         BOOL delret;
395         test_IStream_invalid_operations(stream, mode);
396
397         refcount = IStream_Release(stream);
398         ok(refcount == 0, "SHCreateStreamOnFileW: expected 0, got %d\n", refcount);
399
400         delret = DeleteFileA(test_fileA);
401         ok(delret, "SHCreateStreamOnFileW: could not delete the test file, got error %d\n",
402            GetLastError());
403     }
404 }
405
406
407 static void test_SHCreateStreamOnFileEx(DWORD mode, DWORD stgm)
408 {
409     IStream * stream;
410     IStream * template = NULL;
411     HRESULT ret;
412     ULONG refcount;
413     WCHAR test_file[MAX_PATH];
414     CHAR  test_fileA[MAX_PATH];
415     static const CHAR testEx_txt[] = "\\testEx.txt";
416     BOOL delret;
417
418     trace("SHCreateStreamOnFileEx: testing mode %d, STGM flags %08x\n", mode, stgm);
419
420     /* Don't used a fixed path for the testEx.txt file */
421     GetTempPathA(MAX_PATH, test_fileA);
422     lstrcatA(test_fileA, testEx_txt);
423     MultiByteToWideChar(CP_ACP, 0, test_fileA, -1, test_file, MAX_PATH);
424
425     /* invalid arguments */
426
427     if (0)
428     {
429         /* Crashes on NT4 */
430         stream = NULL;
431         ret = (*pSHCreateStreamOnFileEx)(NULL, mode, 0, FALSE, NULL, &stream);
432         ok(ret == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) || /* XP */
433            ret == E_INVALIDARG /* Vista */,
434           "SHCreateStreamOnFileEx: expected HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) or E_INVALIDARG, got 0x%08x\n", ret);
435         ok(stream == NULL, "SHCreateStreamOnFileEx: expected a NULL IStream object, got %p\n", stream);
436     }
437
438     stream = NULL;
439     ret = (*pSHCreateStreamOnFileEx)(test_file, mode, 0, FALSE, template, &stream);
440     if (ret == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) {
441         win_skip("File probably locked by Anti-Virus/Spam software, trying again\n");
442         Sleep(1000);
443         ret = (*pSHCreateStreamOnFileEx)(test_file, mode, 0, FALSE, template, &stream);
444     }
445     ok( ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
446         ret == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
447         "SHCreateStreamOnFileEx: expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) or "
448         "HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got 0x%08x\n", ret);
449
450     ok(stream == NULL, "SHCreateStreamOnFileEx: expected a NULL IStream object, got %p\n", stream);
451
452     if (0)
453     {
454         /* This test crashes on WinXP SP2 */
455         ret = (*pSHCreateStreamOnFileEx)(test_file, mode, 0, FALSE, NULL, NULL);
456         ok(ret == E_INVALIDARG, "SHCreateStreamOnFileEx: expected E_INVALIDARG, got 0x%08x\n", ret);
457     }
458
459     /* file does not exist */
460
461     stream = NULL;
462     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_FAILIFTHERE | stgm, 0, FALSE, NULL, &stream);
463     if ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED && mode == STGM_READ) {
464         ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) /* XP */ || ret == E_INVALIDARG /* Vista */,
465           "SHCreateStreamOnFileEx: expected E_INVALIDARG or HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), got 0x%08x\n", ret);
466
467         if (ret == E_INVALIDARG) {
468             skip("SHCreateStreamOnFileEx: STGM_TRANSACTED not supported in this configuration.\n");
469             return;
470         }
471     } else {
472         ok( ret == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
473             ret == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
474             "SHCreateStreamOnFileEx: expected HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) or "
475             "HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got 0x%08x\n", ret);
476     }
477     ok(stream == NULL, "SHCreateStreamOnFileEx: expected a NULL IStream object, got %p\n", stream);
478
479     stream = NULL;
480     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_FAILIFTHERE | stgm, 0, TRUE, NULL, &stream);
481     /* not supported on win9x */
482     if (broken(ret == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) && stream == NULL)) {
483         skip("Not supported\n");
484         DeleteFileA(test_fileA);
485         return;
486     }
487
488     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
489     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
490
491     if (stream) {
492         test_IStream_invalid_operations(stream, mode);
493
494         refcount = IStream_Release(stream);
495         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
496
497         delret = DeleteFileA(test_fileA);
498         ok(delret, "SHCreateStreamOnFileEx: could not delete the test file, got error %d\n",
499            GetLastError());
500     }
501
502     stream = NULL;
503     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_CREATE | stgm, 0, FALSE, NULL, &stream);
504     if (ret == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) {
505         win_skip("File probably locked by Anti-Virus/Spam software, trying again\n");
506         Sleep(1000);
507         ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_CREATE | stgm, 0, FALSE, NULL, &stream);
508     }
509     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
510     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
511
512     if (stream) {
513         test_IStream_invalid_operations(stream, mode);
514
515         refcount = IStream_Release(stream);
516         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
517
518         delret = DeleteFileA(test_fileA);
519         ok(delret, "SHCreateStreamOnFileEx: could not delete the test file, got error %d\n",
520            GetLastError());
521     }
522
523     stream = NULL;
524     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_CREATE | stgm, 0, TRUE, NULL, &stream);
525     if (ret == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) {
526         win_skip("File probably locked by Anti-Virus/Spam software, trying again\n");
527         Sleep(1000);
528         ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_CREATE | stgm, 0, TRUE, NULL, &stream);
529     }
530     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
531     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
532
533     if (stream) {
534         test_IStream_invalid_operations(stream, mode);
535
536         refcount = IStream_Release(stream);
537         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
538     }
539
540     /* NOTE: don't delete the file, as it will be used for the file exists tests. */
541
542     /* file exists */
543
544     stream = NULL;
545     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_FAILIFTHERE | stgm, 0, FALSE, NULL, &stream);
546     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
547     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
548
549     if (stream) {
550         test_IStream_invalid_operations(stream, mode);
551
552         refcount = IStream_Release(stream);
553         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
554     }
555
556     stream = NULL;
557     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_FAILIFTHERE | stgm, 0, TRUE, NULL, &stream);
558     ok(ret == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS), "SHCreateStreamOnFileEx: expected HRESULT_FROM_WIN32(ERROR_FILE_EXISTS), got 0x%08x\n", ret);
559     ok(stream == NULL, "SHCreateStreamOnFileEx: expected a NULL IStream object, got %p\n", stream);
560
561     stream = NULL;
562     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_CREATE | stgm, 0, FALSE, NULL, &stream);
563     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
564     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
565
566     if (stream) {
567         test_IStream_invalid_operations(stream, mode);
568
569         refcount = IStream_Release(stream);
570         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
571     }
572
573     stream = NULL;
574     ret = (*pSHCreateStreamOnFileEx)(test_file, mode | STGM_CREATE | stgm, 0, TRUE, NULL, &stream);
575     ok(ret == S_OK, "SHCreateStreamOnFileEx: expected S_OK, got 0x%08x\n", ret);
576     ok(stream != NULL, "SHCreateStreamOnFileEx: expected a valid IStream object, got NULL\n");
577
578     if (stream) {
579         test_IStream_invalid_operations(stream, mode);
580
581         refcount = IStream_Release(stream);
582         ok(refcount == 0, "SHCreateStreamOnFileEx: expected 0, got %d\n", refcount);
583     }
584
585     delret = DeleteFileA(test_fileA);
586     ok(delret, "SHCreateStreamOnFileEx: could not delete the test file, got error %d\n",
587        GetLastError());
588 }
589
590
591 START_TEST(istream)
592 {
593     static const DWORD stgm_access[] = {
594         STGM_READ,
595         STGM_WRITE,
596         STGM_READWRITE
597     };
598
599     static const DWORD stgm_sharing[] = {
600         0,
601         STGM_SHARE_DENY_NONE,
602         STGM_SHARE_DENY_READ,
603         STGM_SHARE_DENY_WRITE,
604         STGM_SHARE_EXCLUSIVE
605     };
606
607     static const DWORD stgm_flags[] = {
608         0,
609         STGM_CONVERT,
610         STGM_DELETEONRELEASE,
611         STGM_CONVERT | STGM_DELETEONRELEASE,
612         STGM_TRANSACTED | STGM_CONVERT,
613         STGM_TRANSACTED | STGM_DELETEONRELEASE,
614         STGM_TRANSACTED | STGM_CONVERT | STGM_DELETEONRELEASE
615     };
616
617     int i, j, k;
618
619     hShlwapi = GetModuleHandleA("shlwapi.dll");
620
621     pSHCreateStreamOnFileA = (void*)GetProcAddress(hShlwapi, "SHCreateStreamOnFileA");
622     pSHCreateStreamOnFileW = (void*)GetProcAddress(hShlwapi, "SHCreateStreamOnFileW");
623     pSHCreateStreamOnFileEx = (void*)GetProcAddress(hShlwapi, "SHCreateStreamOnFileEx");
624
625     if (!pSHCreateStreamOnFileA)
626         skip("SHCreateStreamOnFileA not found.\n");
627
628     if (!pSHCreateStreamOnFileW)
629         skip("SHCreateStreamOnFileW not found.\n");
630
631     if (!pSHCreateStreamOnFileEx)
632         skip("SHCreateStreamOnFileEx not found.\n");
633
634     for (i = 0; i != sizeof(stgm_access)/sizeof(stgm_access[0]); i++) {
635         for (j = 0; j != sizeof(stgm_sharing)/sizeof(stgm_sharing[0]); j ++) {
636             if (pSHCreateStreamOnFileA)
637                 test_SHCreateStreamOnFileA(stgm_access[i], stgm_sharing[j]);
638
639             if (pSHCreateStreamOnFileW)
640                 test_SHCreateStreamOnFileW(stgm_access[i], stgm_sharing[j]);
641
642             if (pSHCreateStreamOnFileEx) {
643                 for (k = 0; k != sizeof(stgm_flags)/sizeof(stgm_flags[0]); k++)
644                     test_SHCreateStreamOnFileEx(stgm_access[i], stgm_sharing[j] | stgm_flags[k]);
645             }
646         }
647     }
648 }