1 /*
2  *
3  * Copyright 2012 Alistair Leslie-Hughes
4  * Copyright 2014 Dmitry Timoshkov
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #define COBJMACROS
22 #include <stdio.h>
23 #include <limits.h>
24 
25 #include "windows.h"
26 #include "ole2.h"
27 #include "olectl.h"
28 #include "oleauto.h"
29 #include "dispex.h"
30 
31 #include "wine/test.h"
32 
33 #include "initguid.h"
34 #include "scrrun.h"
35 
36 static IFileSystem3 *fs3;
37 
38 /* w2k and 2k3 error code. */
39 #define E_VAR_NOT_SET 0x800a005b
40 
41 static inline ULONG get_refcount(IUnknown *iface)
42 {
43     IUnknown_AddRef(iface);
44     return IUnknown_Release(iface);
45 }
46 
47 static const WCHAR crlfW[] = {'\r','\n',0};
48 static const char utf16bom[] = {0xff,0xfe,0};
49 static const WCHAR testfileW[] = {'t','e','s','t','.','t','x','t',0};
50 
51 #define GET_REFCOUNT(iface) \
52     get_refcount((IUnknown*)iface)
53 
54 static inline void get_temp_path(const WCHAR *prefix, WCHAR *path)
55 {
56     WCHAR buffW[MAX_PATH];
57 
58     GetTempPathW(MAX_PATH, buffW);
59     GetTempFileNameW(buffW, prefix, 0, path);
60     DeleteFileW(path);
61 }
62 
63 static IDrive *get_fixed_drive(void)
64 {
65     IDriveCollection *drives;
66     IEnumVARIANT *iter;
67     IDrive *drive;
68     HRESULT hr;
69 
70     hr = IFileSystem3_get_Drives(fs3, &drives);
71     ok(hr == S_OK, "got 0x%08x\n", hr);
72 
73     hr = IDriveCollection_get__NewEnum(drives, (IUnknown**)&iter);
74     ok(hr == S_OK, "got 0x%08x\n", hr);
75     IDriveCollection_Release(drives);
76 
77     while (1) {
78         DriveTypeConst type;
79         VARIANT var;
80 
81         hr = IEnumVARIANT_Next(iter, 1, &var, NULL);
82         if (hr == S_FALSE) {
83             drive = NULL;
84             break;
85         }
86         ok(hr == S_OK, "got 0x%08x\n", hr);
87 
88         hr = IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IDrive, (void**)&drive);
89         ok(hr == S_OK, "got 0x%08x\n", hr);
90         VariantClear(&var);
91 
92         hr = IDrive_get_DriveType(drive, &type);
93         ok(hr == S_OK, "got 0x%08x\n", hr);
94         if (type == Fixed)
95             break;
96 
97         IDrive_Release(drive);
98     }
99 
100     IEnumVARIANT_Release(iter);
101     return drive;
102 }
103 
104 #define test_provideclassinfo(a, b) _test_provideclassinfo((IDispatch*)a, b, __LINE__)
105 static void _test_provideclassinfo(IDispatch *disp, const GUID *guid, int line)
106 {
107     IProvideClassInfo *classinfo;
108     TYPEATTR *attr;
109     ITypeInfo *ti;
110     IUnknown *unk;
111     HRESULT hr;
112 
113     hr = IDispatch_QueryInterface(disp, &IID_IProvideClassInfo, (void **)&classinfo);
114     ok_(__FILE__,line) (hr == S_OK, "Failed to get IProvideClassInfo, %#x.\n", hr);
115 
116     hr = IProvideClassInfo_GetClassInfo(classinfo, &ti);
117     ok_(__FILE__,line) (hr == S_OK, "GetClassInfo() failed, %#x.\n", hr);
118 
119     hr = ITypeInfo_GetTypeAttr(ti, &attr);
120     ok_(__FILE__,line) (hr == S_OK, "GetTypeAttr() failed, %#x.\n", hr);
121 
122     ok_(__FILE__,line) (IsEqualGUID(&attr->guid, guid), "Unexpected typeinfo %s, expected %s\n", wine_dbgstr_guid(&attr->guid),
123         wine_dbgstr_guid(guid));
124 
125     hr = IProvideClassInfo_QueryInterface(classinfo, &IID_IUnknown, (void **)&unk);
126     ok(hr == S_OK, "Failed to QI for IUnknown.\n");
127     ok(unk == (IUnknown *)disp, "Got unk %p, original %p.\n", unk, disp);
128     IUnknown_Release(unk);
129 
130     IProvideClassInfo_Release(classinfo);
131     ITypeInfo_ReleaseTypeAttr(ti, attr);
132     ITypeInfo_Release(ti);
133 }
134 
135 static void test_interfaces(void)
136 {
137     static const WCHAR nonexistent_dirW[] = {
138         'c', ':', '\\', 'N', 'o', 'n', 'e', 'x', 'i', 's', 't', 'e', 'n', 't', 0};
139     static const WCHAR pathW[] = {'p','a','t','h',0};
140     static const WCHAR file_kernel32W[] = {
141         '\\', 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', 0};
142     HRESULT hr;
143     IDispatch *disp;
144     IDispatchEx *dispex;
145     IObjectWithSite *site;
146     VARIANT_BOOL b;
147     BSTR path;
148     WCHAR windows_path[MAX_PATH];
149     WCHAR file_path[MAX_PATH];
150 
151     IFileSystem3_QueryInterface(fs3, &IID_IDispatch, (void**)&disp);
152 
153     GetSystemDirectoryW(windows_path, MAX_PATH);
154     lstrcpyW(file_path, windows_path);
155     lstrcatW(file_path, file_kernel32W);
156 
157     test_provideclassinfo(disp, &CLSID_FileSystemObject);
158 
159     hr = IDispatch_QueryInterface(disp, &IID_IObjectWithSite, (void**)&site);
160     ok(hr == E_NOINTERFACE, "got 0x%08x, expected 0x%08x\n", hr, E_NOINTERFACE);
161 
162     hr = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
163     ok(hr == E_NOINTERFACE, "got 0x%08x, expected 0x%08x\n", hr, E_NOINTERFACE);
164 
165     b = VARIANT_TRUE;
166     hr = IFileSystem3_FileExists(fs3, NULL, &b);
167     ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
168     ok(b == VARIANT_FALSE, "got %x\n", b);
169 
170     hr = IFileSystem3_FileExists(fs3, NULL, NULL);
171     ok(hr == E_POINTER, "got 0x%08x, expected 0x%08x\n", hr, E_POINTER);
172 
173     path = SysAllocString(pathW);
174     b = VARIANT_TRUE;
175     hr = IFileSystem3_FileExists(fs3, path, &b);
176     ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
177     ok(b == VARIANT_FALSE, "got %x\n", b);
178     SysFreeString(path);
179 
180     path = SysAllocString(file_path);
181     b = VARIANT_FALSE;
182     hr = IFileSystem3_FileExists(fs3, path, &b);
183     ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
184     ok(b == VARIANT_TRUE, "got %x\n", b);
185     SysFreeString(path);
186 
187     path = SysAllocString(windows_path);
188     b = VARIANT_TRUE;
189     hr = IFileSystem3_FileExists(fs3, path, &b);
190     ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
191     ok(b == VARIANT_FALSE, "got %x\n", b);
192     SysFreeString(path);
193 
194     /* Folder Exists */
195     hr = IFileSystem3_FolderExists(fs3, NULL, NULL);
196     ok(hr == E_POINTER, "got 0x%08x, expected 0x%08x\n", hr, E_POINTER);
197 
198     path = SysAllocString(windows_path);
199     hr = IFileSystem3_FolderExists(fs3, path, &b);
200     ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
201     ok(b == VARIANT_TRUE, "Folder doesn't exists\n");
202     SysFreeString(path);
203 
204     path = SysAllocString(nonexistent_dirW);
205     hr = IFileSystem3_FolderExists(fs3, path, &b);
206     ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
207     ok(b == VARIANT_FALSE, "Folder exists\n");
208     SysFreeString(path);
209 
210     path = SysAllocString(file_path);
211     hr = IFileSystem3_FolderExists(fs3, path, &b);
212     ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
213     ok(b == VARIANT_FALSE, "Folder exists\n");
214     SysFreeString(path);
215 
216     IDispatch_Release(disp);
217 }
218 
219 static void test_createfolder(void)
220 {
221     WCHAR buffW[MAX_PATH];
222     HRESULT hr;
223     BSTR path;
224     IFolder *folder;
225     BOOL ret;
226 
227     get_temp_path(NULL, buffW);
228     ret = CreateDirectoryW(buffW, NULL);
229     ok(ret, "got %d, %d\n", ret, GetLastError());
230 
231     /* create existing directory */
232     path = SysAllocString(buffW);
233     folder = (void*)0xdeabeef;
234     hr = IFileSystem3_CreateFolder(fs3, path, &folder);
235     ok(hr == CTL_E_FILEALREADYEXISTS, "got 0x%08x\n", hr);
236     ok(folder == NULL, "got %p\n", folder);
237     SysFreeString(path);
238     RemoveDirectoryW(buffW);
239 }
240 
241 static void test_textstream(void)
242 {
243     ITextStream *stream;
244     VARIANT_BOOL b;
245     DWORD written;
246     HANDLE file;
247     HRESULT hr;
248     BSTR name, data;
249     BOOL ret;
250 
251     file = CreateFileW(testfileW, GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
252     CloseHandle(file);
253 
254     name = SysAllocString(testfileW);
255     b = VARIANT_FALSE;
256     hr = IFileSystem3_FileExists(fs3, name, &b);
257     ok(hr == S_OK, "got 0x%08x, expected 0x%08x\n", hr, S_OK);
258     ok(b == VARIANT_TRUE, "got %x\n", b);
259 
260     /* different mode combinations */
261     hr = IFileSystem3_OpenTextFile(fs3, name, ForWriting | ForAppending, VARIANT_FALSE, TristateFalse, &stream);
262     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
263 
264     hr = IFileSystem3_OpenTextFile(fs3, name, ForReading | ForAppending, VARIANT_FALSE, TristateFalse, &stream);
265     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
266 
267     hr = IFileSystem3_OpenTextFile(fs3, name, ForWriting | ForReading, VARIANT_FALSE, TristateFalse, &stream);
268     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
269 
270     hr = IFileSystem3_OpenTextFile(fs3, name, ForAppending, VARIANT_FALSE, TristateFalse, &stream);
271     ok(hr == S_OK, "got 0x%08x\n", hr);
272     hr = ITextStream_Read(stream, 1, &data);
273     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
274     ITextStream_Release(stream);
275 
276     hr = IFileSystem3_OpenTextFile(fs3, name, ForWriting, VARIANT_FALSE, TristateFalse, &stream);
277     ok(hr == S_OK, "got 0x%08x\n", hr);
278     hr = ITextStream_Read(stream, 1, &data);
279     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
280     ITextStream_Release(stream);
281 
282     hr = IFileSystem3_OpenTextFile(fs3, name, ForReading, VARIANT_FALSE, TristateFalse, &stream);
283     ok(hr == S_OK, "got 0x%08x\n", hr);
284 
285     /* try to write when open for reading */
286     hr = ITextStream_WriteLine(stream, name);
287     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
288 
289     hr = ITextStream_Write(stream, name);
290     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
291 
292     hr = ITextStream_get_AtEndOfStream(stream, NULL);
293     ok(hr == E_POINTER, "got 0x%08x\n", hr);
294 
295     b = 10;
296     hr = ITextStream_get_AtEndOfStream(stream, &b);
297     ok(hr == S_OK || broken(hr == S_FALSE), "got 0x%08x\n", hr);
298     ok(b == VARIANT_TRUE, "got 0x%x\n", b);
299 
300     ITextStream_Release(stream);
301 
302     hr = IFileSystem3_OpenTextFile(fs3, name, ForWriting, VARIANT_FALSE, TristateFalse, &stream);
303     ok(hr == S_OK, "got 0x%08x\n", hr);
304 
305     b = 10;
306     hr = ITextStream_get_AtEndOfStream(stream, &b);
307     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
308     ok(b == VARIANT_TRUE || broken(b == 10), "got 0x%x\n", b);
309 
310     b = 10;
311     hr = ITextStream_get_AtEndOfLine(stream, &b);
312 todo_wine {
313     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
314     ok(b == VARIANT_FALSE || broken(b == 10), "got 0x%x\n", b);
315 }
316     hr = ITextStream_Read(stream, 1, &data);
317     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
318 
319     hr = ITextStream_ReadLine(stream, &data);
320     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
321 
322     hr = ITextStream_ReadAll(stream, &data);
323     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
324 
325     ITextStream_Release(stream);
326 
327     hr = IFileSystem3_OpenTextFile(fs3, name, ForAppending, VARIANT_FALSE, TristateFalse, &stream);
328     ok(hr == S_OK, "got 0x%08x\n", hr);
329 
330     b = 10;
331     hr = ITextStream_get_AtEndOfStream(stream, &b);
332     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
333     ok(b == VARIANT_TRUE || broken(b == 10), "got 0x%x\n", b);
334 
335     b = 10;
336     hr = ITextStream_get_AtEndOfLine(stream, &b);
337 todo_wine {
338     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
339     ok(b == VARIANT_FALSE || broken(b == 10), "got 0x%x\n", b);
340 }
341     hr = ITextStream_Read(stream, 1, &data);
342     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
343 
344     hr = ITextStream_ReadLine(stream, &data);
345     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
346 
347     hr = ITextStream_ReadAll(stream, &data);
348     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
349 
350     ITextStream_Release(stream);
351 
352     /* now with non-empty file */
353     file = CreateFileW(testfileW, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
354     ret = WriteFile(file, testfileW, sizeof(testfileW), &written, NULL);
355     ok(ret && written == sizeof(testfileW), "got %d\n", ret);
356     CloseHandle(file);
357 
358     hr = IFileSystem3_OpenTextFile(fs3, name, ForReading, VARIANT_FALSE, TristateFalse, &stream);
359     ok(hr == S_OK, "got 0x%08x\n", hr);
360     b = 10;
361     hr = ITextStream_get_AtEndOfStream(stream, &b);
362     ok(hr == S_OK, "got 0x%08x\n", hr);
363     ok(b == VARIANT_FALSE, "got 0x%x\n", b);
364     ITextStream_Release(stream);
365 
366     SysFreeString(name);
367     DeleteFileW(testfileW);
368 }
369 
370 static void test_GetFileVersion(void)
371 {
372     static const WCHAR k32W[] = {'\\','k','e','r','n','e','l','3','2','.','d','l','l',0};
373     static const WCHAR k33W[] = {'\\','k','e','r','n','e','l','3','3','.','d','l','l',0};
374     WCHAR pathW[MAX_PATH], filenameW[MAX_PATH];
375     BSTR path, version;
376     HRESULT hr;
377 
378     GetSystemDirectoryW(pathW, ARRAY_SIZE(pathW));
379 
380     lstrcpyW(filenameW, pathW);
381     lstrcatW(filenameW, k32W);
382 
383     path = SysAllocString(filenameW);
384     hr = IFileSystem3_GetFileVersion(fs3, path, &version);
385     ok(hr == S_OK, "got 0x%08x\n", hr);
386     ok(*version != 0, "got %s\n", wine_dbgstr_w(version));
387     SysFreeString(version);
388     SysFreeString(path);
389 
390     lstrcpyW(filenameW, pathW);
391     lstrcatW(filenameW, k33W);
392 
393     path = SysAllocString(filenameW);
394     version = (void*)0xdeadbeef;
395     hr = IFileSystem3_GetFileVersion(fs3, path, &version);
396     ok(broken(hr == S_OK) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "got 0x%08x\n", hr);
397     if (hr == S_OK)
398     {
399         ok(*version == 0, "got %s\n", wine_dbgstr_w(version));
400         SysFreeString(version);
401     }
402     else
403         ok(version == (void*)0xdeadbeef, "got %p\n", version);
404     SysFreeString(path);
405 }
406 
407 static void test_GetParentFolderName(void)
408 {
409     static const WCHAR path1[] = {'a',0};
410     static const WCHAR path2[] = {'a','/','a','/','a',0};
411     static const WCHAR path3[] = {'a','\\','a','\\','a',0};
412     static const WCHAR path4[] = {'a','/','a','/','/','\\','\\',0};
413     static const WCHAR path5[] = {'c',':','\\','\\','a',0};
414     static const WCHAR path6[] = {'a','c',':','\\','a',0};
415     static const WCHAR result2[] = {'a','/','a',0};
416     static const WCHAR result3[] = {'a','\\','a',0};
417     static const WCHAR result4[] = {'a',0};
418     static const WCHAR result5[] = {'c',':','\\',0};
419     static const WCHAR result6[] = {'a','c',':',0};
420 
421     static const struct {
422         const WCHAR *path;
423         const WCHAR *result;
424     } tests[] = {
425         {NULL, NULL},
426         {path1, NULL},
427         {path2, result2},
428         {path3, result3},
429         {path4, result4},
430         {path5, result5},
431         {path6, result6}
432     };
433 
434     BSTR path, result;
435     HRESULT hr;
436     int i;
437 
438     hr = IFileSystem3_GetParentFolderName(fs3, NULL, NULL);
439     ok(hr == E_POINTER, "GetParentFolderName returned %x, expected E_POINTER\n", hr);
440 
441     for(i=0; i < ARRAY_SIZE(tests); i++) {
442         result = (BSTR)0xdeadbeef;
443         path = tests[i].path ? SysAllocString(tests[i].path) : NULL;
444         hr = IFileSystem3_GetParentFolderName(fs3, path, &result);
445         ok(hr == S_OK, "%d) GetParentFolderName returned %x, expected S_OK\n", i, hr);
446         if(!tests[i].result)
447             ok(!result, "%d) result = %s\n", i, wine_dbgstr_w(result));
448         else
449             ok(!lstrcmpW(result, tests[i].result), "%d) result = %s\n", i, wine_dbgstr_w(result));
450         SysFreeString(path);
451         SysFreeString(result);
452     }
453 }
454 
455 static void test_GetFileName(void)
456 {
457     static const WCHAR path1[] = {'a',0};
458     static const WCHAR path2[] = {'a','/','a','.','b',0};
459     static const WCHAR path3[] = {'a','\\',0};
460     static const WCHAR path4[] = {'c',':',0};
461     static const WCHAR path5[] = {'/','\\',0};
462     static const WCHAR result2[] = {'a','.','b',0};
463     static const WCHAR result3[] = {'a',0};
464 
465     static const struct {
466         const WCHAR *path;
467         const WCHAR *result;
468     } tests[] = {
469         {NULL, NULL},
470         {path1, path1},
471         {path2, result2},
472         {path3, result3},
473         {path4, NULL},
474         {path5, NULL}
475     };
476 
477     BSTR path, result;
478     HRESULT hr;
479     int i;
480 
481     hr = IFileSystem3_GetFileName(fs3, NULL, NULL);
482     ok(hr == E_POINTER, "GetFileName returned %x, expected E_POINTER\n", hr);
483 
484     for(i=0; i < ARRAY_SIZE(tests); i++) {
485         result = (BSTR)0xdeadbeef;
486         path = tests[i].path ? SysAllocString(tests[i].path) : NULL;
487         hr = IFileSystem3_GetFileName(fs3, path, &result);
488         ok(hr == S_OK, "%d) GetFileName returned %x, expected S_OK\n", i, hr);
489         if(!tests[i].result)
490             ok(!result, "%d) result = %s\n", i, wine_dbgstr_w(result));
491         else
492             ok(!lstrcmpW(result, tests[i].result), "%d) result = %s\n", i, wine_dbgstr_w(result));
493         SysFreeString(path);
494         SysFreeString(result);
495     }
496 }
497 
498 static void test_GetBaseName(void)
499 {
500     static const WCHAR path1[] = {'a',0};
501     static const WCHAR path2[] = {'a','/','a','.','b','.','c',0};
502     static const WCHAR path3[] = {'a','.','b','\\',0};
503     static const WCHAR path4[] = {'c',':',0};
504     static const WCHAR path5[] = {'/','\\',0};
505     static const WCHAR path6[] = {'.','a',0};
506     static const WCHAR result1[] = {'a',0};
507     static const WCHAR result2[] = {'a','.','b',0};
508     static const WCHAR result6[] = {0};
509 
510     static const struct {
511         const WCHAR *path;
512         const WCHAR *result;
513     } tests[] = {
514         {NULL, NULL},
515         {path1, result1},
516         {path2, result2},
517         {path3, result1},
518         {path4, NULL},
519         {path5, NULL},
520         {path6, result6}
521     };
522 
523     BSTR path, result;
524     HRESULT hr;
525     int i;
526 
527     hr = IFileSystem3_GetBaseName(fs3, NULL, NULL);
528     ok(hr == E_POINTER, "GetBaseName returned %x, expected E_POINTER\n", hr);
529 
530     for(i=0; i < ARRAY_SIZE(tests); i++) {
531         result = (BSTR)0xdeadbeef;
532         path = tests[i].path ? SysAllocString(tests[i].path) : NULL;
533         hr = IFileSystem3_GetBaseName(fs3, path, &result);
534         ok(hr == S_OK, "%d) GetBaseName returned %x, expected S_OK\n", i, hr);
535         if(!tests[i].result)
536             ok(!result, "%d) result = %s\n", i, wine_dbgstr_w(result));
537         else
538             ok(!lstrcmpW(result, tests[i].result), "%d) result = %s\n", i, wine_dbgstr_w(result));
539         SysFreeString(path);
540         SysFreeString(result);
541     }
542 }
543 
544 static void test_GetAbsolutePathName(void)
545 {
546     static const WCHAR dir1[] = {'t','e','s','t','_','d','i','r','1',0};
547     static const WCHAR dir2[] = {'t','e','s','t','_','d','i','r','2',0};
548     static const WCHAR dir_match1[] = {'t','e','s','t','_','d','i','r','*',0};
549     static const WCHAR dir_match2[] = {'t','e','s','t','_','d','i','*',0};
550     static const WCHAR cur_dir[] = {'.',0};
551 
552     WIN32_FIND_DATAW fdata;
553     HANDLE find;
554     WCHAR buf[MAX_PATH], buf2[MAX_PATH];
555     BSTR path, result;
556     HRESULT hr;
557 
558     hr = IFileSystem3_GetAbsolutePathName(fs3, NULL, NULL);
559     ok(hr == E_POINTER, "GetAbsolutePathName returned %x, expected E_POINTER\n", hr);
560 
561     hr = IFileSystem3_GetAbsolutePathName(fs3, NULL, &result);
562     ok(hr == S_OK, "GetAbsolutePathName returned %x, expected S_OK\n", hr);
563     GetFullPathNameW(cur_dir, MAX_PATH, buf, NULL);
564     ok(!lstrcmpiW(buf, result), "result = %s, expected %s\n", wine_dbgstr_w(result), wine_dbgstr_w(buf));
565     SysFreeString(result);
566 
567     find = FindFirstFileW(dir_match2, &fdata);
568     if(find != INVALID_HANDLE_VALUE) {
569         skip("GetAbsolutePathName tests\n");
570         FindClose(find);
571         return;
572     }
573 
574     path = SysAllocString(dir_match1);
575     hr = IFileSystem3_GetAbsolutePathName(fs3, path, &result);
576     ok(hr == S_OK, "GetAbsolutePathName returned %x, expected S_OK\n", hr);
577     GetFullPathNameW(dir_match1, MAX_PATH, buf2, NULL);
578     ok(!lstrcmpiW(buf2, result), "result = %s, expected %s\n", wine_dbgstr_w(result), wine_dbgstr_w(buf2));
579     SysFreeString(result);
580 
581     ok(CreateDirectoryW(dir1, NULL), "CreateDirectory(%s) failed\n", wine_dbgstr_w(dir1));
582     hr = IFileSystem3_GetAbsolutePathName(fs3, path, &result);
583     ok(hr == S_OK, "GetAbsolutePathName returned %x, expected S_OK\n", hr);
584     GetFullPathNameW(dir1, MAX_PATH, buf, NULL);
585     ok(!lstrcmpiW(buf, result) || broken(!lstrcmpiW(buf2, result)), "result = %s, expected %s\n",
586                 wine_dbgstr_w(result), wine_dbgstr_w(buf));
587     SysFreeString(result);
588 
589     ok(CreateDirectoryW(dir2, NULL), "CreateDirectory(%s) failed\n", wine_dbgstr_w(dir2));
590     hr = IFileSystem3_GetAbsolutePathName(fs3, path, &result);
591     ok(hr == S_OK, "GetAbsolutePathName returned %x, expected S_OK\n", hr);
592     if(!lstrcmpiW(buf, result) || !lstrcmpiW(buf2, result)) {
593         ok(!lstrcmpiW(buf, result) || broken(!lstrcmpiW(buf2, result)), "result = %s, expected %s\n",
594                 wine_dbgstr_w(result), wine_dbgstr_w(buf));
595     }else {
596         GetFullPathNameW(dir2, MAX_PATH, buf, NULL);
597         ok(!lstrcmpiW(buf, result), "result = %s, expected %s\n",
598                 wine_dbgstr_w(result), wine_dbgstr_w(buf));
599     }
600     SysFreeString(result);
601 
602     SysFreeString(path);
603     path = SysAllocString(dir_match2);
604     hr = IFileSystem3_GetAbsolutePathName(fs3, path, &result);
605     ok(hr == S_OK, "GetAbsolutePathName returned %x, expected S_OK\n", hr);
606     GetFullPathNameW(dir_match2, MAX_PATH, buf, NULL);
607     ok(!lstrcmpiW(buf, result), "result = %s, expected %s\n", wine_dbgstr_w(result), wine_dbgstr_w(buf));
608     SysFreeString(result);
609     SysFreeString(path);
610 
611     RemoveDirectoryW(dir1);
612     RemoveDirectoryW(dir2);
613 }
614 
615 static void test_GetFile(void)
616 {
617     static const WCHAR slW[] = {'\\',0};
618     BSTR path, str;
619     WCHAR pathW[MAX_PATH];
620     FileAttribute fa;
621     VARIANT size;
622     DWORD gfa, new_gfa;
623     IFile *file;
624     HRESULT hr;
625     HANDLE hf;
626     BOOL ret;
627     DATE date;
628 
629     get_temp_path(NULL, pathW);
630 
631     path = SysAllocString(pathW);
632     hr = IFileSystem3_GetFile(fs3, path, NULL);
633     ok(hr == E_POINTER, "GetFile returned %x, expected E_POINTER\n", hr);
634     hr = IFileSystem3_GetFile(fs3, NULL, &file);
635     ok(hr == E_INVALIDARG, "GetFile returned %x, expected E_INVALIDARG\n", hr);
636 
637     file = (IFile*)0xdeadbeef;
638     hr = IFileSystem3_GetFile(fs3, path, &file);
639     ok(!file, "file != NULL\n");
640     ok(hr == CTL_E_FILENOTFOUND, "GetFile returned %x, expected CTL_E_FILENOTFOUND\n", hr);
641 
642     hf = CreateFileW(pathW, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_READONLY, NULL);
643     if(hf == INVALID_HANDLE_VALUE) {
644         skip("Can't create temporary file\n");
645         SysFreeString(path);
646         return;
647     }
648     CloseHandle(hf);
649 
650     hr = IFileSystem3_GetFile(fs3, path, &file);
651     ok(hr == S_OK, "GetFile returned %x, expected S_OK\n", hr);
652 
653     hr = IFile_get_DateLastModified(file, NULL);
654     ok(hr == E_POINTER, "got 0x%08x\n", hr);
655 
656     date = 0.0;
657     hr = IFile_get_DateLastModified(file, &date);
658     ok(hr == S_OK, "got 0x%08x\n", hr);
659     ok(date > 0.0, "got %f\n", date);
660 
661     hr = IFile_get_Path(file, NULL);
662     ok(hr == E_POINTER, "got 0x%08x\n", hr);
663 
664     hr = IFile_get_Path(file, &str);
665     ok(hr == S_OK, "got 0x%08x\n", hr);
666     ok(!lstrcmpiW(str, pathW), "got %s\n", wine_dbgstr_w(str));
667     SysFreeString(str);
668 
669 #define FILE_ATTR_MASK (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
670         FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE | \
671         FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_COMPRESSED)
672 
673     hr = IFile_get_Attributes(file, &fa);
674     gfa = GetFileAttributesW(pathW) & FILE_ATTR_MASK;
675     ok(hr == S_OK, "get_Attributes returned %x, expected S_OK\n", hr);
676     ok(fa == gfa, "fa = %x, expected %x\n", fa, gfa);
677 
678     hr = IFile_put_Attributes(file, gfa | FILE_ATTRIBUTE_READONLY);
679     ok(hr == S_OK, "put_Attributes failed: %08x\n", hr);
680     new_gfa = GetFileAttributesW(pathW) & FILE_ATTR_MASK;
681     ok(new_gfa == (gfa|FILE_ATTRIBUTE_READONLY), "new_gfa = %x, expected %x\n", new_gfa, gfa|FILE_ATTRIBUTE_READONLY);
682 
683     hr = IFile_get_Attributes(file, &fa);
684     ok(hr == S_OK, "get_Attributes returned %x, expected S_OK\n", hr);
685     ok(fa == new_gfa, "fa = %x, expected %x\n", fa, new_gfa);
686 
687     hr = IFile_put_Attributes(file, gfa);
688     ok(hr == S_OK, "put_Attributes failed: %08x\n", hr);
689     new_gfa = GetFileAttributesW(pathW) & FILE_ATTR_MASK;
690     ok(new_gfa == gfa, "new_gfa = %x, expected %x\n", new_gfa, gfa);
691 
692     hr = IFile_get_Attributes(file, &fa);
693     ok(hr == S_OK, "get_Attributes returned %x, expected S_OK\n", hr);
694     ok(fa == gfa, "fa = %x, expected %x\n", fa, gfa);
695 
696     hr = IFile_get_Size(file, &size);
697     ok(hr == S_OK, "get_Size returned %x, expected S_OK\n", hr);
698     ok(V_VT(&size) == VT_I4, "V_VT(&size) = %d, expected VT_I4\n", V_VT(&size));
699     ok(V_I4(&size) == 0, "V_I4(&size) = %d, expected 0\n", V_I4(&size));
700     IFile_Release(file);
701 
702     hr = IFileSystem3_DeleteFile(fs3, path, FALSE);
703     ok(hr==CTL_E_PERMISSIONDENIED || broken(hr==S_OK),
704             "DeleteFile returned %x, expected CTL_E_PERMISSIONDENIED\n", hr);
705     if(hr != S_OK) {
706         hr = IFileSystem3_DeleteFile(fs3, path, TRUE);
707         ok(hr == S_OK, "DeleteFile returned %x, expected S_OK\n", hr);
708     }
709     hr = IFileSystem3_DeleteFile(fs3, path, TRUE);
710     ok(hr == CTL_E_FILENOTFOUND, "DeleteFile returned %x, expected CTL_E_FILENOTFOUND\n", hr);
711 
712     SysFreeString(path);
713 
714     /* try with directory */
715     lstrcatW(pathW, slW);
716     ret = CreateDirectoryW(pathW, NULL);
717     ok(ret, "got %d, error %d\n", ret, GetLastError());
718 
719     path = SysAllocString(pathW);
720     hr = IFileSystem3_GetFile(fs3, path, &file);
721     ok(hr == CTL_E_FILENOTFOUND, "GetFile returned %x, expected S_OK\n", hr);
722     SysFreeString(path);
723 
724     RemoveDirectoryW(pathW);
725 }
726 
727 static inline BOOL create_file(const WCHAR *name)
728 {
729     HANDLE f = CreateFileW(name, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
730     CloseHandle(f);
731     return f != INVALID_HANDLE_VALUE;
732 }
733 
734 static inline void create_path(const WCHAR *folder, const WCHAR *name, WCHAR *ret)
735 {
736     DWORD len = lstrlenW(folder);
737     memmove(ret, folder, len*sizeof(WCHAR));
738     ret[len] = '\\';
739     memmove(ret+len+1, name, (lstrlenW(name)+1)*sizeof(WCHAR));
740 }
741 
742 static void test_CopyFolder(void)
743 {
744     static const WCHAR filesystem3_dir[] = {'f','i','l','e','s','y','s','t','e','m','3','_','t','e','s','t',0};
745     static const WCHAR s1[] = {'s','r','c','1',0};
746     static const WCHAR s[] = {'s','r','c','*',0};
747     static const WCHAR d[] = {'d','s','t',0};
748     static const WCHAR empty[] = {0};
749 
750     WCHAR tmp[MAX_PATH];
751     BSTR bsrc, bdst;
752     HRESULT hr;
753 
754     if(!CreateDirectoryW(filesystem3_dir, NULL)) {
755         skip("can't create temporary directory\n");
756         return;
757     }
758 
759     create_path(filesystem3_dir, s1, tmp);
760     bsrc = SysAllocString(tmp);
761     create_path(filesystem3_dir, d, tmp);
762     bdst = SysAllocString(tmp);
763     hr = IFileSystem3_CopyFile(fs3, bsrc, bdst, VARIANT_TRUE);
764     ok(hr == CTL_E_FILENOTFOUND, "CopyFile returned %x, expected CTL_E_FILENOTFOUND\n", hr);
765 
766     hr = IFileSystem3_CopyFolder(fs3, bsrc, bdst, VARIANT_TRUE);
767     ok(hr == CTL_E_PATHNOTFOUND, "CopyFolder returned %x, expected CTL_E_PATHNOTFOUND\n", hr);
768 
769     ok(create_file(bsrc), "can't create %s file\n", wine_dbgstr_w(bsrc));
770     hr = IFileSystem3_CopyFile(fs3, bsrc, bdst, VARIANT_TRUE);
771     ok(hr == S_OK, "CopyFile returned %x, expected S_OK\n", hr);
772 
773     hr = IFileSystem3_CopyFolder(fs3, bsrc, bdst, VARIANT_TRUE);
774     ok(hr == CTL_E_PATHNOTFOUND, "CopyFolder returned %x, expected CTL_E_PATHNOTFOUND\n", hr);
775 
776     hr = IFileSystem3_DeleteFile(fs3, bsrc, VARIANT_FALSE);
777     ok(hr == S_OK, "DeleteFile returned %x, expected S_OK\n", hr);
778 
779     ok(CreateDirectoryW(bsrc, NULL), "can't create %s\n", wine_dbgstr_w(bsrc));
780     hr = IFileSystem3_CopyFile(fs3, bsrc, bdst, VARIANT_TRUE);
781     ok(hr == CTL_E_FILENOTFOUND, "CopyFile returned %x, expected CTL_E_FILENOTFOUND\n", hr);
782 
783     hr = IFileSystem3_CopyFolder(fs3, bsrc, bdst, VARIANT_TRUE);
784     ok(hr == CTL_E_FILEALREADYEXISTS, "CopyFolder returned %x, expected CTL_E_FILEALREADYEXISTS\n", hr);
785 
786     hr = IFileSystem3_DeleteFile(fs3, bdst, VARIANT_TRUE);
787     ok(hr == S_OK, "DeleteFile returned %x, expected S_OK\n", hr);
788 
789     hr = IFileSystem3_CopyFolder(fs3, bsrc, bdst, VARIANT_TRUE);
790     ok(hr == S_OK, "CopyFolder returned %x, expected S_OK\n", hr);
791 
792     hr = IFileSystem3_CopyFolder(fs3, bsrc, bdst, VARIANT_TRUE);
793     ok(hr == S_OK, "CopyFolder returned %x, expected S_OK\n", hr);
794     create_path(tmp, s1, tmp);
795     ok(GetFileAttributesW(tmp) == INVALID_FILE_ATTRIBUTES,
796             "%s file exists\n", wine_dbgstr_w(tmp));
797 
798     create_path(filesystem3_dir, d, tmp);
799     create_path(tmp, empty, tmp);
800     SysFreeString(bdst);
801     bdst = SysAllocString(tmp);
802     hr = IFileSystem3_CopyFolder(fs3, bsrc, bdst, VARIANT_TRUE);
803     ok(hr == S_OK, "CopyFolder returned %x, expected S_OK\n", hr);
804     create_path(tmp, s1, tmp);
805     ok(GetFileAttributesW(tmp) != INVALID_FILE_ATTRIBUTES,
806             "%s directory doesn't exist\n", wine_dbgstr_w(tmp));
807     ok(RemoveDirectoryW(tmp), "can't remove %s directory\n", wine_dbgstr_w(tmp));
808     create_path(filesystem3_dir, d, tmp);
809     SysFreeString(bdst);
810     bdst = SysAllocString(tmp);
811 
812 
813     create_path(filesystem3_dir, s, tmp);
814     SysFreeString(bsrc);
815     bsrc = SysAllocString(tmp);
816     hr = IFileSystem3_CopyFolder(fs3, bsrc, bdst, VARIANT_TRUE);
817     ok(hr == S_OK, "CopyFolder returned %x, expected S_OK\n", hr);
818     create_path(filesystem3_dir, d, tmp);
819     create_path(tmp, s1, tmp);
820     ok(GetFileAttributesW(tmp) != INVALID_FILE_ATTRIBUTES,
821             "%s directory doesn't exist\n", wine_dbgstr_w(tmp));
822 
823     hr = IFileSystem3_DeleteFolder(fs3, bdst, VARIANT_FALSE);
824     ok(hr == S_OK, "DeleteFolder returned %x, expected S_OK\n", hr);
825 
826     hr = IFileSystem3_CopyFolder(fs3, bsrc, bdst, VARIANT_TRUE);
827     ok(hr == CTL_E_PATHNOTFOUND, "CopyFolder returned %x, expected CTL_E_PATHNOTFOUND\n", hr);
828 
829     create_path(filesystem3_dir, s1, tmp);
830     SysFreeString(bsrc);
831     bsrc = SysAllocString(tmp);
832     create_path(tmp, s1, tmp);
833     ok(create_file(tmp), "can't create %s file\n", wine_dbgstr_w(tmp));
834     hr = IFileSystem3_CopyFolder(fs3, bsrc, bdst, VARIANT_FALSE);
835     ok(hr == S_OK, "CopyFolder returned %x, expected S_OK\n", hr);
836 
837     hr = IFileSystem3_CopyFolder(fs3, bsrc, bdst, VARIANT_FALSE);
838     ok(hr == CTL_E_FILEALREADYEXISTS, "CopyFolder returned %x, expected CTL_E_FILEALREADYEXISTS\n", hr);
839 
840     hr = IFileSystem3_CopyFolder(fs3, bsrc, bdst, VARIANT_TRUE);
841     ok(hr == S_OK, "CopyFolder returned %x, expected S_OK\n", hr);
842     SysFreeString(bsrc);
843     SysFreeString(bdst);
844 
845     bsrc = SysAllocString(filesystem3_dir);
846     hr = IFileSystem3_DeleteFolder(fs3, bsrc, VARIANT_FALSE);
847     ok(hr == S_OK, "DeleteFolder returned %x, expected S_OK\n", hr);
848     SysFreeString(bsrc);
849 }
850 
851 static BSTR bstr_from_str(const char *str)
852 {
853     int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
854     BSTR ret = SysAllocStringLen(NULL, len - 1);  /* NUL character added automatically */
855     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
856     return ret;
857 }
858 
859 struct buildpath_test
860 {
861     const char *path;
862     const char *name;
863     const char *result;
864 };
865 
866 static struct buildpath_test buildpath_data[] =
867 {
868     { "C:\\path", "..\\name.tmp", "C:\\path\\..\\name.tmp" },
869     { "C:\\path", "\\name.tmp", "C:\\path\\name.tmp" },
870     { "C:\\path", "name.tmp", "C:\\path\\name.tmp" },
871     { "C:\\path\\", "name.tmp", "C:\\path\\name.tmp" },
872     { "C:\\path", "\\\\name.tmp", "C:\\path\\\\name.tmp" },
873     { "C:\\path\\", "\\name.tmp", "C:\\path\\name.tmp" },
874     { "C:\\path\\", "\\\\name.tmp", "C:\\path\\\\name.tmp" },
875     { "C:\\path\\\\", "\\\\name.tmp", "C:\\path\\\\\\name.tmp" },
876     { "C:\\\\", "\\name.tmp", "C:\\\\name.tmp" },
877     { "C:", "name.tmp", "C:name.tmp" },
878     { "C:", "\\\\name.tmp", "C:\\\\name.tmp" },
879     { NULL }
880 };
881 
882 static void test_BuildPath(void)
883 {
884     struct buildpath_test *ptr = buildpath_data;
885     BSTR ret, path;
886     HRESULT hr;
887     int i = 0;
888 
889     hr = IFileSystem3_BuildPath(fs3, NULL, NULL, NULL);
890     ok(hr == E_POINTER, "got 0x%08x\n", hr);
891 
892     ret = (BSTR)0xdeadbeef;
893     hr = IFileSystem3_BuildPath(fs3, NULL, NULL, &ret);
894     ok(hr == S_OK, "got 0x%08x\n", hr);
895     ok(*ret == 0, "got %p\n", ret);
896     SysFreeString(ret);
897 
898     ret = (BSTR)0xdeadbeef;
899     path = bstr_from_str("path");
900     hr = IFileSystem3_BuildPath(fs3, path, NULL, &ret);
901     ok(hr == S_OK, "got 0x%08x\n", hr);
902     ok(!lstrcmpW(ret, path), "got %s\n", wine_dbgstr_w(ret));
903     SysFreeString(ret);
904     SysFreeString(path);
905 
906     ret = (BSTR)0xdeadbeef;
907     path = bstr_from_str("path");
908     hr = IFileSystem3_BuildPath(fs3, NULL, path, &ret);
909     ok(hr == S_OK, "got 0x%08x\n", hr);
910     ok(!lstrcmpW(ret, path), "got %s\n", wine_dbgstr_w(ret));
911     SysFreeString(ret);
912     SysFreeString(path);
913 
914     while (ptr->path)
915     {
916         BSTR name, result;
917 
918         ret = NULL;
919         path = bstr_from_str(ptr->path);
920         name = bstr_from_str(ptr->name);
921         result = bstr_from_str(ptr->result);
922         hr = IFileSystem3_BuildPath(fs3, path, name, &ret);
923         ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
924         if (hr == S_OK)
925         {
926             ok(!lstrcmpW(ret, result), "%d: got wrong path %s, expected %s\n", i, wine_dbgstr_w(ret),
927                 wine_dbgstr_w(result));
928             SysFreeString(ret);
929         }
930         SysFreeString(path);
931         SysFreeString(name);
932         SysFreeString(result);
933 
934         i++;
935         ptr++;
936     }
937 }
938 
939 static void test_GetFolder(void)
940 {
941     static const WCHAR dummyW[] = {'d','u','m','m','y',0};
942     WCHAR buffW[MAX_PATH];
943     IFolder *folder;
944     HRESULT hr;
945     BSTR str;
946 
947     folder = (void*)0xdeadbeef;
948     hr = IFileSystem3_GetFolder(fs3, NULL, &folder);
949     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
950     ok(folder == NULL, "got %p\n", folder);
951 
952     hr = IFileSystem3_GetFolder(fs3, NULL, NULL);
953     ok(hr == E_POINTER, "got 0x%08x\n", hr);
954 
955     /* something that doesn't exist */
956     str = SysAllocString(dummyW);
957 
958     hr = IFileSystem3_GetFolder(fs3, str, NULL);
959     ok(hr == E_POINTER, "got 0x%08x\n", hr);
960 
961     folder = (void*)0xdeadbeef;
962     hr = IFileSystem3_GetFolder(fs3, str, &folder);
963     ok(hr == CTL_E_PATHNOTFOUND, "got 0x%08x\n", hr);
964     ok(folder == NULL, "got %p\n", folder);
965     SysFreeString(str);
966 
967     GetWindowsDirectoryW(buffW, MAX_PATH);
968     str = SysAllocString(buffW);
969     hr = IFileSystem3_GetFolder(fs3, str, &folder);
970     ok(hr == S_OK, "got 0x%08x\n", hr);
971     SysFreeString(str);
972     test_provideclassinfo(folder, &CLSID_Folder);
973     IFolder_Release(folder);
974 }
975 
976 static void _test_clone(IEnumVARIANT *enumvar, BOOL position_inherited, LONG count, int line)
977 {
978     HRESULT hr;
979     IEnumVARIANT *clone;
980     ULONG fetched;
981     VARIANT var, var2;
982 
983     hr = IEnumVARIANT_Reset(enumvar);
984     ok(hr == S_OK, "%d: got 0x%08x\n", line, hr);
985 
986     VariantInit(&var);
987     fetched = -1;
988     hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched);
989     ok(hr == S_OK, "%d: got 0x%08x\n", line, hr);
990     ok(fetched == 1, "%d: got %d\n", line, fetched);
991 
992     /* clone enumerator */
993     hr = IEnumVARIANT_Clone(enumvar, &clone);
994     ok(hr == S_OK, "%d: got 0x%08x\n", line, hr);
995     ok(clone != enumvar, "%d: got %p, %p\n", line, enumvar, clone);
996 
997     /* check if clone inherits position */
998     VariantInit(&var2);
999     fetched = -1;
1000     hr = IEnumVARIANT_Next(clone, 1, &var2, &fetched);
1001     if (position_inherited && count == 1)
1002     {
1003         ok(hr == S_FALSE, "%d: got 0x%08x\n", line, hr);
1004         ok(fetched == 0, "%d: got %d\n", line, fetched);
1005     }
1006     else
1007     {
1008         ok(hr == S_OK, "%d: got 0x%08x\n", line, hr);
1009         ok(fetched == 1, "%d: got %d\n", line, fetched);
1010         if (!position_inherited)
1011             todo_wine ok(V_DISPATCH(&var) == V_DISPATCH(&var2), "%d: values don't match\n", line);
1012         else
1013         {
1014             fetched = -1;
1015             hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched);
1016             ok(hr == S_OK, "%d: got 0x%08x\n", line, hr);
1017             ok(fetched == 1, "%d: got %d\n", line, fetched);
1018             todo_wine ok(V_DISPATCH(&var) == V_DISPATCH(&var2), "%d: values don't match\n", line);
1019         }
1020     }
1021 
1022     VariantClear(&var2);
1023     VariantClear(&var);
1024     IEnumVARIANT_Release(clone);
1025 
1026     hr = IEnumVARIANT_Reset(enumvar);
1027     ok(hr == S_OK, "%d: got 0x%08x\n", line, hr);
1028 }
1029 #define test_clone(a, b, c) _test_clone(a, b, c, __LINE__)
1030 
1031 /* Please keep the tests for IFolderCollection and IFileCollection in sync */
1032 static void test_FolderCollection(void)
1033 {
1034     static const WCHAR fooW[] = {'f','o','o',0};
1035     static const WCHAR aW[] = {'\\','a',0};
1036     static const WCHAR bW[] = {'\\','b',0};
1037     static const WCHAR cW[] = {'\\','c',0};
1038     IFolderCollection *folders;
1039     WCHAR buffW[MAX_PATH], pathW[MAX_PATH];
1040     IEnumVARIANT *enumvar;
1041     LONG count, ref, ref2, i;
1042     IUnknown *unk, *unk2;
1043     IFolder *folder;
1044     ULONG fetched;
1045     VARIANT var, var2[2];
1046     HRESULT hr;
1047     BSTR str;
1048     int found_a = 0, found_b = 0, found_c = 0;
1049 
1050     get_temp_path(fooW, buffW);
1051     CreateDirectoryW(buffW, NULL);
1052 
1053     str = SysAllocString(buffW);
1054     hr = IFileSystem3_GetFolder(fs3, str, &folder);
1055     ok(hr == S_OK, "got 0x%08x\n", hr);
1056     SysFreeString(str);
1057 
1058     hr = IFolder_get_SubFolders(folder, NULL);
1059     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1060 
1061     hr = IFolder_get_Path(folder, NULL);
1062     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1063 
1064     hr = IFolder_get_Path(folder, &str);
1065     ok(hr == S_OK, "got 0x%08x\n", hr);
1066     ok(!lstrcmpiW(buffW, str), "got %s, expected %s\n", wine_dbgstr_w(str), wine_dbgstr_w(buffW));
1067     SysFreeString(str);
1068 
1069     lstrcpyW(pathW, buffW);
1070     lstrcatW(pathW, aW);
1071     CreateDirectoryW(pathW, NULL);
1072 
1073     lstrcpyW(pathW, buffW);
1074     lstrcatW(pathW, bW);
1075     CreateDirectoryW(pathW, NULL);
1076 
1077     hr = IFolder_get_SubFolders(folder, &folders);
1078     ok(hr == S_OK, "got 0x%08x\n", hr);
1079     test_provideclassinfo(folders, &CLSID_Folders);
1080     IFolder_Release(folder);
1081 
1082     count = 0;
1083     hr = IFolderCollection_get_Count(folders, &count);
1084     ok(hr == S_OK, "got 0x%08x\n", hr);
1085     ok(count == 2, "got %d\n", count);
1086 
1087     lstrcpyW(pathW, buffW);
1088     lstrcatW(pathW, cW);
1089     CreateDirectoryW(pathW, NULL);
1090 
1091     /* every time property is requested it scans directory */
1092     count = 0;
1093     hr = IFolderCollection_get_Count(folders, &count);
1094     ok(hr == S_OK, "got 0x%08x\n", hr);
1095     ok(count == 3, "got %d\n", count);
1096 
1097     hr = IFolderCollection_get__NewEnum(folders, NULL);
1098     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1099 
1100     hr = IFolderCollection_QueryInterface(folders, &IID_IEnumVARIANT, (void**)&unk);
1101     ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1102 
1103     /* NewEnum creates new instance each time it's called */
1104     ref = GET_REFCOUNT(folders);
1105 
1106     unk = NULL;
1107     hr = IFolderCollection_get__NewEnum(folders, &unk);
1108     ok(hr == S_OK, "got 0x%08x\n", hr);
1109 
1110     ref2 = GET_REFCOUNT(folders);
1111     ok(ref2 == ref + 1, "got %d, %d\n", ref2, ref);
1112 
1113     unk2 = NULL;
1114     hr = IFolderCollection_get__NewEnum(folders, &unk2);
1115     ok(hr == S_OK, "got 0x%08x\n", hr);
1116     ok(unk != unk2, "got %p, %p\n", unk2, unk);
1117     IUnknown_Release(unk2);
1118 
1119     /* now get IEnumVARIANT */
1120     ref = GET_REFCOUNT(folders);
1121     hr = IUnknown_QueryInterface(unk, &IID_IEnumVARIANT, (void**)&enumvar);
1122     ok(hr == S_OK, "got 0x%08x\n", hr);
1123     ref2 = GET_REFCOUNT(folders);
1124     ok(ref2 == ref, "got %d, %d\n", ref2, ref);
1125 
1126     test_clone(enumvar, FALSE, count);
1127 
1128     for (i = 0; i < 3; i++)
1129     {
1130         VariantInit(&var);
1131         fetched = 0;
1132         hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched);
1133         ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
1134         ok(fetched == 1, "%d: got %d\n", i, fetched);
1135         ok(V_VT(&var) == VT_DISPATCH, "%d: got type %d\n", i, V_VT(&var));
1136 
1137         hr = IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IFolder, (void**)&folder);
1138         ok(hr == S_OK, "got 0x%08x\n", hr);
1139 
1140         str = NULL;
1141         hr = IFolder_get_Name(folder, &str);
1142         ok(hr == S_OK, "got 0x%08x\n", hr);
1143         if (!lstrcmpW(str, aW + 1))
1144             found_a++;
1145         else if (!lstrcmpW(str, bW + 1))
1146             found_b++;
1147         else if (!lstrcmpW(str, cW + 1))
1148             found_c++;
1149         else
1150             ok(0, "unexpected folder %s was found\n", wine_dbgstr_w(str));
1151         SysFreeString(str);
1152 
1153         IFolder_Release(folder);
1154         VariantClear(&var);
1155     }
1156 
1157     ok(found_a == 1 && found_b == 1 && found_c == 1,
1158        "each folder should be found 1 time instead of %d/%d/%d\n",
1159        found_a, found_b, found_c);
1160 
1161     VariantInit(&var);
1162     fetched = -1;
1163     hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched);
1164     ok(hr == S_FALSE, "got 0x%08x\n", hr);
1165     ok(fetched == 0, "got %d\n", fetched);
1166 
1167     hr = IEnumVARIANT_Reset(enumvar);
1168     ok(hr == S_OK, "got 0x%08x\n", hr);
1169     hr = IEnumVARIANT_Skip(enumvar, 2);
1170     ok(hr == S_OK, "got 0x%08x\n", hr);
1171     hr = IEnumVARIANT_Skip(enumvar, 0);
1172     ok(hr == S_OK, "got 0x%08x\n", hr);
1173 
1174     VariantInit(&var2[0]);
1175     VariantInit(&var2[1]);
1176     fetched = -1;
1177     hr = IEnumVARIANT_Next(enumvar, 0, var2, &fetched);
1178     ok(hr == S_OK, "got 0x%08x\n", hr);
1179     ok(fetched == 0, "got %d\n", fetched);
1180     fetched = -1;
1181     hr = IEnumVARIANT_Next(enumvar, 2, var2, &fetched);
1182     ok(hr == S_FALSE, "got 0x%08x\n", hr);
1183     ok(fetched == 1, "got %d\n", fetched);
1184     ok(V_VT(&var2[0]) == VT_DISPATCH, "got type %d\n", V_VT(&var2[0]));
1185     VariantClear(&var2[0]);
1186     VariantClear(&var2[1]);
1187 
1188     IEnumVARIANT_Release(enumvar);
1189     IUnknown_Release(unk);
1190 
1191     lstrcpyW(pathW, buffW);
1192     lstrcatW(pathW, aW);
1193     RemoveDirectoryW(pathW);
1194     lstrcpyW(pathW, buffW);
1195     lstrcatW(pathW, bW);
1196     RemoveDirectoryW(pathW);
1197     lstrcpyW(pathW, buffW);
1198     lstrcatW(pathW, cW);
1199     RemoveDirectoryW(pathW);
1200     RemoveDirectoryW(buffW);
1201 
1202     IFolderCollection_Release(folders);
1203 }
1204 
1205 /* Please keep the tests for IFolderCollection and IFileCollection in sync */
1206 static void test_FileCollection(void)
1207 {
1208     static const WCHAR fooW[] = {'\\','f','o','o',0};
1209     static const WCHAR aW[] = {'\\','a',0};
1210     static const WCHAR bW[] = {'\\','b',0};
1211     static const WCHAR cW[] = {'\\','c',0};
1212     WCHAR buffW[MAX_PATH], pathW[MAX_PATH];
1213     IFolder *folder;
1214     IFileCollection *files;
1215     IFile *file;
1216     IEnumVARIANT *enumvar;
1217     LONG count, ref, ref2, i;
1218     IUnknown *unk, *unk2;
1219     ULONG fetched;
1220     VARIANT var, var2[2];
1221     HRESULT hr;
1222     BSTR str;
1223     HANDLE file_a, file_b, file_c;
1224     int found_a = 0, found_b = 0, found_c = 0;
1225 
1226     get_temp_path(fooW, buffW);
1227     CreateDirectoryW(buffW, NULL);
1228 
1229     str = SysAllocString(buffW);
1230     hr = IFileSystem3_GetFolder(fs3, str, &folder);
1231     ok(hr == S_OK, "got 0x%08x\n", hr);
1232     SysFreeString(str);
1233 
1234     hr = IFolder_get_Files(folder, NULL);
1235     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1236 
1237     lstrcpyW(pathW, buffW);
1238     lstrcatW(pathW, aW);
1239     file_a = CreateFileW(pathW, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1240                          FILE_FLAG_DELETE_ON_CLOSE, 0);
1241     lstrcpyW(pathW, buffW);
1242     lstrcatW(pathW, bW);
1243     file_b = CreateFileW(pathW, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1244                          FILE_FLAG_DELETE_ON_CLOSE, 0);
1245 
1246     hr = IFolder_get_Files(folder, &files);
1247     ok(hr == S_OK, "got 0x%08x\n", hr);
1248     test_provideclassinfo(files, &CLSID_Files);
1249     IFolder_Release(folder);
1250 
1251     count = 0;
1252     hr = IFileCollection_get_Count(files, &count);
1253     ok(hr == S_OK, "got 0x%08x\n", hr);
1254     ok(count == 2, "got %d\n", count);
1255 
1256     lstrcpyW(pathW, buffW);
1257     lstrcatW(pathW, cW);
1258     file_c = CreateFileW(pathW, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1259                          FILE_FLAG_DELETE_ON_CLOSE, 0);
1260 
1261     /* every time property is requested it scans directory */
1262     count = 0;
1263     hr = IFileCollection_get_Count(files, &count);
1264     ok(hr == S_OK, "got 0x%08x\n", hr);
1265     ok(count == 3, "got %d\n", count);
1266 
1267     hr = IFileCollection_get__NewEnum(files, NULL);
1268     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1269 
1270     hr = IFileCollection_QueryInterface(files, &IID_IEnumVARIANT, (void**)&unk);
1271     ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1272 
1273     /* NewEnum creates new instance each time it's called */
1274     ref = GET_REFCOUNT(files);
1275 
1276     unk = NULL;
1277     hr = IFileCollection_get__NewEnum(files, &unk);
1278     ok(hr == S_OK, "got 0x%08x\n", hr);
1279 
1280     ref2 = GET_REFCOUNT(files);
1281     ok(ref2 == ref + 1, "got %d, %d\n", ref2, ref);
1282 
1283     unk2 = NULL;
1284     hr = IFileCollection_get__NewEnum(files, &unk2);
1285     ok(hr == S_OK, "got 0x%08x\n", hr);
1286     ok(unk != unk2, "got %p, %p\n", unk2, unk);
1287     IUnknown_Release(unk2);
1288 
1289     /* now get IEnumVARIANT */
1290     ref = GET_REFCOUNT(files);
1291     hr = IUnknown_QueryInterface(unk, &IID_IEnumVARIANT, (void**)&enumvar);
1292     ok(hr == S_OK, "got 0x%08x\n", hr);
1293     ref2 = GET_REFCOUNT(files);
1294     ok(ref2 == ref, "got %d, %d\n", ref2, ref);
1295 
1296     test_clone(enumvar, FALSE, count);
1297 
1298     for (i = 0; i < 3; i++)
1299     {
1300         VariantInit(&var);
1301         fetched = 0;
1302         hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched);
1303         ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
1304         ok(fetched == 1, "%d: got %d\n", i, fetched);
1305         ok(V_VT(&var) == VT_DISPATCH, "%d: got type %d\n", i, V_VT(&var));
1306 
1307         hr = IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IFile, (void **)&file);
1308         ok(hr == S_OK, "got 0x%08x\n", hr);
1309         test_provideclassinfo(file, &CLSID_File);
1310 
1311         str = NULL;
1312         hr = IFile_get_Name(file, &str);
1313         ok(hr == S_OK, "got 0x%08x\n", hr);
1314         if (!lstrcmpW(str, aW + 1))
1315             found_a++;
1316         else if (!lstrcmpW(str, bW + 1))
1317             found_b++;
1318         else if (!lstrcmpW(str, cW + 1))
1319             found_c++;
1320         else
1321             ok(0, "unexpected file %s was found\n", wine_dbgstr_w(str));
1322         SysFreeString(str);
1323 
1324         IFile_Release(file);
1325         VariantClear(&var);
1326     }
1327 
1328     ok(found_a == 1 && found_b == 1 && found_c == 1,
1329        "each file should be found 1 time instead of %d/%d/%d\n",
1330        found_a, found_b, found_c);
1331 
1332     VariantInit(&var);
1333     fetched = -1;
1334     hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched);
1335     ok(hr == S_FALSE, "got 0x%08x\n", hr);
1336     ok(fetched == 0, "got %d\n", fetched);
1337 
1338     hr = IEnumVARIANT_Reset(enumvar);
1339     ok(hr == S_OK, "got 0x%08x\n", hr);
1340     hr = IEnumVARIANT_Skip(enumvar, 2);
1341     ok(hr == S_OK, "got 0x%08x\n", hr);
1342     hr = IEnumVARIANT_Skip(enumvar, 0);
1343     ok(hr == S_OK, "got 0x%08x\n", hr);
1344 
1345     VariantInit(&var2[0]);
1346     VariantInit(&var2[1]);
1347     fetched = -1;
1348     hr = IEnumVARIANT_Next(enumvar, 0, var2, &fetched);
1349     ok(hr == S_OK, "got 0x%08x\n", hr);
1350     ok(fetched == 0, "got %d\n", fetched);
1351     fetched = -1;
1352     hr = IEnumVARIANT_Next(enumvar, 2, var2, &fetched);
1353     ok(hr == S_FALSE, "got 0x%08x\n", hr);
1354     ok(fetched == 1, "got %d\n", fetched);
1355     ok(V_VT(&var2[0]) == VT_DISPATCH, "got type %d\n", V_VT(&var2[0]));
1356     VariantClear(&var2[0]);
1357     VariantClear(&var2[1]);
1358 
1359     IEnumVARIANT_Release(enumvar);
1360     IUnknown_Release(unk);
1361 
1362     CloseHandle(file_a);
1363     CloseHandle(file_b);
1364     CloseHandle(file_c);
1365     RemoveDirectoryW(buffW);
1366 
1367     IFileCollection_Release(files);
1368 }
1369 
1370 static void test_DriveCollection(void)
1371 {
1372     IDriveCollection *drives;
1373     IEnumVARIANT *enumvar;
1374     ULONG fetched;
1375     VARIANT var;
1376     HRESULT hr;
1377     LONG count;
1378 
1379     hr = IFileSystem3_get_Drives(fs3, &drives);
1380     ok(hr == S_OK, "got 0x%08x\n", hr);
1381 
1382     test_provideclassinfo(drives, &CLSID_Drives);
1383 
1384     hr = IDriveCollection_get__NewEnum(drives, (IUnknown**)&enumvar);
1385     ok(hr == S_OK, "got 0x%08x\n", hr);
1386 
1387     hr = IDriveCollection_get_Count(drives, NULL);
1388     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1389 
1390     count = 0;
1391     hr = IDriveCollection_get_Count(drives, &count);
1392     ok(hr == S_OK, "got 0x%08x\n", hr);
1393     ok(count > 0, "got %d\n", count);
1394 
1395     V_VT(&var) = VT_EMPTY;
1396     fetched = -1;
1397     hr = IEnumVARIANT_Next(enumvar, 0, &var, &fetched);
1398     ok(hr == S_OK, "got 0x%08x\n", hr);
1399     ok(fetched == 0, "got %d\n", fetched);
1400 
1401     hr = IEnumVARIANT_Skip(enumvar, 0);
1402     ok(hr == S_OK, "got 0x%08x\n", hr);
1403 
1404     hr = IEnumVARIANT_Skip(enumvar, count);
1405     ok(hr == S_OK, "got 0x%08x\n", hr);
1406 
1407     hr = IEnumVARIANT_Skip(enumvar, 1);
1408     ok(hr == S_FALSE, "got 0x%08x\n", hr);
1409 
1410     test_clone(enumvar, TRUE, count);
1411 
1412     while (IEnumVARIANT_Next(enumvar, 1, &var, &fetched) == S_OK) {
1413         IDrive *drive = (IDrive*)V_DISPATCH(&var);
1414         DriveTypeConst type;
1415         BSTR str;
1416 
1417         hr = IDrive_get_DriveType(drive, &type);
1418         ok(hr == S_OK, "got 0x%08x\n", hr);
1419 
1420         hr = IDrive_get_DriveLetter(drive, NULL);
1421         ok(hr == E_POINTER, "got 0x%08x\n", hr);
1422 
1423         hr = IDrive_get_DriveLetter(drive, &str);
1424         ok(hr == S_OK, "got 0x%08x\n", hr);
1425         ok(SysStringLen(str) == 1, "got string %s\n", wine_dbgstr_w(str));
1426         SysFreeString(str);
1427 
1428         hr = IDrive_get_IsReady(drive, NULL);
1429         ok(hr == E_POINTER, "got 0x%08x\n", hr);
1430 
1431         hr = IDrive_get_TotalSize(drive, NULL);
1432         ok(hr == E_POINTER, "got 0x%08x\n", hr);
1433 
1434         hr = IDrive_get_AvailableSpace(drive, NULL);
1435         ok(hr == E_POINTER, "got 0x%08x\n", hr);
1436 
1437         hr = IDrive_get_FreeSpace(drive, NULL);
1438         ok(hr == E_POINTER, "got 0x%08x\n", hr);
1439 
1440         if (type == Fixed) {
1441             VARIANT_BOOL ready = VARIANT_FALSE;
1442             VARIANT size;
1443 
1444             hr = IDrive_get_IsReady(drive, &ready);
1445             ok(hr == S_OK, "got 0x%08x\n", hr);
1446             ok(ready == VARIANT_TRUE, "got %x\n", ready);
1447 
1448             if (ready != VARIANT_TRUE) {
1449                 hr = IDrive_get_DriveLetter(drive, &str);
1450                 ok(hr == S_OK, "got 0x%08x\n", hr);
1451 
1452                 skip("Drive %s is not ready, skipping some tests\n", wine_dbgstr_w(str));
1453 
1454                 VariantClear(&var);
1455                 SysFreeString(str);
1456                 continue;
1457             }
1458 
1459             V_VT(&size) = VT_EMPTY;
1460             hr = IDrive_get_TotalSize(drive, &size);
1461             ok(hr == S_OK, "got 0x%08x\n", hr);
1462             ok(V_VT(&size) == VT_R8 || V_VT(&size) == VT_I4, "got %d\n", V_VT(&size));
1463             if (V_VT(&size) == VT_R8)
1464                 ok(V_R8(&size) > 0, "got %f\n", V_R8(&size));
1465             else
1466                 ok(V_I4(&size) > 0, "got %d\n", V_I4(&size));
1467 
1468             V_VT(&size) = VT_EMPTY;
1469             hr = IDrive_get_AvailableSpace(drive, &size);
1470             ok(hr == S_OK, "got 0x%08x\n", hr);
1471             ok(V_VT(&size) == VT_R8 || V_VT(&size) == VT_I4, "got %d\n", V_VT(&size));
1472             if (V_VT(&size) == VT_R8)
1473                 ok(V_R8(&size) > (double)INT_MAX, "got %f\n", V_R8(&size));
1474             else
1475                 ok(V_I4(&size) > 0, "got %d\n", V_I4(&size));
1476 
1477             V_VT(&size) = VT_EMPTY;
1478             hr = IDrive_get_FreeSpace(drive, &size);
1479             ok(hr == S_OK, "got 0x%08x\n", hr);
1480             ok(V_VT(&size) == VT_R8 || V_VT(&size) == VT_I4, "got %d\n", V_VT(&size));
1481             if (V_VT(&size) == VT_R8)
1482                 ok(V_R8(&size) > 0, "got %f\n", V_R8(&size));
1483             else
1484                 ok(V_I4(&size) > 0, "got %d\n", V_I4(&size));
1485         }
1486         VariantClear(&var);
1487     }
1488 
1489     IEnumVARIANT_Release(enumvar);
1490     IDriveCollection_Release(drives);
1491 }
1492 
1493 static void get_temp_filepath(const WCHAR *filename, WCHAR *path, WCHAR *dir)
1494 {
1495     static const WCHAR scrrunW[] = {'s','c','r','r','u','n','\\',0};
1496 
1497     GetTempPathW(MAX_PATH, path);
1498     lstrcatW(path, scrrunW);
1499     lstrcpyW(dir, path);
1500     lstrcatW(path, filename);
1501 }
1502 
1503 static void test_CreateTextFile(void)
1504 {
1505     WCHAR pathW[MAX_PATH], dirW[MAX_PATH], buffW[10];
1506     ITextStream *stream;
1507     BSTR nameW, str;
1508     HANDLE file;
1509     HRESULT hr;
1510     BOOL ret;
1511 
1512     get_temp_filepath(testfileW, pathW, dirW);
1513 
1514     /* dir doesn't exist */
1515     nameW = SysAllocString(pathW);
1516     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_FALSE, &stream);
1517     ok(hr == CTL_E_PATHNOTFOUND, "got 0x%08x\n", hr);
1518 
1519     ret = CreateDirectoryW(dirW, NULL);
1520     ok(ret, "got %d, %d\n", ret, GetLastError());
1521 
1522     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_FALSE, &stream);
1523     ok(hr == S_OK, "got 0x%08x\n", hr);
1524 
1525     test_provideclassinfo(stream, &CLSID_TextStream);
1526 
1527     hr = ITextStream_Read(stream, 1, &str);
1528     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
1529 
1530     hr = ITextStream_Close(stream);
1531     ok(hr == S_OK, "got 0x%08x\n", hr);
1532 
1533     hr = ITextStream_Read(stream, 1, &str);
1534     ok(hr == CTL_E_BADFILEMODE || hr == E_VAR_NOT_SET, "got 0x%08x\n", hr);
1535 
1536     hr = ITextStream_Close(stream);
1537     ok(hr == S_FALSE || hr == E_VAR_NOT_SET, "got 0x%08x\n", hr);
1538 
1539     ITextStream_Release(stream);
1540 
1541     /* check it's created */
1542     file = CreateFileW(pathW, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1543     ok(file != INVALID_HANDLE_VALUE, "got %p\n", file);
1544     CloseHandle(file);
1545 
1546     /* try to create again with no-overwrite mode */
1547     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_FALSE, &stream);
1548     ok(hr == CTL_E_FILEALREADYEXISTS, "got 0x%08x\n", hr);
1549 
1550     /* now overwrite */
1551     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_TRUE, VARIANT_FALSE, &stream);
1552     ok(hr == S_OK, "got 0x%08x\n", hr);
1553     ITextStream_Release(stream);
1554 
1555     /* overwrite in Unicode mode, check for BOM */
1556     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_TRUE, VARIANT_TRUE, &stream);
1557     ok(hr == S_OK, "got 0x%08x\n", hr);
1558     ITextStream_Release(stream);
1559 
1560     /* File was created in Unicode mode, it contains 0xfffe BOM. Opening it in non-Unicode mode
1561        treats BOM like a valuable data with appropriate CP_ACP -> WCHAR conversion. */
1562     buffW[0] = 0;
1563     MultiByteToWideChar(CP_ACP, 0, utf16bom, -1, buffW, ARRAY_SIZE(buffW));
1564 
1565     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateFalse, &stream);
1566     ok(hr == S_OK, "got 0x%08x\n", hr);
1567     hr = ITextStream_ReadAll(stream, &str);
1568     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
1569     ok(!lstrcmpW(str, buffW), "got %s, expected %s\n", wine_dbgstr_w(str), wine_dbgstr_w(buffW));
1570     SysFreeString(str);
1571     ITextStream_Release(stream);
1572 
1573     DeleteFileW(nameW);
1574     RemoveDirectoryW(dirW);
1575     SysFreeString(nameW);
1576 }
1577 
1578 static void test_WriteLine(void)
1579 {
1580     WCHAR pathW[MAX_PATH], dirW[MAX_PATH];
1581     WCHAR buffW[MAX_PATH], buff2W[MAX_PATH];
1582     char buffA[MAX_PATH];
1583     ITextStream *stream;
1584     DWORD r, len;
1585     HANDLE file;
1586     BSTR nameW;
1587     HRESULT hr;
1588     BOOL ret;
1589 
1590     get_temp_filepath(testfileW, pathW, dirW);
1591 
1592     ret = CreateDirectoryW(dirW, NULL);
1593     ok(ret, "got %d, %d\n", ret, GetLastError());
1594 
1595     /* create as ASCII file first */
1596     nameW = SysAllocString(pathW);
1597     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_FALSE, &stream);
1598     ok(hr == S_OK, "got 0x%08x\n", hr);
1599 
1600     hr = ITextStream_WriteLine(stream, nameW);
1601     ok(hr == S_OK, "got 0x%08x\n", hr);
1602     ITextStream_Release(stream);
1603 
1604     /* check contents */
1605     file = CreateFileW(pathW, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1606     ok(file != INVALID_HANDLE_VALUE, "got %p\n", file);
1607     r = 0;
1608     ret = ReadFile(file, buffA, sizeof(buffA), &r, NULL);
1609     ok(ret && r, "read %d, got %d, %d\n", r, ret, GetLastError());
1610 
1611     len = MultiByteToWideChar(CP_ACP, 0, buffA, r, buffW, ARRAY_SIZE(buffW));
1612     buffW[len] = 0;
1613     lstrcpyW(buff2W, nameW);
1614     lstrcatW(buff2W, crlfW);
1615     ok(!lstrcmpW(buff2W, buffW), "got %s, expected %s\n", wine_dbgstr_w(buffW), wine_dbgstr_w(buff2W));
1616     CloseHandle(file);
1617     DeleteFileW(nameW);
1618 
1619     /* same for unicode file */
1620     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_TRUE, &stream);
1621     ok(hr == S_OK, "got 0x%08x\n", hr);
1622 
1623     hr = ITextStream_WriteLine(stream, nameW);
1624     ok(hr == S_OK, "got 0x%08x\n", hr);
1625     ITextStream_Release(stream);
1626 
1627     /* check contents */
1628     file = CreateFileW(pathW, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1629     ok(file != INVALID_HANDLE_VALUE, "got %p\n", file);
1630     r = 0;
1631     ret = ReadFile(file, buffW, sizeof(buffW), &r, NULL);
1632     ok(ret && r, "read %d, got %d, %d\n", r, ret, GetLastError());
1633     buffW[r/sizeof(WCHAR)] = 0;
1634 
1635     buff2W[0] = 0xfeff;
1636     buff2W[1] = 0;
1637     lstrcatW(buff2W, nameW);
1638     lstrcatW(buff2W, crlfW);
1639     ok(!lstrcmpW(buff2W, buffW), "got %s, expected %s\n", wine_dbgstr_w(buffW), wine_dbgstr_w(buff2W));
1640     CloseHandle(file);
1641     DeleteFileW(nameW);
1642 
1643     RemoveDirectoryW(dirW);
1644     SysFreeString(nameW);
1645 }
1646 
1647 static void test_ReadAll(void)
1648 {
1649     static const WCHAR secondlineW[] = {'s','e','c','o','n','d',0};
1650     static const WCHAR aW[] = {'A',0};
1651     WCHAR pathW[MAX_PATH], dirW[MAX_PATH], buffW[500];
1652     ITextStream *stream;
1653     BSTR nameW;
1654     HRESULT hr;
1655     BOOL ret;
1656     BSTR str;
1657 
1658     get_temp_filepath(testfileW, pathW, dirW);
1659 
1660     ret = CreateDirectoryW(dirW, NULL);
1661     ok(ret, "got %d, %d\n", ret, GetLastError());
1662 
1663     /* Unicode file -> read with ascii stream */
1664     nameW = SysAllocString(pathW);
1665     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_TRUE, &stream);
1666     ok(hr == S_OK, "got 0x%08x\n", hr);
1667 
1668     hr = ITextStream_WriteLine(stream, nameW);
1669     ok(hr == S_OK, "got 0x%08x\n", hr);
1670 
1671     str = SysAllocString(secondlineW);
1672     hr = ITextStream_WriteLine(stream, str);
1673     ok(hr == S_OK, "got 0x%08x\n", hr);
1674     SysFreeString(str);
1675 
1676     hr = ITextStream_ReadAll(stream, NULL);
1677     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1678 
1679     str = (void*)0xdeadbeef;
1680     hr = ITextStream_ReadAll(stream, &str);
1681     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
1682     ok(str == NULL || broken(str == (void*)0xdeadbeef) /* win2k */, "got %p\n", str);
1683 
1684     ITextStream_Release(stream);
1685 
1686     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateFalse, &stream);
1687     ok(hr == S_OK, "got 0x%08x\n", hr);
1688 
1689     hr = ITextStream_ReadAll(stream, NULL);
1690     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1691 
1692     /* Buffer content is not interpreted - BOM is kept, all data is converted to WCHARs */
1693     str = NULL;
1694     hr = ITextStream_ReadAll(stream, &str);
1695     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
1696     buffW[0] = 0;
1697     MultiByteToWideChar(CP_ACP, 0, utf16bom, -1, buffW, ARRAY_SIZE(buffW));
1698     ok(str[0] == buffW[0] && str[1] == buffW[1], "got %s, %d\n", wine_dbgstr_w(str), SysStringLen(str));
1699     SysFreeString(str);
1700     ITextStream_Release(stream);
1701 
1702     /* Unicode file -> read with unicode stream */
1703     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
1704     ok(hr == S_OK, "got 0x%08x\n", hr);
1705 
1706     lstrcpyW(buffW, nameW);
1707     lstrcatW(buffW, crlfW);
1708     lstrcatW(buffW, secondlineW);
1709     lstrcatW(buffW, crlfW);
1710     str = NULL;
1711     hr = ITextStream_ReadAll(stream, &str);
1712     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
1713     ok(!lstrcmpW(buffW, str), "got %s\n", wine_dbgstr_w(str));
1714     SysFreeString(str);
1715 
1716     /* ReadAll one more time */
1717     str = (void*)0xdeadbeef;
1718     hr = ITextStream_ReadAll(stream, &str);
1719     ok(hr == CTL_E_ENDOFFILE, "got 0x%08x\n", hr);
1720     ok(str == NULL || broken(str == (void*)0xdeadbeef) /* win2k */, "got %p\n", str);
1721 
1722     /* ReadLine fails the same way */
1723     str = (void*)0xdeadbeef;
1724     hr = ITextStream_ReadLine(stream, &str);
1725     ok(hr == CTL_E_ENDOFFILE, "got 0x%08x\n", hr);
1726     ok(str == NULL || broken(str == (void*)0xdeadbeef) /* win2k */, "got %p\n", str);
1727     ITextStream_Release(stream);
1728 
1729     /* Open again and skip first line before ReadAll */
1730     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
1731     ok(hr == S_OK, "got 0x%08x\n", hr);
1732 
1733     str = NULL;
1734     hr = ITextStream_ReadLine(stream, &str);
1735 todo_wine {
1736     ok(hr == S_OK, "got 0x%08x\n", hr);
1737     ok(str != NULL, "got %p\n", str);
1738 }
1739     SysFreeString(str);
1740 
1741     lstrcpyW(buffW, secondlineW);
1742     lstrcatW(buffW, crlfW);
1743     str = NULL;
1744     hr = ITextStream_ReadAll(stream, &str);
1745     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
1746 todo_wine
1747     ok(!lstrcmpW(buffW, str), "got %s\n", wine_dbgstr_w(str));
1748     SysFreeString(str);
1749     ITextStream_Release(stream);
1750 
1751     /* ASCII file, read with Unicode stream */
1752     /* 1. one byte content, not enough for Unicode read */
1753     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_TRUE, VARIANT_FALSE, &stream);
1754     ok(hr == S_OK, "got 0x%08x\n", hr);
1755     str = SysAllocString(aW);
1756     hr = ITextStream_Write(stream, str);
1757     ok(hr == S_OK, "got 0x%08x\n", hr);
1758     SysFreeString(str);
1759     ITextStream_Release(stream);
1760 
1761     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
1762     ok(hr == S_OK, "got 0x%08x\n", hr);
1763 
1764     str = (void*)0xdeadbeef;
1765     hr = ITextStream_ReadAll(stream, &str);
1766     ok(hr == CTL_E_ENDOFFILE, "got 0x%08x\n", hr);
1767     ok(str == NULL || broken(str == (void*)0xdeadbeef) /* win2k */, "got %p\n", str);
1768 
1769     ITextStream_Release(stream);
1770 
1771     DeleteFileW(nameW);
1772     RemoveDirectoryW(dirW);
1773     SysFreeString(nameW);
1774 }
1775 
1776 static void test_Read(void)
1777 {
1778     static const WCHAR secondlineW[] = {'s','e','c','o','n','d',0};
1779     static const WCHAR aW[] = {'A',0};
1780     WCHAR pathW[MAX_PATH], dirW[MAX_PATH], buffW[500];
1781     ITextStream *stream;
1782     BSTR nameW;
1783     HRESULT hr;
1784     BOOL ret;
1785     BSTR str;
1786 
1787     get_temp_filepath(testfileW, pathW, dirW);
1788 
1789     ret = CreateDirectoryW(dirW, NULL);
1790     ok(ret, "got %d, %d\n", ret, GetLastError());
1791 
1792     /* Unicode file -> read with ascii stream */
1793     nameW = SysAllocString(pathW);
1794     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_TRUE, &stream);
1795     ok(hr == S_OK, "got 0x%08x\n", hr);
1796 
1797     hr = ITextStream_WriteLine(stream, nameW);
1798     ok(hr == S_OK, "got 0x%08x\n", hr);
1799 
1800     str = SysAllocString(secondlineW);
1801     hr = ITextStream_WriteLine(stream, str);
1802     ok(hr == S_OK, "got 0x%08x\n", hr);
1803     SysFreeString(str);
1804 
1805     hr = ITextStream_Read(stream, 0, NULL);
1806     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1807 
1808     hr = ITextStream_Read(stream, 1, NULL);
1809     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1810 
1811     hr = ITextStream_Read(stream, -1, NULL);
1812     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1813 
1814     str = (void*)0xdeadbeef;
1815     hr = ITextStream_Read(stream, 1, &str);
1816     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
1817     ok(str == NULL, "got %p\n", str);
1818 
1819     ITextStream_Release(stream);
1820 
1821     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateFalse, &stream);
1822     ok(hr == S_OK, "got 0x%08x\n", hr);
1823 
1824     hr = ITextStream_Read(stream, 1, NULL);
1825     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1826 
1827     str = (void*)0xdeadbeef;
1828     hr = ITextStream_Read(stream, -1, &str);
1829     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1830     ok(str == NULL, "got %p\n", str);
1831 
1832     str = (void*)0xdeadbeef;
1833     hr = ITextStream_Read(stream, 0, &str);
1834     ok(hr == S_OK, "got 0x%08x\n", hr);
1835     ok(str == NULL, "got %p\n", str);
1836 
1837     /* Buffer content is not interpreted - BOM is kept, all data is converted to WCHARs */
1838     str = NULL;
1839     hr = ITextStream_Read(stream, 2, &str);
1840     ok(hr == S_OK, "got 0x%08x\n", hr);
1841 
1842     buffW[0] = 0;
1843     MultiByteToWideChar(CP_ACP, 0, utf16bom, -1, buffW, ARRAY_SIZE(buffW));
1844 
1845     ok(!lstrcmpW(str, buffW), "got %s, expected %s\n", wine_dbgstr_w(str), wine_dbgstr_w(buffW));
1846     ok(SysStringLen(str) == 2, "got %d\n", SysStringLen(str));
1847     SysFreeString(str);
1848     ITextStream_Release(stream);
1849 
1850     /* Unicode file -> read with unicode stream */
1851     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
1852     ok(hr == S_OK, "got 0x%08x\n", hr);
1853 
1854     lstrcpyW(buffW, nameW);
1855     lstrcatW(buffW, crlfW);
1856     lstrcatW(buffW, secondlineW);
1857     lstrcatW(buffW, crlfW);
1858     str = NULL;
1859     hr = ITextStream_Read(stream, 500, &str);
1860     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
1861     ok(!lstrcmpW(buffW, str), "got %s\n", wine_dbgstr_w(str));
1862     SysFreeString(str);
1863 
1864     /* ReadAll one more time */
1865     str = (void*)0xdeadbeef;
1866     hr = ITextStream_Read(stream, 10, &str);
1867     ok(hr == CTL_E_ENDOFFILE, "got 0x%08x\n", hr);
1868     ok(str == NULL, "got %p\n", str);
1869 
1870     /* ReadLine fails the same way */
1871     str = (void*)0xdeadbeef;
1872     hr = ITextStream_ReadLine(stream, &str);
1873     ok(hr == CTL_E_ENDOFFILE, "got 0x%08x\n", hr);
1874     ok(str == NULL || broken(str == (void*)0xdeadbeef), "got %p\n", str);
1875     ITextStream_Release(stream);
1876 
1877     /* Open again and skip first line before ReadAll */
1878     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
1879     ok(hr == S_OK, "got 0x%08x\n", hr);
1880 
1881     str = NULL;
1882     hr = ITextStream_ReadLine(stream, &str);
1883 todo_wine {
1884     ok(hr == S_OK, "got 0x%08x\n", hr);
1885     ok(str != NULL, "got %p\n", str);
1886 }
1887     SysFreeString(str);
1888 
1889     lstrcpyW(buffW, secondlineW);
1890     lstrcatW(buffW, crlfW);
1891     str = NULL;
1892     hr = ITextStream_Read(stream, 100, &str);
1893     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
1894 todo_wine
1895     ok(!lstrcmpW(buffW, str), "got %s\n", wine_dbgstr_w(str));
1896     SysFreeString(str);
1897     ITextStream_Release(stream);
1898 
1899     /* default read will use Unicode */
1900     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateUseDefault, &stream);
1901     ok(hr == S_OK, "got 0x%08x\n", hr);
1902 
1903     lstrcpyW(buffW, nameW);
1904     lstrcatW(buffW, crlfW);
1905     lstrcatW(buffW, secondlineW);
1906     lstrcatW(buffW, crlfW);
1907     str = NULL;
1908     hr = ITextStream_Read(stream, 500, &str);
1909     ok(hr == S_FALSE || broken(hr == S_OK) /* win2003 */, "got 0x%08x\n", hr);
1910     ok(!lstrcmpW(buffW, str), "got %s\n", wine_dbgstr_w(str));
1911     SysFreeString(str);
1912 
1913     ITextStream_Release(stream);
1914 
1915     /* default append will use Unicode */
1916     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForAppending, VARIANT_FALSE, TristateUseDefault, &stream);
1917     ok(hr == S_OK, "got 0x%08x\n", hr);
1918 
1919     str = SysAllocString(L"123");
1920     hr = ITextStream_Write(stream, str);
1921     ok(hr == S_OK, "got %08x\n", hr);
1922     SysFreeString(str);
1923 
1924     ITextStream_Release(stream);
1925 
1926     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
1927     ok(hr == S_OK, "got 0x%08x\n", hr);
1928 
1929     lstrcatW(buffW, L"123");
1930     str = NULL;
1931     hr = ITextStream_Read(stream, 500, &str);
1932     ok(hr == S_FALSE || broken(hr == S_OK) /* win2003 */, "got 0x%08x\n", hr);
1933     ok(!lstrcmpW(buffW, str), "got %s\n", wine_dbgstr_w(str));
1934     SysFreeString(str);
1935 
1936     ITextStream_Release(stream);
1937 
1938     /* default write will use ASCII */
1939     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForWriting, VARIANT_FALSE, TristateUseDefault, &stream);
1940     ok(hr == S_OK, "got 0x%08x\n", hr);
1941 
1942     str = SysAllocString(L"123");
1943     hr = ITextStream_Write(stream, str);
1944     ok(hr == S_OK, "got %08x\n", hr);
1945     SysFreeString(str);
1946 
1947     ITextStream_Release(stream);
1948 
1949     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateFalse, &stream);
1950     ok(hr == S_OK, "got 0x%08x\n", hr);
1951 
1952     str = (void*)0xdeadbeef;
1953     hr = ITextStream_Read(stream, 500, &str);
1954     ok(hr == S_FALSE || broken(hr == S_OK) /* win2003 */, "got 0x%08x\n", hr);
1955     ok(!wcscmp(str, L"123"), "got %s\n", wine_dbgstr_w(str));
1956 
1957     ITextStream_Release(stream);
1958     /* ASCII file, read with default stream */
1959     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_TRUE, VARIANT_FALSE, &stream);
1960     ok(hr == S_OK, "got 0x%08x\n", hr);
1961     str = SysAllocString(L"test");
1962     hr = ITextStream_Write(stream, str);
1963     ok(hr == S_OK, "got 0x%08x\n", hr);
1964     SysFreeString(str);
1965     ITextStream_Release(stream);
1966 
1967     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateUseDefault, &stream);
1968     ok(hr == S_OK, "got 0x%08x\n", hr);
1969 
1970     str = (void*)0xdeadbeef;
1971     hr = ITextStream_Read(stream, 500, &str);
1972     ok(hr == S_FALSE || broken(hr == S_OK) /* win2003 */, "got 0x%08x\n", hr);
1973     ok(!wcscmp(str, L"test"), "got %s\n", wine_dbgstr_w(str));
1974 
1975     ITextStream_Release(stream);
1976 
1977     /* default append will use Unicode */
1978     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForAppending, VARIANT_FALSE, TristateUseDefault, &stream);
1979     ok(hr == S_OK, "got 0x%08x\n", hr);
1980 
1981     str = SysAllocString(L"123");
1982     hr = ITextStream_Write(stream, str);
1983     ok(hr == S_OK, "got %08x\n", hr);
1984     SysFreeString(str);
1985 
1986     ITextStream_Release(stream);
1987 
1988     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateFalse, &stream);
1989     ok(hr == S_OK, "got 0x%08x\n", hr);
1990 
1991     str = NULL;
1992     hr = ITextStream_Read(stream, 500, &str);
1993     ok(hr == S_FALSE || broken(hr == S_OK) /* win2003 */, "got 0x%08x\n", hr);
1994     ok(!lstrcmpW(L"test123", str), "got %s\n", wine_dbgstr_w(str));
1995     SysFreeString(str);
1996 
1997     ITextStream_Release(stream);
1998 
1999     /* default write will use ASCII as well */
2000     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForWriting, VARIANT_FALSE, TristateUseDefault, &stream);
2001     ok(hr == S_OK, "got 0x%08x\n", hr);
2002 
2003     str = SysAllocString(L"test string");
2004     hr = ITextStream_Write(stream, str);
2005     ok(hr == S_OK, "got %08x\n", hr);
2006     SysFreeString(str);
2007 
2008     ITextStream_Release(stream);
2009 
2010     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateFalse, &stream);
2011     ok(hr == S_OK, "got 0x%08x\n", hr);
2012 
2013     str = (void*)0xdeadbeef;
2014     hr = ITextStream_Read(stream, 500, &str);
2015     ok(hr == S_FALSE || broken(hr == S_OK) /* win2003 */, "got 0x%08x\n", hr);
2016     ok(!wcscmp(str, L"test string"), "got %s\n", wine_dbgstr_w(str));
2017 
2018     ITextStream_Release(stream);
2019 
2020     /* ASCII file, read with Unicode stream */
2021     /* 1. one byte content, not enough for Unicode read */
2022     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_TRUE, VARIANT_FALSE, &stream);
2023     ok(hr == S_OK, "got 0x%08x\n", hr);
2024     str = SysAllocString(aW);
2025     hr = ITextStream_Write(stream, str);
2026     ok(hr == S_OK, "got 0x%08x\n", hr);
2027     SysFreeString(str);
2028     ITextStream_Release(stream);
2029 
2030     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
2031     ok(hr == S_OK, "got 0x%08x\n", hr);
2032 
2033     str = (void*)0xdeadbeef;
2034     hr = ITextStream_Read(stream, 500, &str);
2035     ok(hr == CTL_E_ENDOFFILE, "got 0x%08x\n", hr);
2036     ok(str == NULL, "got %p\n", str);
2037 
2038     ITextStream_Release(stream);
2039 
2040     DeleteFileW(nameW);
2041     RemoveDirectoryW(dirW);
2042     SysFreeString(nameW);
2043 }
2044 
2045 struct driveexists_test {
2046     const WCHAR drivespec[10];
2047     const INT drivetype;
2048     const VARIANT_BOOL expected_ret;
2049 };
2050 
2051 /* If 'drivetype' != -1, the first character of 'drivespec' will be replaced
2052  * with the drive letter of a drive of this type. If such a drive does not exist,
2053  * the test will be skipped. */
2054 static const struct driveexists_test driveexiststestdata[] = {
2055     { {'N',':','\\',0}, DRIVE_NO_ROOT_DIR, VARIANT_FALSE },
2056     { {'R',':','\\',0}, DRIVE_REMOVABLE, VARIANT_TRUE },
2057     { {'F',':','\\',0}, DRIVE_FIXED, VARIANT_TRUE },
2058     { {'F',':',0}, DRIVE_FIXED, VARIANT_TRUE },
2059     { {'F','?',0}, DRIVE_FIXED, VARIANT_FALSE },
2060     { {'F',0}, DRIVE_FIXED, VARIANT_TRUE },
2061     { {'?',0}, -1, VARIANT_FALSE },
2062     { { 0 } }
2063 };
2064 
2065 static void test_DriveExists(void)
2066 {
2067     const struct driveexists_test *ptr = driveexiststestdata;
2068     HRESULT hr;
2069     VARIANT_BOOL ret;
2070     BSTR drivespec;
2071     WCHAR root[] = {'?',':','\\',0};
2072 
2073     hr = IFileSystem3_DriveExists(fs3, NULL, NULL);
2074     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2075 
2076     ret = VARIANT_TRUE;
2077     hr = IFileSystem3_DriveExists(fs3, NULL, &ret);
2078     ok(hr == S_OK, "got 0x%08x\n", hr);
2079     ok(ret == VARIANT_FALSE, "got %x\n", ret);
2080 
2081     drivespec = SysAllocString(root);
2082     hr = IFileSystem3_DriveExists(fs3, drivespec, NULL);
2083     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2084     SysFreeString(drivespec);
2085 
2086     for (; *ptr->drivespec; ptr++) {
2087         drivespec = SysAllocString(ptr->drivespec);
2088         if (ptr->drivetype != -1) {
2089             for (root[0] = 'A'; root[0] <= 'Z'; root[0]++)
2090                 if (GetDriveTypeW(root) == ptr->drivetype)
2091                     break;
2092             if (root[0] > 'Z') {
2093                 skip("No drive with type 0x%x found, skipping test %s.\n",
2094                         ptr->drivetype, wine_dbgstr_w(ptr->drivespec));
2095                 SysFreeString(drivespec);
2096                 continue;
2097             }
2098 
2099             /* Test both upper and lower case drive letters. */
2100             drivespec[0] = root[0];
2101             ret = ptr->expected_ret == VARIANT_TRUE ? VARIANT_FALSE : VARIANT_TRUE;
2102             hr = IFileSystem3_DriveExists(fs3, drivespec, &ret);
2103             ok(hr == S_OK, "got 0x%08x for drive spec %s (%s)\n",
2104                     hr, wine_dbgstr_w(drivespec), wine_dbgstr_w(ptr->drivespec));
2105             ok(ret == ptr->expected_ret, "got %d, expected %d for drive spec %s (%s)\n",
2106                     ret, ptr->expected_ret, wine_dbgstr_w(drivespec), wine_dbgstr_w(ptr->drivespec));
2107 
2108             drivespec[0] = tolower(root[0]);
2109         }
2110 
2111         ret = ptr->expected_ret == VARIANT_TRUE ? VARIANT_FALSE : VARIANT_TRUE;
2112         hr = IFileSystem3_DriveExists(fs3, drivespec, &ret);
2113         ok(hr == S_OK, "got 0x%08x for drive spec %s (%s)\n",
2114                 hr, wine_dbgstr_w(drivespec), wine_dbgstr_w(ptr->drivespec));
2115         ok(ret == ptr->expected_ret, "got %d, expected %d for drive spec %s (%s)\n",
2116                 ret, ptr->expected_ret, wine_dbgstr_w(drivespec), wine_dbgstr_w(ptr->drivespec));
2117 
2118         SysFreeString(drivespec);
2119     }
2120 }
2121 
2122 struct getdrivename_test {
2123     const WCHAR path[10];
2124     const WCHAR drive[5];
2125 };
2126 
2127 static const struct getdrivename_test getdrivenametestdata[] = {
2128     { {'C',':','\\','1','.','t','s','t',0}, {'C',':',0} },
2129     { {'O',':','\\','1','.','t','s','t',0}, {'O',':',0} },
2130     { {'O',':',0}, {'O',':',0} },
2131     { {'o',':',0}, {'o',':',0} },
2132     { {'O','O',':',0} },
2133     { {':',0} },
2134     { {'O',0} },
2135     { { 0 } }
2136 };
2137 
2138 static void test_GetDriveName(void)
2139 {
2140     const struct getdrivename_test *ptr = getdrivenametestdata;
2141     HRESULT hr;
2142     BSTR name;
2143 
2144     hr = IFileSystem3_GetDriveName(fs3, NULL, NULL);
2145     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2146 
2147     name = (void*)0xdeadbeef;
2148     hr = IFileSystem3_GetDriveName(fs3, NULL, &name);
2149     ok(hr == S_OK, "got 0x%08x\n", hr);
2150     ok(name == NULL, "got %p\n", name);
2151 
2152     while (*ptr->path) {
2153         BSTR path = SysAllocString(ptr->path);
2154         name = (void*)0xdeadbeef;
2155         hr = IFileSystem3_GetDriveName(fs3, path, &name);
2156         ok(hr == S_OK, "got 0x%08x\n", hr);
2157         if (name)
2158             ok(!lstrcmpW(ptr->drive, name), "got %s, expected %s\n", wine_dbgstr_w(name), wine_dbgstr_w(ptr->drive));
2159         else
2160             ok(!*ptr->drive, "got %s, expected %s\n", wine_dbgstr_w(name), wine_dbgstr_w(ptr->drive));
2161         SysFreeString(path);
2162         SysFreeString(name);
2163         ptr++;
2164     }
2165 }
2166 
2167 struct getdrive_test {
2168     const WCHAR drivespec[12];
2169     HRESULT res;
2170     const WCHAR driveletter[2];
2171 };
2172 
2173 static void test_GetDrive(void)
2174 {
2175     HRESULT hr;
2176     IDrive *drive_fixed, *drive;
2177     BSTR dl_fixed, drivespec;
2178     WCHAR root[] = {'?',':','\\',0};
2179 
2180     drive = (void*)0xdeadbeef;
2181     hr = IFileSystem3_GetDrive(fs3, NULL, NULL);
2182     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2183     ok(drive == (void*)0xdeadbeef, "got %p\n", drive);
2184 
2185     for (root[0] = 'A'; root[0] <= 'Z'; root[0]++)
2186         if (GetDriveTypeW(root) == DRIVE_NO_ROOT_DIR)
2187             break;
2188 
2189     if (root[0] > 'Z')
2190         skip("All drive letters are occupied, skipping test for nonexisting drive.\n");
2191     else {
2192         drivespec = SysAllocString(root);
2193         drive = (void*)0xdeadbeef;
2194         hr = IFileSystem3_GetDrive(fs3, drivespec, &drive);
2195         ok(hr == CTL_E_DEVICEUNAVAILABLE, "got 0x%08x\n", hr);
2196         ok(drive == NULL, "got %p\n", drive);
2197         SysFreeString(drivespec);
2198     }
2199 
2200     drive_fixed = get_fixed_drive();
2201     if (!drive_fixed) {
2202         skip("No fixed drive found, skipping test.\n");
2203         return;
2204     }
2205 
2206     hr = IDrive_get_DriveLetter(drive_fixed, &dl_fixed);
2207     ok(hr == S_OK, "got 0x%08x\n", hr);
2208 
2209     if (FAILED(hr))
2210         skip("Could not retrieve drive letter of fixed drive, skipping test.\n");
2211     else {
2212         WCHAR dl_upper = toupper(dl_fixed[0]);
2213         WCHAR dl_lower = tolower(dl_fixed[0]);
2214         struct getdrive_test testdata[] = {
2215             { {dl_upper,0}, S_OK, {dl_upper,0} },
2216             { {dl_upper,':',0}, S_OK, {dl_upper,0} },
2217             { {dl_upper,':','\\',0}, S_OK, {dl_upper,0} },
2218             { {dl_lower,':','\\',0}, S_OK, {dl_upper,0} },
2219             { {dl_upper,'\\',0}, E_INVALIDARG, { 0 } },
2220             { {dl_lower,'\\',0}, E_INVALIDARG, { 0 } },
2221             { {'$',':','\\',0}, E_INVALIDARG, { 0 } },
2222             { {'\\','h','o','s','t','\\','s','h','a','r','e',0}, E_INVALIDARG, { 0 } },
2223             { {'h','o','s','t','\\','s','h','a','r','e',0}, E_INVALIDARG, { 0 } },
2224             { { 0 } },
2225         };
2226         struct getdrive_test *ptr = &testdata[0];
2227 
2228         for (; *ptr->drivespec; ptr++) {
2229             drivespec = SysAllocString(ptr->drivespec);
2230             drive = (void*)0xdeadbeef;
2231             hr = IFileSystem3_GetDrive(fs3, drivespec, &drive);
2232             ok(hr == ptr->res, "got 0x%08x, expected 0x%08x for drive spec %s\n",
2233                     hr, ptr->res, wine_dbgstr_w(ptr->drivespec));
2234             ok(!lstrcmpW(ptr->drivespec, drivespec), "GetDrive modified its DriveSpec argument\n");
2235             SysFreeString(drivespec);
2236 
2237             if (*ptr->driveletter) {
2238                 BSTR driveletter;
2239                 hr = IDrive_get_DriveLetter(drive, &driveletter);
2240                 ok(hr == S_OK, "got 0x%08x for drive spec %s\n", hr, wine_dbgstr_w(ptr->drivespec));
2241                 if (SUCCEEDED(hr)) {
2242                     ok(!lstrcmpW(ptr->driveletter, driveletter), "got %s, expected %s for drive spec %s\n",
2243                             wine_dbgstr_w(driveletter), wine_dbgstr_w(ptr->driveletter),
2244                             wine_dbgstr_w(ptr->drivespec));
2245                     SysFreeString(driveletter);
2246                 }
2247                 test_provideclassinfo(drive, &CLSID_Drive);
2248                 IDrive_Release(drive);
2249             } else
2250                 ok(drive == NULL, "got %p for drive spec %s\n", drive, wine_dbgstr_w(ptr->drivespec));
2251         }
2252         SysFreeString(dl_fixed);
2253     }
2254 }
2255 
2256 static void test_SerialNumber(void)
2257 {
2258     IDrive *drive;
2259     LONG serial;
2260     HRESULT hr;
2261     BSTR name;
2262 
2263     drive = get_fixed_drive();
2264     if (!drive) {
2265         skip("No fixed drive found, skipping test.\n");
2266         return;
2267     }
2268 
2269     hr = IDrive_get_SerialNumber(drive, NULL);
2270     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2271 
2272     serial = 0xdeadbeef;
2273     hr = IDrive_get_SerialNumber(drive, &serial);
2274     ok(hr == S_OK, "got 0x%08x\n", hr);
2275     ok(serial != 0xdeadbeef, "got %x\n", serial);
2276 
2277     hr = IDrive_get_FileSystem(drive, NULL);
2278     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2279 
2280     name = NULL;
2281     hr = IDrive_get_FileSystem(drive, &name);
2282     ok(hr == S_OK, "got 0x%08x\n", hr);
2283     ok(name != NULL, "got %p\n", name);
2284     SysFreeString(name);
2285 
2286     hr = IDrive_get_VolumeName(drive, NULL);
2287     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2288 
2289     name = NULL;
2290     hr = IDrive_get_VolumeName(drive, &name);
2291     ok(hr == S_OK, "got 0x%08x\n", hr);
2292     ok(name != NULL, "got %p\n", name);
2293     SysFreeString(name);
2294 
2295     IDrive_Release(drive);
2296 }
2297 
2298 static const struct extension_test {
2299     WCHAR path[20];
2300     WCHAR ext[10];
2301 } extension_tests[] = {
2302     { {'n','o','e','x','t',0}, {0} },
2303     { {'n','.','o','.','e','x','t',0}, {'e','x','t',0} },
2304     { {'n','.','o','.','e','X','t',0}, {'e','X','t',0} },
2305     { { 0 } }
2306 };
2307 
2308 static void test_GetExtensionName(void)
2309 {
2310     BSTR path, ext;
2311     HRESULT hr;
2312     int i;
2313 
2314     for (i = 0; i < ARRAY_SIZE(extension_tests); i++) {
2315 
2316        path = SysAllocString(extension_tests[i].path);
2317        ext = NULL;
2318        hr = IFileSystem3_GetExtensionName(fs3, path, &ext);
2319        ok(hr == S_OK, "got 0x%08x\n", hr);
2320        if (*extension_tests[i].ext)
2321            ok(!lstrcmpW(ext, extension_tests[i].ext), "%d: path %s, got %s, expected %s\n", i,
2322                wine_dbgstr_w(path), wine_dbgstr_w(ext), wine_dbgstr_w(extension_tests[i].ext));
2323        else
2324            ok(ext == NULL, "%d: path %s, got %s, expected %s\n", i,
2325                wine_dbgstr_w(path), wine_dbgstr_w(ext), wine_dbgstr_w(extension_tests[i].ext));
2326 
2327        SysFreeString(path);
2328        SysFreeString(ext);
2329     }
2330 }
2331 
2332 static void test_GetSpecialFolder(void)
2333 {
2334     WCHAR pathW[MAX_PATH];
2335     IFolder *folder;
2336     HRESULT hr;
2337     DWORD ret;
2338     BSTR path;
2339 
2340     hr = IFileSystem3_GetSpecialFolder(fs3, WindowsFolder, NULL);
2341     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2342 
2343     hr = IFileSystem3_GetSpecialFolder(fs3, TemporaryFolder+1, NULL);
2344     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2345 
2346     hr = IFileSystem3_GetSpecialFolder(fs3, TemporaryFolder+1, &folder);
2347     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2348 
2349     hr = IFileSystem3_GetSpecialFolder(fs3, WindowsFolder, &folder);
2350     ok(hr == S_OK, "got 0x%08x\n", hr);
2351     hr = IFolder_get_Path(folder, &path);
2352     ok(hr == S_OK, "got 0x%08x\n", hr);
2353     GetWindowsDirectoryW(pathW, ARRAY_SIZE(pathW));
2354     ok(!lstrcmpiW(pathW, path), "got %s, expected %s\n", wine_dbgstr_w(path), wine_dbgstr_w(pathW));
2355     SysFreeString(path);
2356     IFolder_Release(folder);
2357 
2358     hr = IFileSystem3_GetSpecialFolder(fs3, SystemFolder, &folder);
2359     ok(hr == S_OK, "got 0x%08x\n", hr);
2360     hr = IFolder_get_Path(folder, &path);
2361     ok(hr == S_OK, "got 0x%08x\n", hr);
2362     GetSystemDirectoryW(pathW, ARRAY_SIZE(pathW));
2363     ok(!lstrcmpiW(pathW, path), "got %s, expected %s\n", wine_dbgstr_w(path), wine_dbgstr_w(pathW));
2364     SysFreeString(path);
2365     IFolder_Release(folder);
2366 
2367     hr = IFileSystem3_GetSpecialFolder(fs3, TemporaryFolder, &folder);
2368     ok(hr == S_OK, "got 0x%08x\n", hr);
2369     hr = IFolder_get_Path(folder, &path);
2370     ok(hr == S_OK, "got 0x%08x\n", hr);
2371     ret = GetTempPathW(ARRAY_SIZE(pathW), pathW);
2372     if (ret && pathW[ret-1] == '\\')
2373         pathW[ret-1] = 0;
2374 
2375     ok(!lstrcmpiW(pathW, path), "got %s, expected %s\n", wine_dbgstr_w(path), wine_dbgstr_w(pathW));
2376     SysFreeString(path);
2377     IFolder_Release(folder);
2378 }
2379 
2380 START_TEST(filesystem)
2381 {
2382     HRESULT hr;
2383 
2384     CoInitialize(NULL);
2385 
2386     hr = CoCreateInstance(&CLSID_FileSystemObject, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
2387             &IID_IFileSystem3, (void**)&fs3);
2388     if(FAILED(hr)) {
2389         win_skip("Could not create FileSystem object: %08x\n", hr);
2390         return;
2391     }
2392 
2393     test_interfaces();
2394     test_createfolder();
2395     test_textstream();
2396     test_GetFileVersion();
2397     test_GetParentFolderName();
2398     test_GetFileName();
2399     test_GetBaseName();
2400     test_GetAbsolutePathName();
2401     test_GetFile();
2402     test_CopyFolder();
2403     test_BuildPath();
2404     test_GetFolder();
2405     test_FolderCollection();
2406     test_FileCollection();
2407     test_DriveCollection();
2408     test_CreateTextFile();
2409     test_WriteLine();
2410     test_ReadAll();
2411     test_Read();
2412     test_DriveExists();
2413     test_GetDriveName();
2414     test_GetDrive();
2415     test_SerialNumber();
2416     test_GetExtensionName();
2417     test_GetSpecialFolder();
2418 
2419     IFileSystem3_Release(fs3);
2420 
2421     CoUninitialize();
2422 }
2423