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 /* Please keep the tests for IFolderCollection and IFileCollection in sync */
977 static void test_FolderCollection(void)
978 {
979     static const WCHAR fooW[] = {'f','o','o',0};
980     static const WCHAR aW[] = {'\\','a',0};
981     static const WCHAR bW[] = {'\\','b',0};
982     static const WCHAR cW[] = {'\\','c',0};
983     IFolderCollection *folders;
984     WCHAR buffW[MAX_PATH], pathW[MAX_PATH];
985     IEnumVARIANT *enumvar, *clone;
986     LONG count, ref, ref2, i;
987     IUnknown *unk, *unk2;
988     IFolder *folder;
989     ULONG fetched;
990     VARIANT var, var2[2];
991     HRESULT hr;
992     BSTR str;
993     int found_a = 0, found_b = 0, found_c = 0;
994 
995     get_temp_path(fooW, buffW);
996     CreateDirectoryW(buffW, NULL);
997 
998     str = SysAllocString(buffW);
999     hr = IFileSystem3_GetFolder(fs3, str, &folder);
1000     ok(hr == S_OK, "got 0x%08x\n", hr);
1001     SysFreeString(str);
1002 
1003     hr = IFolder_get_SubFolders(folder, NULL);
1004     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1005 
1006     hr = IFolder_get_Path(folder, NULL);
1007     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1008 
1009     hr = IFolder_get_Path(folder, &str);
1010     ok(hr == S_OK, "got 0x%08x\n", hr);
1011     ok(!lstrcmpiW(buffW, str), "got %s, expected %s\n", wine_dbgstr_w(str), wine_dbgstr_w(buffW));
1012     SysFreeString(str);
1013 
1014     lstrcpyW(pathW, buffW);
1015     lstrcatW(pathW, aW);
1016     CreateDirectoryW(pathW, NULL);
1017 
1018     lstrcpyW(pathW, buffW);
1019     lstrcatW(pathW, bW);
1020     CreateDirectoryW(pathW, NULL);
1021 
1022     hr = IFolder_get_SubFolders(folder, &folders);
1023     ok(hr == S_OK, "got 0x%08x\n", hr);
1024     test_provideclassinfo(folders, &CLSID_Folders);
1025     IFolder_Release(folder);
1026 
1027     count = 0;
1028     hr = IFolderCollection_get_Count(folders, &count);
1029     ok(hr == S_OK, "got 0x%08x\n", hr);
1030     ok(count == 2, "got %d\n", count);
1031 
1032     lstrcpyW(pathW, buffW);
1033     lstrcatW(pathW, cW);
1034     CreateDirectoryW(pathW, NULL);
1035 
1036     /* every time property is requested it scans directory */
1037     count = 0;
1038     hr = IFolderCollection_get_Count(folders, &count);
1039     ok(hr == S_OK, "got 0x%08x\n", hr);
1040     ok(count == 3, "got %d\n", count);
1041 
1042     hr = IFolderCollection_get__NewEnum(folders, NULL);
1043     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1044 
1045     hr = IFolderCollection_QueryInterface(folders, &IID_IEnumVARIANT, (void**)&unk);
1046     ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1047 
1048     /* NewEnum creates new instance each time it's called */
1049     ref = GET_REFCOUNT(folders);
1050 
1051     unk = NULL;
1052     hr = IFolderCollection_get__NewEnum(folders, &unk);
1053     ok(hr == S_OK, "got 0x%08x\n", hr);
1054 
1055     ref2 = GET_REFCOUNT(folders);
1056     ok(ref2 == ref + 1, "got %d, %d\n", ref2, ref);
1057 
1058     unk2 = NULL;
1059     hr = IFolderCollection_get__NewEnum(folders, &unk2);
1060     ok(hr == S_OK, "got 0x%08x\n", hr);
1061     ok(unk != unk2, "got %p, %p\n", unk2, unk);
1062     IUnknown_Release(unk2);
1063 
1064     /* now get IEnumVARIANT */
1065     ref = GET_REFCOUNT(folders);
1066     hr = IUnknown_QueryInterface(unk, &IID_IEnumVARIANT, (void**)&enumvar);
1067     ok(hr == S_OK, "got 0x%08x\n", hr);
1068     ref2 = GET_REFCOUNT(folders);
1069     ok(ref2 == ref, "got %d, %d\n", ref2, ref);
1070 
1071     /* clone enumerator */
1072     hr = IEnumVARIANT_Clone(enumvar, &clone);
1073     ok(hr == S_OK, "got 0x%08x\n", hr);
1074     ok(clone != enumvar, "got %p, %p\n", enumvar, clone);
1075     IEnumVARIANT_Release(clone);
1076 
1077     hr = IEnumVARIANT_Reset(enumvar);
1078     ok(hr == S_OK, "got 0x%08x\n", hr);
1079 
1080     for (i = 0; i < 3; i++)
1081     {
1082         VariantInit(&var);
1083         fetched = 0;
1084         hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched);
1085         ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
1086         ok(fetched == 1, "%d: got %d\n", i, fetched);
1087         ok(V_VT(&var) == VT_DISPATCH, "%d: got type %d\n", i, V_VT(&var));
1088 
1089         hr = IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IFolder, (void**)&folder);
1090         ok(hr == S_OK, "got 0x%08x\n", hr);
1091 
1092         str = NULL;
1093         hr = IFolder_get_Name(folder, &str);
1094         ok(hr == S_OK, "got 0x%08x\n", hr);
1095         if (!lstrcmpW(str, aW + 1))
1096             found_a++;
1097         else if (!lstrcmpW(str, bW + 1))
1098             found_b++;
1099         else if (!lstrcmpW(str, cW + 1))
1100             found_c++;
1101         else
1102             ok(0, "unexpected folder %s was found\n", wine_dbgstr_w(str));
1103         SysFreeString(str);
1104 
1105         IFolder_Release(folder);
1106         VariantClear(&var);
1107     }
1108 
1109     ok(found_a == 1 && found_b == 1 && found_c == 1,
1110        "each folder should be found 1 time instead of %d/%d/%d\n",
1111        found_a, found_b, found_c);
1112 
1113     VariantInit(&var);
1114     fetched = -1;
1115     hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched);
1116     ok(hr == S_FALSE, "got 0x%08x\n", hr);
1117     ok(fetched == 0, "got %d\n", fetched);
1118 
1119     hr = IEnumVARIANT_Reset(enumvar);
1120     ok(hr == S_OK, "got 0x%08x\n", hr);
1121     hr = IEnumVARIANT_Skip(enumvar, 2);
1122     ok(hr == S_OK, "got 0x%08x\n", hr);
1123     hr = IEnumVARIANT_Skip(enumvar, 0);
1124     ok(hr == S_OK, "got 0x%08x\n", hr);
1125 
1126     VariantInit(&var2[0]);
1127     VariantInit(&var2[1]);
1128     fetched = -1;
1129     hr = IEnumVARIANT_Next(enumvar, 0, var2, &fetched);
1130     ok(hr == S_OK, "got 0x%08x\n", hr);
1131     ok(fetched == 0, "got %d\n", fetched);
1132     fetched = -1;
1133     hr = IEnumVARIANT_Next(enumvar, 2, var2, &fetched);
1134     ok(hr == S_FALSE, "got 0x%08x\n", hr);
1135     ok(fetched == 1, "got %d\n", fetched);
1136     ok(V_VT(&var2[0]) == VT_DISPATCH, "got type %d\n", V_VT(&var2[0]));
1137     VariantClear(&var2[0]);
1138     VariantClear(&var2[1]);
1139 
1140     IEnumVARIANT_Release(enumvar);
1141     IUnknown_Release(unk);
1142 
1143     lstrcpyW(pathW, buffW);
1144     lstrcatW(pathW, aW);
1145     RemoveDirectoryW(pathW);
1146     lstrcpyW(pathW, buffW);
1147     lstrcatW(pathW, bW);
1148     RemoveDirectoryW(pathW);
1149     lstrcpyW(pathW, buffW);
1150     lstrcatW(pathW, cW);
1151     RemoveDirectoryW(pathW);
1152     RemoveDirectoryW(buffW);
1153 
1154     IFolderCollection_Release(folders);
1155 }
1156 
1157 /* Please keep the tests for IFolderCollection and IFileCollection in sync */
1158 static void test_FileCollection(void)
1159 {
1160     static const WCHAR fooW[] = {'\\','f','o','o',0};
1161     static const WCHAR aW[] = {'\\','a',0};
1162     static const WCHAR bW[] = {'\\','b',0};
1163     static const WCHAR cW[] = {'\\','c',0};
1164     WCHAR buffW[MAX_PATH], pathW[MAX_PATH];
1165     IFolder *folder;
1166     IFileCollection *files;
1167     IFile *file;
1168     IEnumVARIANT *enumvar, *clone;
1169     LONG count, ref, ref2, i;
1170     IUnknown *unk, *unk2;
1171     ULONG fetched;
1172     VARIANT var, var2[2];
1173     HRESULT hr;
1174     BSTR str;
1175     HANDLE file_a, file_b, file_c;
1176     int found_a = 0, found_b = 0, found_c = 0;
1177 
1178     get_temp_path(fooW, buffW);
1179     CreateDirectoryW(buffW, NULL);
1180 
1181     str = SysAllocString(buffW);
1182     hr = IFileSystem3_GetFolder(fs3, str, &folder);
1183     ok(hr == S_OK, "got 0x%08x\n", hr);
1184     SysFreeString(str);
1185 
1186     hr = IFolder_get_Files(folder, NULL);
1187     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1188 
1189     lstrcpyW(pathW, buffW);
1190     lstrcatW(pathW, aW);
1191     file_a = CreateFileW(pathW, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1192                          FILE_FLAG_DELETE_ON_CLOSE, 0);
1193     lstrcpyW(pathW, buffW);
1194     lstrcatW(pathW, bW);
1195     file_b = CreateFileW(pathW, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1196                          FILE_FLAG_DELETE_ON_CLOSE, 0);
1197 
1198     hr = IFolder_get_Files(folder, &files);
1199     ok(hr == S_OK, "got 0x%08x\n", hr);
1200     test_provideclassinfo(files, &CLSID_Files);
1201     IFolder_Release(folder);
1202 
1203     count = 0;
1204     hr = IFileCollection_get_Count(files, &count);
1205     ok(hr == S_OK, "got 0x%08x\n", hr);
1206     ok(count == 2, "got %d\n", count);
1207 
1208     lstrcpyW(pathW, buffW);
1209     lstrcatW(pathW, cW);
1210     file_c = CreateFileW(pathW, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
1211                          FILE_FLAG_DELETE_ON_CLOSE, 0);
1212 
1213     /* every time property is requested it scans directory */
1214     count = 0;
1215     hr = IFileCollection_get_Count(files, &count);
1216     ok(hr == S_OK, "got 0x%08x\n", hr);
1217     ok(count == 3, "got %d\n", count);
1218 
1219     hr = IFileCollection_get__NewEnum(files, NULL);
1220     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1221 
1222     hr = IFileCollection_QueryInterface(files, &IID_IEnumVARIANT, (void**)&unk);
1223     ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1224 
1225     /* NewEnum creates new instance each time it's called */
1226     ref = GET_REFCOUNT(files);
1227 
1228     unk = NULL;
1229     hr = IFileCollection_get__NewEnum(files, &unk);
1230     ok(hr == S_OK, "got 0x%08x\n", hr);
1231 
1232     ref2 = GET_REFCOUNT(files);
1233     ok(ref2 == ref + 1, "got %d, %d\n", ref2, ref);
1234 
1235     unk2 = NULL;
1236     hr = IFileCollection_get__NewEnum(files, &unk2);
1237     ok(hr == S_OK, "got 0x%08x\n", hr);
1238     ok(unk != unk2, "got %p, %p\n", unk2, unk);
1239     IUnknown_Release(unk2);
1240 
1241     /* now get IEnumVARIANT */
1242     ref = GET_REFCOUNT(files);
1243     hr = IUnknown_QueryInterface(unk, &IID_IEnumVARIANT, (void**)&enumvar);
1244     ok(hr == S_OK, "got 0x%08x\n", hr);
1245     ref2 = GET_REFCOUNT(files);
1246     ok(ref2 == ref, "got %d, %d\n", ref2, ref);
1247 
1248     /* clone enumerator */
1249     hr = IEnumVARIANT_Clone(enumvar, &clone);
1250     ok(hr == S_OK, "got 0x%08x\n", hr);
1251     ok(clone != enumvar, "got %p, %p\n", enumvar, clone);
1252     IEnumVARIANT_Release(clone);
1253 
1254     hr = IEnumVARIANT_Reset(enumvar);
1255     ok(hr == S_OK, "got 0x%08x\n", hr);
1256 
1257     for (i = 0; i < 3; i++)
1258     {
1259         VariantInit(&var);
1260         fetched = 0;
1261         hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched);
1262         ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
1263         ok(fetched == 1, "%d: got %d\n", i, fetched);
1264         ok(V_VT(&var) == VT_DISPATCH, "%d: got type %d\n", i, V_VT(&var));
1265 
1266         hr = IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IFile, (void **)&file);
1267         ok(hr == S_OK, "got 0x%08x\n", hr);
1268         test_provideclassinfo(file, &CLSID_File);
1269 
1270         str = NULL;
1271         hr = IFile_get_Name(file, &str);
1272         ok(hr == S_OK, "got 0x%08x\n", hr);
1273         if (!lstrcmpW(str, aW + 1))
1274             found_a++;
1275         else if (!lstrcmpW(str, bW + 1))
1276             found_b++;
1277         else if (!lstrcmpW(str, cW + 1))
1278             found_c++;
1279         else
1280             ok(0, "unexpected file %s was found\n", wine_dbgstr_w(str));
1281         SysFreeString(str);
1282 
1283         IFile_Release(file);
1284         VariantClear(&var);
1285     }
1286 
1287     ok(found_a == 1 && found_b == 1 && found_c == 1,
1288        "each file should be found 1 time instead of %d/%d/%d\n",
1289        found_a, found_b, found_c);
1290 
1291     VariantInit(&var);
1292     fetched = -1;
1293     hr = IEnumVARIANT_Next(enumvar, 1, &var, &fetched);
1294     ok(hr == S_FALSE, "got 0x%08x\n", hr);
1295     ok(fetched == 0, "got %d\n", fetched);
1296 
1297     hr = IEnumVARIANT_Reset(enumvar);
1298     ok(hr == S_OK, "got 0x%08x\n", hr);
1299     hr = IEnumVARIANT_Skip(enumvar, 2);
1300     ok(hr == S_OK, "got 0x%08x\n", hr);
1301     hr = IEnumVARIANT_Skip(enumvar, 0);
1302     ok(hr == S_OK, "got 0x%08x\n", hr);
1303 
1304     VariantInit(&var2[0]);
1305     VariantInit(&var2[1]);
1306     fetched = -1;
1307     hr = IEnumVARIANT_Next(enumvar, 0, var2, &fetched);
1308     ok(hr == S_OK, "got 0x%08x\n", hr);
1309     ok(fetched == 0, "got %d\n", fetched);
1310     fetched = -1;
1311     hr = IEnumVARIANT_Next(enumvar, 2, var2, &fetched);
1312     ok(hr == S_FALSE, "got 0x%08x\n", hr);
1313     ok(fetched == 1, "got %d\n", fetched);
1314     ok(V_VT(&var2[0]) == VT_DISPATCH, "got type %d\n", V_VT(&var2[0]));
1315     VariantClear(&var2[0]);
1316     VariantClear(&var2[1]);
1317 
1318     IEnumVARIANT_Release(enumvar);
1319     IUnknown_Release(unk);
1320 
1321     CloseHandle(file_a);
1322     CloseHandle(file_b);
1323     CloseHandle(file_c);
1324     RemoveDirectoryW(buffW);
1325 
1326     IFileCollection_Release(files);
1327 }
1328 
1329 static void test_DriveCollection(void)
1330 {
1331     IDriveCollection *drives;
1332     IEnumVARIANT *enumvar;
1333     ULONG fetched;
1334     VARIANT var;
1335     HRESULT hr;
1336     LONG count;
1337 
1338     hr = IFileSystem3_get_Drives(fs3, &drives);
1339     ok(hr == S_OK, "got 0x%08x\n", hr);
1340 
1341     test_provideclassinfo(drives, &CLSID_Drives);
1342 
1343     hr = IDriveCollection_get__NewEnum(drives, (IUnknown**)&enumvar);
1344     ok(hr == S_OK, "got 0x%08x\n", hr);
1345 
1346     hr = IDriveCollection_get_Count(drives, NULL);
1347     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1348 
1349     count = 0;
1350     hr = IDriveCollection_get_Count(drives, &count);
1351     ok(hr == S_OK, "got 0x%08x\n", hr);
1352     ok(count > 0, "got %d\n", count);
1353 
1354     V_VT(&var) = VT_EMPTY;
1355     fetched = -1;
1356     hr = IEnumVARIANT_Next(enumvar, 0, &var, &fetched);
1357     ok(hr == S_OK, "got 0x%08x\n", hr);
1358     ok(fetched == 0, "got %d\n", fetched);
1359 
1360     hr = IEnumVARIANT_Skip(enumvar, 0);
1361     ok(hr == S_OK, "got 0x%08x\n", hr);
1362 
1363     hr = IEnumVARIANT_Skip(enumvar, count);
1364     ok(hr == S_OK, "got 0x%08x\n", hr);
1365 
1366     hr = IEnumVARIANT_Skip(enumvar, 1);
1367     ok(hr == S_FALSE, "got 0x%08x\n", hr);
1368 
1369     /* reset and iterate again */
1370     hr = IEnumVARIANT_Reset(enumvar);
1371     ok(hr == S_OK, "got 0x%08x\n", hr);
1372 
1373     while (IEnumVARIANT_Next(enumvar, 1, &var, &fetched) == S_OK) {
1374         IDrive *drive = (IDrive*)V_DISPATCH(&var);
1375         DriveTypeConst type;
1376         BSTR str;
1377 
1378         hr = IDrive_get_DriveType(drive, &type);
1379         ok(hr == S_OK, "got 0x%08x\n", hr);
1380 
1381         hr = IDrive_get_DriveLetter(drive, NULL);
1382         ok(hr == E_POINTER, "got 0x%08x\n", hr);
1383 
1384         hr = IDrive_get_DriveLetter(drive, &str);
1385         ok(hr == S_OK, "got 0x%08x\n", hr);
1386         ok(SysStringLen(str) == 1, "got string %s\n", wine_dbgstr_w(str));
1387         SysFreeString(str);
1388 
1389         hr = IDrive_get_IsReady(drive, NULL);
1390         ok(hr == E_POINTER, "got 0x%08x\n", hr);
1391 
1392         hr = IDrive_get_TotalSize(drive, NULL);
1393         ok(hr == E_POINTER, "got 0x%08x\n", hr);
1394 
1395         hr = IDrive_get_AvailableSpace(drive, NULL);
1396         ok(hr == E_POINTER, "got 0x%08x\n", hr);
1397 
1398         hr = IDrive_get_FreeSpace(drive, NULL);
1399         ok(hr == E_POINTER, "got 0x%08x\n", hr);
1400 
1401         if (type == Fixed) {
1402             VARIANT_BOOL ready = VARIANT_FALSE;
1403             VARIANT size;
1404 
1405             hr = IDrive_get_IsReady(drive, &ready);
1406             ok(hr == S_OK, "got 0x%08x\n", hr);
1407             ok(ready == VARIANT_TRUE, "got %x\n", ready);
1408 
1409             if (ready != VARIANT_TRUE) {
1410                 hr = IDrive_get_DriveLetter(drive, &str);
1411                 ok(hr == S_OK, "got 0x%08x\n", hr);
1412 
1413                 skip("Drive %s is not ready, skipping some tests\n", wine_dbgstr_w(str));
1414 
1415                 VariantClear(&var);
1416                 SysFreeString(str);
1417                 continue;
1418             }
1419 
1420             V_VT(&size) = VT_EMPTY;
1421             hr = IDrive_get_TotalSize(drive, &size);
1422             ok(hr == S_OK, "got 0x%08x\n", hr);
1423             ok(V_VT(&size) == VT_R8 || V_VT(&size) == VT_I4, "got %d\n", V_VT(&size));
1424             if (V_VT(&size) == VT_R8)
1425                 ok(V_R8(&size) > 0, "got %f\n", V_R8(&size));
1426             else
1427                 ok(V_I4(&size) > 0, "got %d\n", V_I4(&size));
1428 
1429             V_VT(&size) = VT_EMPTY;
1430             hr = IDrive_get_AvailableSpace(drive, &size);
1431             ok(hr == S_OK, "got 0x%08x\n", hr);
1432             ok(V_VT(&size) == VT_R8 || V_VT(&size) == VT_I4, "got %d\n", V_VT(&size));
1433             if (V_VT(&size) == VT_R8)
1434                 ok(V_R8(&size) > (double)INT_MAX, "got %f\n", V_R8(&size));
1435             else
1436                 ok(V_I4(&size) > 0, "got %d\n", V_I4(&size));
1437 
1438             V_VT(&size) = VT_EMPTY;
1439             hr = IDrive_get_FreeSpace(drive, &size);
1440             ok(hr == S_OK, "got 0x%08x\n", hr);
1441             ok(V_VT(&size) == VT_R8 || V_VT(&size) == VT_I4, "got %d\n", V_VT(&size));
1442             if (V_VT(&size) == VT_R8)
1443                 ok(V_R8(&size) > 0, "got %f\n", V_R8(&size));
1444             else
1445                 ok(V_I4(&size) > 0, "got %d\n", V_I4(&size));
1446         }
1447         VariantClear(&var);
1448     }
1449 
1450     IEnumVARIANT_Release(enumvar);
1451     IDriveCollection_Release(drives);
1452 }
1453 
1454 static void get_temp_filepath(const WCHAR *filename, WCHAR *path, WCHAR *dir)
1455 {
1456     static const WCHAR scrrunW[] = {'s','c','r','r','u','n','\\',0};
1457 
1458     GetTempPathW(MAX_PATH, path);
1459     lstrcatW(path, scrrunW);
1460     lstrcpyW(dir, path);
1461     lstrcatW(path, filename);
1462 }
1463 
1464 static void test_CreateTextFile(void)
1465 {
1466     WCHAR pathW[MAX_PATH], dirW[MAX_PATH], buffW[10];
1467     ITextStream *stream;
1468     BSTR nameW, str;
1469     HANDLE file;
1470     HRESULT hr;
1471     BOOL ret;
1472 
1473     get_temp_filepath(testfileW, pathW, dirW);
1474 
1475     /* dir doesn't exist */
1476     nameW = SysAllocString(pathW);
1477     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_FALSE, &stream);
1478     ok(hr == CTL_E_PATHNOTFOUND, "got 0x%08x\n", hr);
1479 
1480     ret = CreateDirectoryW(dirW, NULL);
1481     ok(ret, "got %d, %d\n", ret, GetLastError());
1482 
1483     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_FALSE, &stream);
1484     ok(hr == S_OK, "got 0x%08x\n", hr);
1485 
1486     test_provideclassinfo(stream, &CLSID_TextStream);
1487 
1488     hr = ITextStream_Read(stream, 1, &str);
1489     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
1490 
1491     hr = ITextStream_Close(stream);
1492     ok(hr == S_OK, "got 0x%08x\n", hr);
1493 
1494     hr = ITextStream_Read(stream, 1, &str);
1495     ok(hr == CTL_E_BADFILEMODE || hr == E_VAR_NOT_SET, "got 0x%08x\n", hr);
1496 
1497     hr = ITextStream_Close(stream);
1498     ok(hr == S_FALSE || hr == E_VAR_NOT_SET, "got 0x%08x\n", hr);
1499 
1500     ITextStream_Release(stream);
1501 
1502     /* check it's created */
1503     file = CreateFileW(pathW, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1504     ok(file != INVALID_HANDLE_VALUE, "got %p\n", file);
1505     CloseHandle(file);
1506 
1507     /* try to create again with no-overwrite mode */
1508     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_FALSE, &stream);
1509     ok(hr == CTL_E_FILEALREADYEXISTS, "got 0x%08x\n", hr);
1510 
1511     /* now overwrite */
1512     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_TRUE, VARIANT_FALSE, &stream);
1513     ok(hr == S_OK, "got 0x%08x\n", hr);
1514     ITextStream_Release(stream);
1515 
1516     /* overwrite in Unicode mode, check for BOM */
1517     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_TRUE, VARIANT_TRUE, &stream);
1518     ok(hr == S_OK, "got 0x%08x\n", hr);
1519     ITextStream_Release(stream);
1520 
1521     /* File was created in Unicode mode, it contains 0xfffe BOM. Opening it in non-Unicode mode
1522        treats BOM like a valuable data with appropriate CP_ACP -> WCHAR conversion. */
1523     buffW[0] = 0;
1524     MultiByteToWideChar(CP_ACP, 0, utf16bom, -1, buffW, ARRAY_SIZE(buffW));
1525 
1526     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateFalse, &stream);
1527     ok(hr == S_OK, "got 0x%08x\n", hr);
1528     hr = ITextStream_ReadAll(stream, &str);
1529     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
1530     ok(!lstrcmpW(str, buffW), "got %s, expected %s\n", wine_dbgstr_w(str), wine_dbgstr_w(buffW));
1531     SysFreeString(str);
1532     ITextStream_Release(stream);
1533 
1534     DeleteFileW(nameW);
1535     RemoveDirectoryW(dirW);
1536     SysFreeString(nameW);
1537 }
1538 
1539 static void test_WriteLine(void)
1540 {
1541     WCHAR pathW[MAX_PATH], dirW[MAX_PATH];
1542     WCHAR buffW[MAX_PATH], buff2W[MAX_PATH];
1543     char buffA[MAX_PATH];
1544     ITextStream *stream;
1545     DWORD r, len;
1546     HANDLE file;
1547     BSTR nameW;
1548     HRESULT hr;
1549     BOOL ret;
1550 
1551     get_temp_filepath(testfileW, pathW, dirW);
1552 
1553     ret = CreateDirectoryW(dirW, NULL);
1554     ok(ret, "got %d, %d\n", ret, GetLastError());
1555 
1556     /* create as ASCII file first */
1557     nameW = SysAllocString(pathW);
1558     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_FALSE, &stream);
1559     ok(hr == S_OK, "got 0x%08x\n", hr);
1560 
1561     hr = ITextStream_WriteLine(stream, nameW);
1562     ok(hr == S_OK, "got 0x%08x\n", hr);
1563     ITextStream_Release(stream);
1564 
1565     /* check contents */
1566     file = CreateFileW(pathW, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1567     ok(file != INVALID_HANDLE_VALUE, "got %p\n", file);
1568     r = 0;
1569     ret = ReadFile(file, buffA, sizeof(buffA), &r, NULL);
1570     ok(ret && r, "read %d, got %d, %d\n", r, ret, GetLastError());
1571 
1572     len = MultiByteToWideChar(CP_ACP, 0, buffA, r, buffW, ARRAY_SIZE(buffW));
1573     buffW[len] = 0;
1574     lstrcpyW(buff2W, nameW);
1575     lstrcatW(buff2W, crlfW);
1576     ok(!lstrcmpW(buff2W, buffW), "got %s, expected %s\n", wine_dbgstr_w(buffW), wine_dbgstr_w(buff2W));
1577     CloseHandle(file);
1578     DeleteFileW(nameW);
1579 
1580     /* same for unicode file */
1581     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_TRUE, &stream);
1582     ok(hr == S_OK, "got 0x%08x\n", hr);
1583 
1584     hr = ITextStream_WriteLine(stream, nameW);
1585     ok(hr == S_OK, "got 0x%08x\n", hr);
1586     ITextStream_Release(stream);
1587 
1588     /* check contents */
1589     file = CreateFileW(pathW, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1590     ok(file != INVALID_HANDLE_VALUE, "got %p\n", file);
1591     r = 0;
1592     ret = ReadFile(file, buffW, sizeof(buffW), &r, NULL);
1593     ok(ret && r, "read %d, got %d, %d\n", r, ret, GetLastError());
1594     buffW[r/sizeof(WCHAR)] = 0;
1595 
1596     buff2W[0] = 0xfeff;
1597     buff2W[1] = 0;
1598     lstrcatW(buff2W, nameW);
1599     lstrcatW(buff2W, crlfW);
1600     ok(!lstrcmpW(buff2W, buffW), "got %s, expected %s\n", wine_dbgstr_w(buffW), wine_dbgstr_w(buff2W));
1601     CloseHandle(file);
1602     DeleteFileW(nameW);
1603 
1604     RemoveDirectoryW(dirW);
1605     SysFreeString(nameW);
1606 }
1607 
1608 static void test_ReadAll(void)
1609 {
1610     static const WCHAR secondlineW[] = {'s','e','c','o','n','d',0};
1611     static const WCHAR aW[] = {'A',0};
1612     WCHAR pathW[MAX_PATH], dirW[MAX_PATH], buffW[500];
1613     ITextStream *stream;
1614     BSTR nameW;
1615     HRESULT hr;
1616     BOOL ret;
1617     BSTR str;
1618 
1619     get_temp_filepath(testfileW, pathW, dirW);
1620 
1621     ret = CreateDirectoryW(dirW, NULL);
1622     ok(ret, "got %d, %d\n", ret, GetLastError());
1623 
1624     /* Unicode file -> read with ascii stream */
1625     nameW = SysAllocString(pathW);
1626     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_TRUE, &stream);
1627     ok(hr == S_OK, "got 0x%08x\n", hr);
1628 
1629     hr = ITextStream_WriteLine(stream, nameW);
1630     ok(hr == S_OK, "got 0x%08x\n", hr);
1631 
1632     str = SysAllocString(secondlineW);
1633     hr = ITextStream_WriteLine(stream, str);
1634     ok(hr == S_OK, "got 0x%08x\n", hr);
1635     SysFreeString(str);
1636 
1637     hr = ITextStream_ReadAll(stream, NULL);
1638     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1639 
1640     str = (void*)0xdeadbeef;
1641     hr = ITextStream_ReadAll(stream, &str);
1642     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
1643     ok(str == NULL || broken(str == (void*)0xdeadbeef) /* win2k */, "got %p\n", str);
1644 
1645     ITextStream_Release(stream);
1646 
1647     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateFalse, &stream);
1648     ok(hr == S_OK, "got 0x%08x\n", hr);
1649 
1650     hr = ITextStream_ReadAll(stream, NULL);
1651     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1652 
1653     /* Buffer content is not interpreted - BOM is kept, all data is converted to WCHARs */
1654     str = NULL;
1655     hr = ITextStream_ReadAll(stream, &str);
1656     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
1657     buffW[0] = 0;
1658     MultiByteToWideChar(CP_ACP, 0, utf16bom, -1, buffW, ARRAY_SIZE(buffW));
1659     ok(str[0] == buffW[0] && str[1] == buffW[1], "got %s, %d\n", wine_dbgstr_w(str), SysStringLen(str));
1660     SysFreeString(str);
1661     ITextStream_Release(stream);
1662 
1663     /* Unicode file -> read with unicode stream */
1664     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
1665     ok(hr == S_OK, "got 0x%08x\n", hr);
1666 
1667     lstrcpyW(buffW, nameW);
1668     lstrcatW(buffW, crlfW);
1669     lstrcatW(buffW, secondlineW);
1670     lstrcatW(buffW, crlfW);
1671     str = NULL;
1672     hr = ITextStream_ReadAll(stream, &str);
1673     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
1674     ok(!lstrcmpW(buffW, str), "got %s\n", wine_dbgstr_w(str));
1675     SysFreeString(str);
1676 
1677     /* ReadAll one more time */
1678     str = (void*)0xdeadbeef;
1679     hr = ITextStream_ReadAll(stream, &str);
1680     ok(hr == CTL_E_ENDOFFILE, "got 0x%08x\n", hr);
1681     ok(str == NULL || broken(str == (void*)0xdeadbeef) /* win2k */, "got %p\n", str);
1682 
1683     /* ReadLine fails the same way */
1684     str = (void*)0xdeadbeef;
1685     hr = ITextStream_ReadLine(stream, &str);
1686     ok(hr == CTL_E_ENDOFFILE, "got 0x%08x\n", hr);
1687     ok(str == NULL || broken(str == (void*)0xdeadbeef) /* win2k */, "got %p\n", str);
1688     ITextStream_Release(stream);
1689 
1690     /* Open again and skip first line before ReadAll */
1691     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
1692     ok(hr == S_OK, "got 0x%08x\n", hr);
1693 
1694     str = NULL;
1695     hr = ITextStream_ReadLine(stream, &str);
1696 todo_wine {
1697     ok(hr == S_OK, "got 0x%08x\n", hr);
1698     ok(str != NULL, "got %p\n", str);
1699 }
1700     SysFreeString(str);
1701 
1702     lstrcpyW(buffW, secondlineW);
1703     lstrcatW(buffW, crlfW);
1704     str = NULL;
1705     hr = ITextStream_ReadAll(stream, &str);
1706     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
1707 todo_wine
1708     ok(!lstrcmpW(buffW, str), "got %s\n", wine_dbgstr_w(str));
1709     SysFreeString(str);
1710     ITextStream_Release(stream);
1711 
1712     /* ASCII file, read with Unicode stream */
1713     /* 1. one byte content, not enough for Unicode read */
1714     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_TRUE, VARIANT_FALSE, &stream);
1715     ok(hr == S_OK, "got 0x%08x\n", hr);
1716     str = SysAllocString(aW);
1717     hr = ITextStream_Write(stream, str);
1718     ok(hr == S_OK, "got 0x%08x\n", hr);
1719     SysFreeString(str);
1720     ITextStream_Release(stream);
1721 
1722     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
1723     ok(hr == S_OK, "got 0x%08x\n", hr);
1724 
1725     str = (void*)0xdeadbeef;
1726     hr = ITextStream_ReadAll(stream, &str);
1727     ok(hr == CTL_E_ENDOFFILE, "got 0x%08x\n", hr);
1728     ok(str == NULL || broken(str == (void*)0xdeadbeef) /* win2k */, "got %p\n", str);
1729 
1730     ITextStream_Release(stream);
1731 
1732     DeleteFileW(nameW);
1733     RemoveDirectoryW(dirW);
1734     SysFreeString(nameW);
1735 }
1736 
1737 static void test_Read(void)
1738 {
1739     static const WCHAR secondlineW[] = {'s','e','c','o','n','d',0};
1740     static const WCHAR aW[] = {'A',0};
1741     WCHAR pathW[MAX_PATH], dirW[MAX_PATH], buffW[500];
1742     ITextStream *stream;
1743     BSTR nameW;
1744     HRESULT hr;
1745     BOOL ret;
1746     BSTR str;
1747 
1748     get_temp_filepath(testfileW, pathW, dirW);
1749 
1750     ret = CreateDirectoryW(dirW, NULL);
1751     ok(ret, "got %d, %d\n", ret, GetLastError());
1752 
1753     /* Unicode file -> read with ascii stream */
1754     nameW = SysAllocString(pathW);
1755     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_FALSE, VARIANT_TRUE, &stream);
1756     ok(hr == S_OK, "got 0x%08x\n", hr);
1757 
1758     hr = ITextStream_WriteLine(stream, nameW);
1759     ok(hr == S_OK, "got 0x%08x\n", hr);
1760 
1761     str = SysAllocString(secondlineW);
1762     hr = ITextStream_WriteLine(stream, str);
1763     ok(hr == S_OK, "got 0x%08x\n", hr);
1764     SysFreeString(str);
1765 
1766     hr = ITextStream_Read(stream, 0, NULL);
1767     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1768 
1769     hr = ITextStream_Read(stream, 1, NULL);
1770     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1771 
1772     hr = ITextStream_Read(stream, -1, NULL);
1773     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1774 
1775     str = (void*)0xdeadbeef;
1776     hr = ITextStream_Read(stream, 1, &str);
1777     ok(hr == CTL_E_BADFILEMODE, "got 0x%08x\n", hr);
1778     ok(str == NULL, "got %p\n", str);
1779 
1780     ITextStream_Release(stream);
1781 
1782     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateFalse, &stream);
1783     ok(hr == S_OK, "got 0x%08x\n", hr);
1784 
1785     hr = ITextStream_Read(stream, 1, NULL);
1786     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1787 
1788     str = (void*)0xdeadbeef;
1789     hr = ITextStream_Read(stream, -1, &str);
1790     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1791     ok(str == NULL, "got %p\n", str);
1792 
1793     str = (void*)0xdeadbeef;
1794     hr = ITextStream_Read(stream, 0, &str);
1795     ok(hr == S_OK, "got 0x%08x\n", hr);
1796     ok(str == NULL, "got %p\n", str);
1797 
1798     /* Buffer content is not interpreted - BOM is kept, all data is converted to WCHARs */
1799     str = NULL;
1800     hr = ITextStream_Read(stream, 2, &str);
1801     ok(hr == S_OK, "got 0x%08x\n", hr);
1802 
1803     buffW[0] = 0;
1804     MultiByteToWideChar(CP_ACP, 0, utf16bom, -1, buffW, ARRAY_SIZE(buffW));
1805 
1806     ok(!lstrcmpW(str, buffW), "got %s, expected %s\n", wine_dbgstr_w(str), wine_dbgstr_w(buffW));
1807     ok(SysStringLen(str) == 2, "got %d\n", SysStringLen(str));
1808     SysFreeString(str);
1809     ITextStream_Release(stream);
1810 
1811     /* Unicode file -> read with unicode stream */
1812     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
1813     ok(hr == S_OK, "got 0x%08x\n", hr);
1814 
1815     lstrcpyW(buffW, nameW);
1816     lstrcatW(buffW, crlfW);
1817     lstrcatW(buffW, secondlineW);
1818     lstrcatW(buffW, crlfW);
1819     str = NULL;
1820     hr = ITextStream_Read(stream, 500, &str);
1821     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
1822     ok(!lstrcmpW(buffW, str), "got %s\n", wine_dbgstr_w(str));
1823     SysFreeString(str);
1824 
1825     /* ReadAll one more time */
1826     str = (void*)0xdeadbeef;
1827     hr = ITextStream_Read(stream, 10, &str);
1828     ok(hr == CTL_E_ENDOFFILE, "got 0x%08x\n", hr);
1829     ok(str == NULL, "got %p\n", str);
1830 
1831     /* ReadLine fails the same way */
1832     str = (void*)0xdeadbeef;
1833     hr = ITextStream_ReadLine(stream, &str);
1834     ok(hr == CTL_E_ENDOFFILE, "got 0x%08x\n", hr);
1835     ok(str == NULL || broken(str == (void*)0xdeadbeef), "got %p\n", str);
1836     ITextStream_Release(stream);
1837 
1838     /* Open again and skip first line before ReadAll */
1839     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
1840     ok(hr == S_OK, "got 0x%08x\n", hr);
1841 
1842     str = NULL;
1843     hr = ITextStream_ReadLine(stream, &str);
1844 todo_wine {
1845     ok(hr == S_OK, "got 0x%08x\n", hr);
1846     ok(str != NULL, "got %p\n", str);
1847 }
1848     SysFreeString(str);
1849 
1850     lstrcpyW(buffW, secondlineW);
1851     lstrcatW(buffW, crlfW);
1852     str = NULL;
1853     hr = ITextStream_Read(stream, 100, &str);
1854     ok(hr == S_FALSE || broken(hr == S_OK) /* win2k */, "got 0x%08x\n", hr);
1855 todo_wine
1856     ok(!lstrcmpW(buffW, str), "got %s\n", wine_dbgstr_w(str));
1857     SysFreeString(str);
1858     ITextStream_Release(stream);
1859 
1860     /* ASCII file, read with Unicode stream */
1861     /* 1. one byte content, not enough for Unicode read */
1862     hr = IFileSystem3_CreateTextFile(fs3, nameW, VARIANT_TRUE, VARIANT_FALSE, &stream);
1863     ok(hr == S_OK, "got 0x%08x\n", hr);
1864     str = SysAllocString(aW);
1865     hr = ITextStream_Write(stream, str);
1866     ok(hr == S_OK, "got 0x%08x\n", hr);
1867     SysFreeString(str);
1868     ITextStream_Release(stream);
1869 
1870     hr = IFileSystem3_OpenTextFile(fs3, nameW, ForReading, VARIANT_FALSE, TristateTrue, &stream);
1871     ok(hr == S_OK, "got 0x%08x\n", hr);
1872 
1873     str = (void*)0xdeadbeef;
1874     hr = ITextStream_Read(stream, 500, &str);
1875     ok(hr == CTL_E_ENDOFFILE, "got 0x%08x\n", hr);
1876     ok(str == NULL, "got %p\n", str);
1877 
1878     ITextStream_Release(stream);
1879 
1880     DeleteFileW(nameW);
1881     RemoveDirectoryW(dirW);
1882     SysFreeString(nameW);
1883 }
1884 
1885 struct driveexists_test {
1886     const WCHAR drivespec[10];
1887     const INT drivetype;
1888     const VARIANT_BOOL expected_ret;
1889 };
1890 
1891 /* If 'drivetype' != -1, the first character of 'drivespec' will be replaced
1892  * with the drive letter of a drive of this type. If such a drive does not exist,
1893  * the test will be skipped. */
1894 static const struct driveexists_test driveexiststestdata[] = {
1895     { {'N',':','\\',0}, DRIVE_NO_ROOT_DIR, VARIANT_FALSE },
1896     { {'R',':','\\',0}, DRIVE_REMOVABLE, VARIANT_TRUE },
1897     { {'F',':','\\',0}, DRIVE_FIXED, VARIANT_TRUE },
1898     { {'F',':',0}, DRIVE_FIXED, VARIANT_TRUE },
1899     { {'F','?',0}, DRIVE_FIXED, VARIANT_FALSE },
1900     { {'F',0}, DRIVE_FIXED, VARIANT_TRUE },
1901     { {'?',0}, -1, VARIANT_FALSE },
1902     { { 0 } }
1903 };
1904 
1905 static void test_DriveExists(void)
1906 {
1907     const struct driveexists_test *ptr = driveexiststestdata;
1908     HRESULT hr;
1909     VARIANT_BOOL ret;
1910     BSTR drivespec;
1911     WCHAR root[] = {'?',':','\\',0};
1912 
1913     hr = IFileSystem3_DriveExists(fs3, NULL, NULL);
1914     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1915 
1916     ret = VARIANT_TRUE;
1917     hr = IFileSystem3_DriveExists(fs3, NULL, &ret);
1918     ok(hr == S_OK, "got 0x%08x\n", hr);
1919     ok(ret == VARIANT_FALSE, "got %x\n", ret);
1920 
1921     drivespec = SysAllocString(root);
1922     hr = IFileSystem3_DriveExists(fs3, drivespec, NULL);
1923     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1924     SysFreeString(drivespec);
1925 
1926     for (; *ptr->drivespec; ptr++) {
1927         drivespec = SysAllocString(ptr->drivespec);
1928         if (ptr->drivetype != -1) {
1929             for (root[0] = 'A'; root[0] <= 'Z'; root[0]++)
1930                 if (GetDriveTypeW(root) == ptr->drivetype)
1931                     break;
1932             if (root[0] > 'Z') {
1933                 skip("No drive with type 0x%x found, skipping test %s.\n",
1934                         ptr->drivetype, wine_dbgstr_w(ptr->drivespec));
1935                 SysFreeString(drivespec);
1936                 continue;
1937             }
1938 
1939             /* Test both upper and lower case drive letters. */
1940             drivespec[0] = root[0];
1941             ret = ptr->expected_ret == VARIANT_TRUE ? VARIANT_FALSE : VARIANT_TRUE;
1942             hr = IFileSystem3_DriveExists(fs3, drivespec, &ret);
1943             ok(hr == S_OK, "got 0x%08x for drive spec %s (%s)\n",
1944                     hr, wine_dbgstr_w(drivespec), wine_dbgstr_w(ptr->drivespec));
1945             ok(ret == ptr->expected_ret, "got %d, expected %d for drive spec %s (%s)\n",
1946                     ret, ptr->expected_ret, wine_dbgstr_w(drivespec), wine_dbgstr_w(ptr->drivespec));
1947 
1948             drivespec[0] = tolower(root[0]);
1949         }
1950 
1951         ret = ptr->expected_ret == VARIANT_TRUE ? VARIANT_FALSE : VARIANT_TRUE;
1952         hr = IFileSystem3_DriveExists(fs3, drivespec, &ret);
1953         ok(hr == S_OK, "got 0x%08x for drive spec %s (%s)\n",
1954                 hr, wine_dbgstr_w(drivespec), wine_dbgstr_w(ptr->drivespec));
1955         ok(ret == ptr->expected_ret, "got %d, expected %d for drive spec %s (%s)\n",
1956                 ret, ptr->expected_ret, wine_dbgstr_w(drivespec), wine_dbgstr_w(ptr->drivespec));
1957 
1958         SysFreeString(drivespec);
1959     }
1960 }
1961 
1962 struct getdrivename_test {
1963     const WCHAR path[10];
1964     const WCHAR drive[5];
1965 };
1966 
1967 static const struct getdrivename_test getdrivenametestdata[] = {
1968     { {'C',':','\\','1','.','t','s','t',0}, {'C',':',0} },
1969     { {'O',':','\\','1','.','t','s','t',0}, {'O',':',0} },
1970     { {'O',':',0}, {'O',':',0} },
1971     { {'o',':',0}, {'o',':',0} },
1972     { {'O','O',':',0} },
1973     { {':',0} },
1974     { {'O',0} },
1975     { { 0 } }
1976 };
1977 
1978 static void test_GetDriveName(void)
1979 {
1980     const struct getdrivename_test *ptr = getdrivenametestdata;
1981     HRESULT hr;
1982     BSTR name;
1983 
1984     hr = IFileSystem3_GetDriveName(fs3, NULL, NULL);
1985     ok(hr == E_POINTER, "got 0x%08x\n", hr);
1986 
1987     name = (void*)0xdeadbeef;
1988     hr = IFileSystem3_GetDriveName(fs3, NULL, &name);
1989     ok(hr == S_OK, "got 0x%08x\n", hr);
1990     ok(name == NULL, "got %p\n", name);
1991 
1992     while (*ptr->path) {
1993         BSTR path = SysAllocString(ptr->path);
1994         name = (void*)0xdeadbeef;
1995         hr = IFileSystem3_GetDriveName(fs3, path, &name);
1996         ok(hr == S_OK, "got 0x%08x\n", hr);
1997         if (name)
1998             ok(!lstrcmpW(ptr->drive, name), "got %s, expected %s\n", wine_dbgstr_w(name), wine_dbgstr_w(ptr->drive));
1999         else
2000             ok(!*ptr->drive, "got %s, expected %s\n", wine_dbgstr_w(name), wine_dbgstr_w(ptr->drive));
2001         SysFreeString(path);
2002         SysFreeString(name);
2003         ptr++;
2004     }
2005 }
2006 
2007 struct getdrive_test {
2008     const WCHAR drivespec[12];
2009     HRESULT res;
2010     const WCHAR driveletter[2];
2011 };
2012 
2013 static void test_GetDrive(void)
2014 {
2015     HRESULT hr;
2016     IDrive *drive_fixed, *drive;
2017     BSTR dl_fixed, drivespec;
2018     WCHAR root[] = {'?',':','\\',0};
2019 
2020     drive = (void*)0xdeadbeef;
2021     hr = IFileSystem3_GetDrive(fs3, NULL, NULL);
2022     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2023     ok(drive == (void*)0xdeadbeef, "got %p\n", drive);
2024 
2025     for (root[0] = 'A'; root[0] <= 'Z'; root[0]++)
2026         if (GetDriveTypeW(root) == DRIVE_NO_ROOT_DIR)
2027             break;
2028 
2029     if (root[0] > 'Z')
2030         skip("All drive letters are occupied, skipping test for nonexisting drive.\n");
2031     else {
2032         drivespec = SysAllocString(root);
2033         drive = (void*)0xdeadbeef;
2034         hr = IFileSystem3_GetDrive(fs3, drivespec, &drive);
2035         ok(hr == CTL_E_DEVICEUNAVAILABLE, "got 0x%08x\n", hr);
2036         ok(drive == NULL, "got %p\n", drive);
2037         SysFreeString(drivespec);
2038     }
2039 
2040     drive_fixed = get_fixed_drive();
2041     if (!drive_fixed) {
2042         skip("No fixed drive found, skipping test.\n");
2043         return;
2044     }
2045 
2046     hr = IDrive_get_DriveLetter(drive_fixed, &dl_fixed);
2047     ok(hr == S_OK, "got 0x%08x\n", hr);
2048 
2049     if (FAILED(hr))
2050         skip("Could not retrieve drive letter of fixed drive, skipping test.\n");
2051     else {
2052         WCHAR dl_upper = toupper(dl_fixed[0]);
2053         WCHAR dl_lower = tolower(dl_fixed[0]);
2054         struct getdrive_test testdata[] = {
2055             { {dl_upper,0}, S_OK, {dl_upper,0} },
2056             { {dl_upper,':',0}, S_OK, {dl_upper,0} },
2057             { {dl_upper,':','\\',0}, S_OK, {dl_upper,0} },
2058             { {dl_lower,':','\\',0}, S_OK, {dl_upper,0} },
2059             { {dl_upper,'\\',0}, E_INVALIDARG, { 0 } },
2060             { {dl_lower,'\\',0}, E_INVALIDARG, { 0 } },
2061             { {'$',':','\\',0}, E_INVALIDARG, { 0 } },
2062             { {'\\','h','o','s','t','\\','s','h','a','r','e',0}, E_INVALIDARG, { 0 } },
2063             { {'h','o','s','t','\\','s','h','a','r','e',0}, E_INVALIDARG, { 0 } },
2064             { { 0 } },
2065         };
2066         struct getdrive_test *ptr = &testdata[0];
2067 
2068         for (; *ptr->drivespec; ptr++) {
2069             drivespec = SysAllocString(ptr->drivespec);
2070             drive = (void*)0xdeadbeef;
2071             hr = IFileSystem3_GetDrive(fs3, drivespec, &drive);
2072             ok(hr == ptr->res, "got 0x%08x, expected 0x%08x for drive spec %s\n",
2073                     hr, ptr->res, wine_dbgstr_w(ptr->drivespec));
2074             ok(!lstrcmpW(ptr->drivespec, drivespec), "GetDrive modified its DriveSpec argument\n");
2075             SysFreeString(drivespec);
2076 
2077             if (*ptr->driveletter) {
2078                 BSTR driveletter;
2079                 hr = IDrive_get_DriveLetter(drive, &driveletter);
2080                 ok(hr == S_OK, "got 0x%08x for drive spec %s\n", hr, wine_dbgstr_w(ptr->drivespec));
2081                 if (SUCCEEDED(hr)) {
2082                     ok(!lstrcmpW(ptr->driveletter, driveletter), "got %s, expected %s for drive spec %s\n",
2083                             wine_dbgstr_w(driveletter), wine_dbgstr_w(ptr->driveletter),
2084                             wine_dbgstr_w(ptr->drivespec));
2085                     SysFreeString(driveletter);
2086                 }
2087                 test_provideclassinfo(drive, &CLSID_Drive);
2088                 IDrive_Release(drive);
2089             } else
2090                 ok(drive == NULL, "got %p for drive spec %s\n", drive, wine_dbgstr_w(ptr->drivespec));
2091         }
2092         SysFreeString(dl_fixed);
2093     }
2094 }
2095 
2096 static void test_SerialNumber(void)
2097 {
2098     IDrive *drive;
2099     LONG serial;
2100     HRESULT hr;
2101     BSTR name;
2102 
2103     drive = get_fixed_drive();
2104     if (!drive) {
2105         skip("No fixed drive found, skipping test.\n");
2106         return;
2107     }
2108 
2109     hr = IDrive_get_SerialNumber(drive, NULL);
2110     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2111 
2112     serial = 0xdeadbeef;
2113     hr = IDrive_get_SerialNumber(drive, &serial);
2114     ok(hr == S_OK, "got 0x%08x\n", hr);
2115     ok(serial != 0xdeadbeef, "got %x\n", serial);
2116 
2117     hr = IDrive_get_FileSystem(drive, NULL);
2118     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2119 
2120     name = NULL;
2121     hr = IDrive_get_FileSystem(drive, &name);
2122     ok(hr == S_OK, "got 0x%08x\n", hr);
2123     ok(name != NULL, "got %p\n", name);
2124     SysFreeString(name);
2125 
2126     hr = IDrive_get_VolumeName(drive, NULL);
2127     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2128 
2129     name = NULL;
2130     hr = IDrive_get_VolumeName(drive, &name);
2131     ok(hr == S_OK, "got 0x%08x\n", hr);
2132     ok(name != NULL, "got %p\n", name);
2133     SysFreeString(name);
2134 
2135     IDrive_Release(drive);
2136 }
2137 
2138 static const struct extension_test {
2139     WCHAR path[20];
2140     WCHAR ext[10];
2141 } extension_tests[] = {
2142     { {'n','o','e','x','t',0}, {0} },
2143     { {'n','.','o','.','e','x','t',0}, {'e','x','t',0} },
2144     { {'n','.','o','.','e','X','t',0}, {'e','X','t',0} },
2145     { { 0 } }
2146 };
2147 
2148 static void test_GetExtensionName(void)
2149 {
2150     BSTR path, ext;
2151     HRESULT hr;
2152     int i;
2153 
2154     for (i = 0; i < ARRAY_SIZE(extension_tests); i++) {
2155 
2156        path = SysAllocString(extension_tests[i].path);
2157        ext = NULL;
2158        hr = IFileSystem3_GetExtensionName(fs3, path, &ext);
2159        ok(hr == S_OK, "got 0x%08x\n", hr);
2160        if (*extension_tests[i].ext)
2161            ok(!lstrcmpW(ext, extension_tests[i].ext), "%d: path %s, got %s, expected %s\n", i,
2162                wine_dbgstr_w(path), wine_dbgstr_w(ext), wine_dbgstr_w(extension_tests[i].ext));
2163        else
2164            ok(ext == NULL, "%d: path %s, got %s, expected %s\n", i,
2165                wine_dbgstr_w(path), wine_dbgstr_w(ext), wine_dbgstr_w(extension_tests[i].ext));
2166 
2167        SysFreeString(path);
2168        SysFreeString(ext);
2169     }
2170 }
2171 
2172 static void test_GetSpecialFolder(void)
2173 {
2174     WCHAR pathW[MAX_PATH];
2175     IFolder *folder;
2176     HRESULT hr;
2177     DWORD ret;
2178     BSTR path;
2179 
2180     hr = IFileSystem3_GetSpecialFolder(fs3, WindowsFolder, NULL);
2181     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2182 
2183     hr = IFileSystem3_GetSpecialFolder(fs3, TemporaryFolder+1, NULL);
2184     ok(hr == E_POINTER, "got 0x%08x\n", hr);
2185 
2186     hr = IFileSystem3_GetSpecialFolder(fs3, TemporaryFolder+1, &folder);
2187     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2188 
2189     hr = IFileSystem3_GetSpecialFolder(fs3, WindowsFolder, &folder);
2190     ok(hr == S_OK, "got 0x%08x\n", hr);
2191     hr = IFolder_get_Path(folder, &path);
2192     ok(hr == S_OK, "got 0x%08x\n", hr);
2193     GetWindowsDirectoryW(pathW, ARRAY_SIZE(pathW));
2194     ok(!lstrcmpiW(pathW, path), "got %s, expected %s\n", wine_dbgstr_w(path), wine_dbgstr_w(pathW));
2195     SysFreeString(path);
2196     IFolder_Release(folder);
2197 
2198     hr = IFileSystem3_GetSpecialFolder(fs3, SystemFolder, &folder);
2199     ok(hr == S_OK, "got 0x%08x\n", hr);
2200     hr = IFolder_get_Path(folder, &path);
2201     ok(hr == S_OK, "got 0x%08x\n", hr);
2202     GetSystemDirectoryW(pathW, ARRAY_SIZE(pathW));
2203     ok(!lstrcmpiW(pathW, path), "got %s, expected %s\n", wine_dbgstr_w(path), wine_dbgstr_w(pathW));
2204     SysFreeString(path);
2205     IFolder_Release(folder);
2206 
2207     hr = IFileSystem3_GetSpecialFolder(fs3, TemporaryFolder, &folder);
2208     ok(hr == S_OK, "got 0x%08x\n", hr);
2209     hr = IFolder_get_Path(folder, &path);
2210     ok(hr == S_OK, "got 0x%08x\n", hr);
2211     ret = GetTempPathW(ARRAY_SIZE(pathW), pathW);
2212     if (ret && pathW[ret-1] == '\\')
2213         pathW[ret-1] = 0;
2214 
2215     ok(!lstrcmpiW(pathW, path), "got %s, expected %s\n", wine_dbgstr_w(path), wine_dbgstr_w(pathW));
2216     SysFreeString(path);
2217     IFolder_Release(folder);
2218 }
2219 
2220 START_TEST(filesystem)
2221 {
2222     HRESULT hr;
2223 
2224     CoInitialize(NULL);
2225 
2226     hr = CoCreateInstance(&CLSID_FileSystemObject, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
2227             &IID_IFileSystem3, (void**)&fs3);
2228     if(FAILED(hr)) {
2229         win_skip("Could not create FileSystem object: %08x\n", hr);
2230         return;
2231     }
2232 
2233     test_interfaces();
2234     test_createfolder();
2235     test_textstream();
2236     test_GetFileVersion();
2237     test_GetParentFolderName();
2238     test_GetFileName();
2239     test_GetBaseName();
2240     test_GetAbsolutePathName();
2241     test_GetFile();
2242     test_CopyFolder();
2243     test_BuildPath();
2244     test_GetFolder();
2245     test_FolderCollection();
2246     test_FileCollection();
2247     test_DriveCollection();
2248     test_CreateTextFile();
2249     test_WriteLine();
2250     test_ReadAll();
2251     test_Read();
2252     test_DriveExists();
2253     test_GetDriveName();
2254     test_GetDrive();
2255     test_SerialNumber();
2256     test_GetExtensionName();
2257     test_GetSpecialFolder();
2258 
2259     IFileSystem3_Release(fs3);
2260 
2261     CoUninitialize();
2262 }
2263