xref: /reactos/dll/win32/scrrun/filesystem.c (revision c2c66aff)
1 /*
2  * Copyright 2012 Alistair Leslie-Hughes
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "scrrun_private.h"
20 
21 #include <winver.h>
22 #include <olectl.h>
23 #include <ntsecapi.h>
24 #include <wine/unicode.h>
25 
26 static const WCHAR bsW[] = {'\\',0};
27 static const WCHAR utf16bom = 0xfeff;
28 
29 struct filesystem {
30     struct provideclassinfo classinfo;
31     IFileSystem3 IFileSystem3_iface;
32 };
33 
34 struct foldercollection {
35     struct provideclassinfo classinfo;
36     IFolderCollection IFolderCollection_iface;
37     LONG ref;
38     BSTR path;
39 };
40 
41 struct filecollection {
42     struct provideclassinfo classinfo;
43     IFileCollection IFileCollection_iface;
44     LONG ref;
45     BSTR path;
46 };
47 
48 struct drivecollection {
49     struct provideclassinfo classinfo;
50     IDriveCollection IDriveCollection_iface;
51     LONG ref;
52     DWORD drives;
53     LONG count;
54 };
55 
56 struct enumdata {
57     union
58     {
59         struct
60         {
61             struct foldercollection *coll;
62             HANDLE find;
63         } foldercoll;
64         struct
65         {
66             struct filecollection *coll;
67             HANDLE find;
68         } filecoll;
69         struct
70         {
71             struct drivecollection *coll;
72             INT cur;
73         } drivecoll;
74     } u;
75 };
76 
77 struct enumvariant {
78     IEnumVARIANT IEnumVARIANT_iface;
79     LONG ref;
80 
81     struct enumdata data;
82 };
83 
84 struct drive {
85     struct provideclassinfo classinfo;
86     IDrive IDrive_iface;
87     LONG ref;
88     BSTR root;
89 };
90 
91 struct folder {
92     struct provideclassinfo classinfo;
93     IFolder IFolder_iface;
94     LONG ref;
95     BSTR path;
96 };
97 
98 struct file {
99     struct provideclassinfo classinfo;
100     IFile IFile_iface;
101     LONG ref;
102 
103     WCHAR *path;
104 };
105 
106 struct textstream {
107     struct provideclassinfo classinfo;
108     ITextStream ITextStream_iface;
109     LONG ref;
110 
111     IOMode mode;
112     BOOL unicode;
113     BOOL first_read;
114     LARGE_INTEGER size;
115     HANDLE file;
116 };
117 
118 enum iotype {
119     IORead,
120     IOWrite
121 };
122 
123 static inline struct filesystem *impl_from_IFileSystem3(IFileSystem3 *iface)
124 {
125     return CONTAINING_RECORD(iface, struct filesystem, IFileSystem3_iface);
126 }
127 
128 static inline struct drive *impl_from_IDrive(IDrive *iface)
129 {
130     return CONTAINING_RECORD(iface, struct drive, IDrive_iface);
131 }
132 
133 static inline struct folder *impl_from_IFolder(IFolder *iface)
134 {
135     return CONTAINING_RECORD(iface, struct folder, IFolder_iface);
136 }
137 
138 static inline struct file *impl_from_IFile(IFile *iface)
139 {
140     return CONTAINING_RECORD(iface, struct file, IFile_iface);
141 }
142 
143 static inline struct textstream *impl_from_ITextStream(ITextStream *iface)
144 {
145     return CONTAINING_RECORD(iface, struct textstream, ITextStream_iface);
146 }
147 
148 static inline struct foldercollection *impl_from_IFolderCollection(IFolderCollection *iface)
149 {
150     return CONTAINING_RECORD(iface, struct foldercollection, IFolderCollection_iface);
151 }
152 
153 static inline struct filecollection *impl_from_IFileCollection(IFileCollection *iface)
154 {
155     return CONTAINING_RECORD(iface, struct filecollection, IFileCollection_iface);
156 }
157 
158 static inline struct drivecollection *impl_from_IDriveCollection(IDriveCollection *iface)
159 {
160     return CONTAINING_RECORD(iface, struct drivecollection, IDriveCollection_iface);
161 }
162 
163 static inline struct enumvariant *impl_from_IEnumVARIANT(IEnumVARIANT *iface)
164 {
165     return CONTAINING_RECORD(iface, struct enumvariant, IEnumVARIANT_iface);
166 }
167 
168 static inline HRESULT create_error(DWORD err)
169 {
170     switch(err) {
171     case ERROR_FILE_NOT_FOUND: return CTL_E_FILENOTFOUND;
172     case ERROR_PATH_NOT_FOUND: return CTL_E_PATHNOTFOUND;
173     case ERROR_ACCESS_DENIED: return CTL_E_PERMISSIONDENIED;
174     case ERROR_FILE_EXISTS: return CTL_E_FILEALREADYEXISTS;
175     case ERROR_ALREADY_EXISTS: return CTL_E_FILEALREADYEXISTS;
176     default:
177         FIXME("Unsupported error code: %d\n", err);
178         return E_FAIL;
179     }
180 }
181 
182 static HRESULT create_folder(const WCHAR*, IFolder**);
183 static HRESULT create_file(BSTR, IFile**);
184 static HRESULT create_foldercoll_enum(struct foldercollection*, IUnknown**);
185 static HRESULT create_filecoll_enum(struct filecollection*, IUnknown**);
186 
187 static inline BOOL is_dir_data(const WIN32_FIND_DATAW *data)
188 {
189     static const WCHAR dotdotW[] = {'.','.',0};
190     static const WCHAR dotW[] = {'.',0};
191 
192     return (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
193             strcmpW(data->cFileName, dotdotW) &&
194             strcmpW(data->cFileName, dotW);
195 }
196 
197 static inline BOOL is_file_data(const WIN32_FIND_DATAW *data)
198 {
199     return !(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
200 }
201 
202 static BSTR get_full_path(BSTR path, const WIN32_FIND_DATAW *data)
203 {
204     int len = SysStringLen(path);
205     WCHAR buffW[MAX_PATH];
206 
207     strcpyW(buffW, path);
208     if (path[len-1] != '\\')
209         strcatW(buffW, bsW);
210     strcatW(buffW, data->cFileName);
211 
212     return SysAllocString(buffW);
213 }
214 
215 static BOOL textstream_check_iomode(struct textstream *This, enum iotype type)
216 {
217     if (type == IORead)
218         return This->mode == ForWriting || This->mode == ForAppending;
219     else
220         return This->mode == ForReading;
221 }
222 
223 static HRESULT WINAPI textstream_QueryInterface(ITextStream *iface, REFIID riid, void **obj)
224 {
225     struct textstream *This = impl_from_ITextStream(iface);
226 
227     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
228 
229     if (IsEqualIID(riid, &IID_ITextStream) ||
230         IsEqualIID(riid, &IID_IDispatch) ||
231         IsEqualIID(riid, &IID_IUnknown))
232     {
233         *obj = &This->ITextStream_iface;
234     }
235     else if (IsEqualIID(riid, &IID_IProvideClassInfo))
236     {
237         *obj = &This->classinfo.IProvideClassInfo_iface;
238     }
239     else
240         return E_NOINTERFACE;
241 
242     IUnknown_AddRef((IUnknown*)*obj);
243     return S_OK;
244 }
245 
246 static ULONG WINAPI textstream_AddRef(ITextStream *iface)
247 {
248     struct textstream *This = impl_from_ITextStream(iface);
249     ULONG ref = InterlockedIncrement(&This->ref);
250     TRACE("(%p)->(%d)\n", This, ref);
251     return ref;
252 }
253 
254 static ULONG WINAPI textstream_Release(ITextStream *iface)
255 {
256     struct textstream *This = impl_from_ITextStream(iface);
257     ULONG ref = InterlockedDecrement(&This->ref);
258     TRACE("(%p)->(%d)\n", This, ref);
259 
260     if (!ref)
261     {
262         CloseHandle(This->file);
263         heap_free(This);
264     }
265 
266     return ref;
267 }
268 
269 static HRESULT WINAPI textstream_GetTypeInfoCount(ITextStream *iface, UINT *pctinfo)
270 {
271     struct textstream *This = impl_from_ITextStream(iface);
272     TRACE("(%p)->(%p)\n", This, pctinfo);
273     *pctinfo = 1;
274     return S_OK;
275 }
276 
277 static HRESULT WINAPI textstream_GetTypeInfo(ITextStream *iface, UINT iTInfo,
278                                         LCID lcid, ITypeInfo **ppTInfo)
279 {
280     struct textstream *This = impl_from_ITextStream(iface);
281     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
282     return get_typeinfo(ITextStream_tid, ppTInfo);
283 }
284 
285 static HRESULT WINAPI textstream_GetIDsOfNames(ITextStream *iface, REFIID riid,
286                                         LPOLESTR *rgszNames, UINT cNames,
287                                         LCID lcid, DISPID *rgDispId)
288 {
289     struct textstream *This = impl_from_ITextStream(iface);
290     ITypeInfo *typeinfo;
291     HRESULT hr;
292 
293     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
294 
295     hr = get_typeinfo(ITextStream_tid, &typeinfo);
296     if(SUCCEEDED(hr))
297     {
298         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
299         ITypeInfo_Release(typeinfo);
300     }
301 
302     return hr;
303 }
304 
305 static HRESULT WINAPI textstream_Invoke(ITextStream *iface, DISPID dispIdMember,
306                                       REFIID riid, LCID lcid, WORD wFlags,
307                                       DISPPARAMS *pDispParams, VARIANT *pVarResult,
308                                       EXCEPINFO *pExcepInfo, UINT *puArgErr)
309 {
310     struct textstream *This = impl_from_ITextStream(iface);
311     ITypeInfo *typeinfo;
312     HRESULT hr;
313 
314     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
315            lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
316 
317     hr = get_typeinfo(ITextStream_tid, &typeinfo);
318     if(SUCCEEDED(hr))
319     {
320         hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags,
321                 pDispParams, pVarResult, pExcepInfo, puArgErr);
322         ITypeInfo_Release(typeinfo);
323     }
324 
325     return hr;
326 }
327 
328 static HRESULT WINAPI textstream_get_Line(ITextStream *iface, LONG *line)
329 {
330     struct textstream *This = impl_from_ITextStream(iface);
331     FIXME("(%p)->(%p): stub\n", This, line);
332     return E_NOTIMPL;
333 }
334 
335 static HRESULT WINAPI textstream_get_Column(ITextStream *iface, LONG *column)
336 {
337     struct textstream *This = impl_from_ITextStream(iface);
338     FIXME("(%p)->(%p): stub\n", This, column);
339     return E_NOTIMPL;
340 }
341 
342 static HRESULT WINAPI textstream_get_AtEndOfStream(ITextStream *iface, VARIANT_BOOL *eos)
343 {
344     struct textstream *This = impl_from_ITextStream(iface);
345     LARGE_INTEGER pos, dist;
346 
347     TRACE("(%p)->(%p)\n", This, eos);
348 
349     if (!eos)
350         return E_POINTER;
351 
352     if (textstream_check_iomode(This, IORead)) {
353         *eos = VARIANT_TRUE;
354         return CTL_E_BADFILEMODE;
355     }
356 
357     dist.QuadPart = 0;
358     if (!SetFilePointerEx(This->file, dist, &pos, FILE_CURRENT))
359         return E_FAIL;
360 
361     *eos = This->size.QuadPart == pos.QuadPart ? VARIANT_TRUE : VARIANT_FALSE;
362     return S_OK;
363 }
364 
365 static HRESULT WINAPI textstream_get_AtEndOfLine(ITextStream *iface, VARIANT_BOOL *eol)
366 {
367     struct textstream *This = impl_from_ITextStream(iface);
368     FIXME("(%p)->(%p): stub\n", This, eol);
369     return E_NOTIMPL;
370 }
371 
372 /*
373    Reads 'toread' bytes from a file, converts if needed
374    BOM is skipped if 'bof' is set.
375  */
376 static HRESULT textstream_read(struct textstream *stream, LONG toread, BOOL bof, BSTR *text)
377 {
378     HRESULT hr = S_OK;
379     DWORD read;
380     char *buff;
381     BOOL ret;
382 
383     if (toread == 0) {
384         *text = SysAllocStringLen(NULL, 0);
385         return *text ? S_FALSE : E_OUTOFMEMORY;
386     }
387 
388     if (toread < sizeof(WCHAR))
389         return CTL_E_ENDOFFILE;
390 
391     buff = heap_alloc(toread);
392     if (!buff)
393         return E_OUTOFMEMORY;
394 
395     ret = ReadFile(stream->file, buff, toread, &read, NULL);
396     if (!ret || toread != read) {
397         WARN("failed to read from file %d, %d, error %d\n", read, toread, GetLastError());
398         heap_free(buff);
399         return E_FAIL;
400     }
401 
402     if (stream->unicode) {
403         int i = 0;
404 
405         /* skip BOM */
406         if (bof && *(WCHAR*)buff == utf16bom) {
407             read -= sizeof(WCHAR);
408             i += sizeof(WCHAR);
409         }
410 
411         *text = SysAllocStringLen(read ? (WCHAR*)&buff[i] : NULL, read/sizeof(WCHAR));
412         if (!*text) hr = E_OUTOFMEMORY;
413     }
414     else {
415         INT len = MultiByteToWideChar(CP_ACP, 0, buff, read, NULL, 0);
416         *text = SysAllocStringLen(NULL, len);
417         if (*text)
418             MultiByteToWideChar(CP_ACP, 0, buff, read, *text, len);
419         else
420             hr = E_OUTOFMEMORY;
421     }
422     heap_free(buff);
423 
424     return hr;
425 }
426 
427 static HRESULT WINAPI textstream_Read(ITextStream *iface, LONG len, BSTR *text)
428 {
429     struct textstream *This = impl_from_ITextStream(iface);
430     LARGE_INTEGER start, end, dist;
431     DWORD toread;
432     HRESULT hr;
433 
434     TRACE("(%p)->(%d %p)\n", This, len, text);
435 
436     if (!text)
437         return E_POINTER;
438 
439     *text = NULL;
440     if (len <= 0)
441         return len == 0 ? S_OK : E_INVALIDARG;
442 
443     if (textstream_check_iomode(This, IORead))
444         return CTL_E_BADFILEMODE;
445 
446     if (!This->first_read) {
447         VARIANT_BOOL eos;
448 
449         /* check for EOF */
450         hr = ITextStream_get_AtEndOfStream(iface, &eos);
451         if (FAILED(hr))
452             return hr;
453 
454         if (eos == VARIANT_TRUE)
455             return CTL_E_ENDOFFILE;
456     }
457 
458     /* read everything from current position */
459     dist.QuadPart = 0;
460     SetFilePointerEx(This->file, dist, &start, FILE_CURRENT);
461     SetFilePointerEx(This->file, dist, &end, FILE_END);
462     toread = end.QuadPart - start.QuadPart;
463     /* rewind back */
464     dist.QuadPart = start.QuadPart;
465     SetFilePointerEx(This->file, dist, NULL, FILE_BEGIN);
466 
467     This->first_read = FALSE;
468     if (This->unicode) len *= sizeof(WCHAR);
469 
470     hr = textstream_read(This, min(toread, len), start.QuadPart == 0, text);
471     if (FAILED(hr))
472         return hr;
473     else
474         return toread <= len ? S_FALSE : S_OK;
475 }
476 
477 static HRESULT WINAPI textstream_ReadLine(ITextStream *iface, BSTR *text)
478 {
479     struct textstream *This = impl_from_ITextStream(iface);
480     VARIANT_BOOL eos;
481     HRESULT hr;
482 
483     FIXME("(%p)->(%p): stub\n", This, text);
484 
485     if (!text)
486         return E_POINTER;
487 
488     *text = NULL;
489     if (textstream_check_iomode(This, IORead))
490         return CTL_E_BADFILEMODE;
491 
492     /* check for EOF */
493     hr = ITextStream_get_AtEndOfStream(iface, &eos);
494     if (FAILED(hr))
495         return hr;
496 
497     if (eos == VARIANT_TRUE)
498         return CTL_E_ENDOFFILE;
499 
500     return E_NOTIMPL;
501 }
502 
503 static HRESULT WINAPI textstream_ReadAll(ITextStream *iface, BSTR *text)
504 {
505     struct textstream *This = impl_from_ITextStream(iface);
506     LARGE_INTEGER start, end, dist;
507     DWORD toread;
508     HRESULT hr;
509 
510     TRACE("(%p)->(%p)\n", This, text);
511 
512     if (!text)
513         return E_POINTER;
514 
515     *text = NULL;
516     if (textstream_check_iomode(This, IORead))
517         return CTL_E_BADFILEMODE;
518 
519     if (!This->first_read) {
520         VARIANT_BOOL eos;
521 
522         /* check for EOF */
523         hr = ITextStream_get_AtEndOfStream(iface, &eos);
524         if (FAILED(hr))
525             return hr;
526 
527         if (eos == VARIANT_TRUE)
528             return CTL_E_ENDOFFILE;
529     }
530 
531     /* read everything from current position */
532     dist.QuadPart = 0;
533     SetFilePointerEx(This->file, dist, &start, FILE_CURRENT);
534     SetFilePointerEx(This->file, dist, &end, FILE_END);
535     toread = end.QuadPart - start.QuadPart;
536     /* rewind back */
537     dist.QuadPart = start.QuadPart;
538     SetFilePointerEx(This->file, dist, NULL, FILE_BEGIN);
539 
540     This->first_read = FALSE;
541 
542     hr = textstream_read(This, toread, start.QuadPart == 0, text);
543     return FAILED(hr) ? hr : S_FALSE;
544 }
545 
546 static HRESULT textstream_writestr(struct textstream *stream, BSTR text)
547 {
548     DWORD written = 0;
549     BOOL ret;
550 
551     if (stream->unicode) {
552         ret = WriteFile(stream->file, text, SysStringByteLen(text), &written, NULL);
553         return (ret && written == SysStringByteLen(text)) ? S_OK : create_error(GetLastError());
554     } else {
555         DWORD len = WideCharToMultiByte(CP_ACP, 0, text, SysStringLen(text), NULL, 0, NULL, NULL);
556         char *buffA;
557         HRESULT hr;
558 
559         buffA = heap_alloc(len);
560         if (!buffA)
561             return E_OUTOFMEMORY;
562 
563         WideCharToMultiByte(CP_ACP, 0, text, SysStringLen(text), buffA, len, NULL, NULL);
564         ret = WriteFile(stream->file, buffA, len, &written, NULL);
565         hr = (ret && written == len) ? S_OK : create_error(GetLastError());
566         heap_free(buffA);
567         return hr;
568     }
569 }
570 
571 static HRESULT WINAPI textstream_Write(ITextStream *iface, BSTR text)
572 {
573     struct textstream *This = impl_from_ITextStream(iface);
574 
575     TRACE("(%p)->(%s)\n", This, debugstr_w(text));
576 
577     if (textstream_check_iomode(This, IOWrite))
578         return CTL_E_BADFILEMODE;
579 
580     return textstream_writestr(This, text);
581 }
582 
583 static HRESULT textstream_writecrlf(struct textstream *stream)
584 {
585     static const WCHAR crlfW[] = {'\r','\n'};
586     static const char crlfA[] = {'\r','\n'};
587     DWORD written = 0, len;
588     const void *ptr;
589     BOOL ret;
590 
591     if (stream->unicode) {
592         ptr = crlfW;
593         len = sizeof(crlfW);
594     }
595     else {
596         ptr = crlfA;
597         len = sizeof(crlfA);
598     }
599 
600     ret = WriteFile(stream->file, ptr, len, &written, NULL);
601     return (ret && written == len) ? S_OK : create_error(GetLastError());
602 }
603 
604 static HRESULT WINAPI textstream_WriteLine(ITextStream *iface, BSTR text)
605 {
606     struct textstream *This = impl_from_ITextStream(iface);
607     HRESULT hr;
608 
609     TRACE("(%p)->(%s)\n", This, debugstr_w(text));
610 
611     if (textstream_check_iomode(This, IOWrite))
612         return CTL_E_BADFILEMODE;
613 
614     hr = textstream_writestr(This, text);
615     if (SUCCEEDED(hr))
616         hr = textstream_writecrlf(This);
617     return hr;
618 }
619 
620 static HRESULT WINAPI textstream_WriteBlankLines(ITextStream *iface, LONG lines)
621 {
622     struct textstream *This = impl_from_ITextStream(iface);
623     FIXME("(%p)->(%d): stub\n", This, lines);
624     return E_NOTIMPL;
625 }
626 
627 static HRESULT WINAPI textstream_Skip(ITextStream *iface, LONG count)
628 {
629     struct textstream *This = impl_from_ITextStream(iface);
630     FIXME("(%p)->(%d): stub\n", This, count);
631     return E_NOTIMPL;
632 }
633 
634 static HRESULT WINAPI textstream_SkipLine(ITextStream *iface)
635 {
636     struct textstream *This = impl_from_ITextStream(iface);
637     FIXME("(%p): stub\n", This);
638     return E_NOTIMPL;
639 }
640 
641 static HRESULT WINAPI textstream_Close(ITextStream *iface)
642 {
643     struct textstream *This = impl_from_ITextStream(iface);
644     HRESULT hr = S_OK;
645 
646     TRACE("(%p)\n", This);
647 
648     if(!CloseHandle(This->file))
649         hr = S_FALSE;
650 
651     This->file = NULL;
652 
653     return hr;
654 }
655 
656 static const ITextStreamVtbl textstreamvtbl = {
657     textstream_QueryInterface,
658     textstream_AddRef,
659     textstream_Release,
660     textstream_GetTypeInfoCount,
661     textstream_GetTypeInfo,
662     textstream_GetIDsOfNames,
663     textstream_Invoke,
664     textstream_get_Line,
665     textstream_get_Column,
666     textstream_get_AtEndOfStream,
667     textstream_get_AtEndOfLine,
668     textstream_Read,
669     textstream_ReadLine,
670     textstream_ReadAll,
671     textstream_Write,
672     textstream_WriteLine,
673     textstream_WriteBlankLines,
674     textstream_Skip,
675     textstream_SkipLine,
676     textstream_Close
677 };
678 
679 static HRESULT create_textstream(const WCHAR *filename, DWORD disposition, IOMode mode, BOOL unicode, ITextStream **ret)
680 {
681     struct textstream *stream;
682     DWORD access = 0;
683 
684     /* map access mode */
685     switch (mode)
686     {
687     case ForReading:
688         access = GENERIC_READ;
689         break;
690     case ForWriting:
691         access = GENERIC_WRITE;
692         break;
693     case ForAppending:
694         access = FILE_APPEND_DATA;
695         break;
696     default:
697         return E_INVALIDARG;
698     }
699 
700     stream = heap_alloc(sizeof(struct textstream));
701     if (!stream) return E_OUTOFMEMORY;
702 
703     stream->ITextStream_iface.lpVtbl = &textstreamvtbl;
704     stream->ref = 1;
705     stream->mode = mode;
706     stream->unicode = unicode;
707     stream->first_read = TRUE;
708 
709     stream->file = CreateFileW(filename, access, 0, NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL);
710     if (stream->file == INVALID_HANDLE_VALUE)
711     {
712         HRESULT hr = create_error(GetLastError());
713         heap_free(stream);
714         return hr;
715     }
716 
717     if (mode == ForReading)
718         GetFileSizeEx(stream->file, &stream->size);
719     else
720         stream->size.QuadPart = 0;
721 
722     /* Write Unicode BOM */
723     if (unicode && mode == ForWriting && (disposition == CREATE_ALWAYS || disposition == CREATE_NEW)) {
724         DWORD written = 0;
725         BOOL ret = WriteFile(stream->file, &utf16bom, sizeof(utf16bom), &written, NULL);
726         if (!ret || written != sizeof(utf16bom)) {
727             ITextStream_Release(&stream->ITextStream_iface);
728             return create_error(GetLastError());
729         }
730     }
731 
732     init_classinfo(&CLSID_TextStream, (IUnknown *)&stream->ITextStream_iface, &stream->classinfo);
733     *ret = &stream->ITextStream_iface;
734     return S_OK;
735 }
736 
737 static HRESULT WINAPI drive_QueryInterface(IDrive *iface, REFIID riid, void **obj)
738 {
739     struct drive *This = impl_from_IDrive(iface);
740 
741     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
742 
743     *obj = NULL;
744 
745     if (IsEqualIID( riid, &IID_IDrive ) ||
746         IsEqualIID( riid, &IID_IDispatch ) ||
747         IsEqualIID( riid, &IID_IUnknown))
748     {
749         *obj = &This->IDrive_iface;
750     }
751     else if (IsEqualIID( riid, &IID_IProvideClassInfo ))
752     {
753         *obj = &This->classinfo.IProvideClassInfo_iface;
754     }
755     else
756         return E_NOINTERFACE;
757 
758     IUnknown_AddRef((IUnknown*)*obj);
759     return S_OK;
760 }
761 
762 static ULONG WINAPI drive_AddRef(IDrive *iface)
763 {
764     struct drive *This = impl_from_IDrive(iface);
765     ULONG ref = InterlockedIncrement(&This->ref);
766     TRACE("(%p)->(%d)\n", This, ref);
767     return ref;
768 }
769 
770 static ULONG WINAPI drive_Release(IDrive *iface)
771 {
772     struct drive *This = impl_from_IDrive(iface);
773     ULONG ref = InterlockedDecrement(&This->ref);
774     TRACE("(%p)->(%d)\n", This, ref);
775 
776     if (!ref)
777     {
778         SysFreeString(This->root);
779         heap_free(This);
780     }
781 
782     return ref;
783 }
784 
785 static HRESULT WINAPI drive_GetTypeInfoCount(IDrive *iface, UINT *pctinfo)
786 {
787     struct drive *This = impl_from_IDrive(iface);
788     TRACE("(%p)->(%p)\n", This, pctinfo);
789     *pctinfo = 1;
790     return S_OK;
791 }
792 
793 static HRESULT WINAPI drive_GetTypeInfo(IDrive *iface, UINT iTInfo,
794                                         LCID lcid, ITypeInfo **ppTInfo)
795 {
796     struct drive *This = impl_from_IDrive(iface);
797     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
798     return get_typeinfo(IDrive_tid, ppTInfo);
799 }
800 
801 static HRESULT WINAPI drive_GetIDsOfNames(IDrive *iface, REFIID riid,
802                                         LPOLESTR *rgszNames, UINT cNames,
803                                         LCID lcid, DISPID *rgDispId)
804 {
805     struct drive *This = impl_from_IDrive(iface);
806     ITypeInfo *typeinfo;
807     HRESULT hr;
808 
809     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
810 
811     hr = get_typeinfo(IDrive_tid, &typeinfo);
812     if(SUCCEEDED(hr))
813     {
814         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
815         ITypeInfo_Release(typeinfo);
816     }
817 
818     return hr;
819 }
820 
821 static HRESULT WINAPI drive_Invoke(IDrive *iface, DISPID dispIdMember,
822                                       REFIID riid, LCID lcid, WORD wFlags,
823                                       DISPPARAMS *pDispParams, VARIANT *pVarResult,
824                                       EXCEPINFO *pExcepInfo, UINT *puArgErr)
825 {
826     struct drive *This = impl_from_IDrive(iface);
827     ITypeInfo *typeinfo;
828     HRESULT hr;
829 
830     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
831            lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
832 
833     hr = get_typeinfo(IDrive_tid, &typeinfo);
834     if(SUCCEEDED(hr))
835     {
836         hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags,
837                 pDispParams, pVarResult, pExcepInfo, puArgErr);
838         ITypeInfo_Release(typeinfo);
839     }
840 
841     return hr;
842 }
843 
844 static HRESULT WINAPI drive_get_Path(IDrive *iface, BSTR *path)
845 {
846     struct drive *This = impl_from_IDrive(iface);
847     FIXME("(%p)->(%p): stub\n", This, path);
848     return E_NOTIMPL;
849 }
850 
851 static HRESULT WINAPI drive_get_DriveLetter(IDrive *iface, BSTR *letter)
852 {
853     struct drive *This = impl_from_IDrive(iface);
854 
855     TRACE("(%p)->(%p)\n", This, letter);
856 
857     if (!letter)
858         return E_POINTER;
859 
860     *letter = SysAllocStringLen(This->root, 1);
861     if (!*letter)
862         return E_OUTOFMEMORY;
863 
864     return S_OK;
865 }
866 
867 static HRESULT WINAPI drive_get_ShareName(IDrive *iface, BSTR *share_name)
868 {
869     struct drive *This = impl_from_IDrive(iface);
870     FIXME("(%p)->(%p): stub\n", This, share_name);
871     return E_NOTIMPL;
872 }
873 
874 static HRESULT WINAPI drive_get_DriveType(IDrive *iface, DriveTypeConst *type)
875 {
876     struct drive *This = impl_from_IDrive(iface);
877 
878     TRACE("(%p)->(%p)\n", This, type);
879 
880     switch (GetDriveTypeW(This->root))
881     {
882     case DRIVE_REMOVABLE:
883         *type = Removable;
884         break;
885     case DRIVE_FIXED:
886         *type = Fixed;
887         break;
888     case DRIVE_REMOTE:
889         *type = Remote;
890         break;
891     case DRIVE_CDROM:
892         *type = CDRom;
893         break;
894     case DRIVE_RAMDISK:
895         *type = RamDisk;
896         break;
897     default:
898         *type = UnknownType;
899         break;
900     }
901 
902     return S_OK;
903 }
904 
905 static HRESULT WINAPI drive_get_RootFolder(IDrive *iface, IFolder **folder)
906 {
907     struct drive *This = impl_from_IDrive(iface);
908     FIXME("(%p)->(%p): stub\n", This, folder);
909     return E_NOTIMPL;
910 }
911 
912 static HRESULT variant_from_largeint(const ULARGE_INTEGER *src, VARIANT *v)
913 {
914     HRESULT hr = S_OK;
915 
916     if (src->u.HighPart || src->u.LowPart > INT_MAX)
917     {
918         V_VT(v) = VT_R8;
919         hr = VarR8FromUI8(src->QuadPart, &V_R8(v));
920     }
921     else
922     {
923         V_VT(v) = VT_I4;
924         V_I4(v) = src->u.LowPart;
925     }
926 
927     return hr;
928 }
929 
930 static HRESULT WINAPI drive_get_AvailableSpace(IDrive *iface, VARIANT *v)
931 {
932     struct drive *This = impl_from_IDrive(iface);
933     ULARGE_INTEGER avail;
934 
935     TRACE("(%p)->(%p)\n", This, v);
936 
937     if (!v)
938         return E_POINTER;
939 
940     if (!GetDiskFreeSpaceExW(This->root, &avail, NULL, NULL))
941         return E_FAIL;
942 
943     return variant_from_largeint(&avail, v);
944 }
945 
946 static HRESULT WINAPI drive_get_FreeSpace(IDrive *iface, VARIANT *v)
947 {
948     struct drive *This = impl_from_IDrive(iface);
949     ULARGE_INTEGER freespace;
950 
951     TRACE("(%p)->(%p)\n", This, v);
952 
953     if (!v)
954         return E_POINTER;
955 
956     if (!GetDiskFreeSpaceExW(This->root, &freespace, NULL, NULL))
957         return E_FAIL;
958 
959     return variant_from_largeint(&freespace, v);
960 }
961 
962 static HRESULT WINAPI drive_get_TotalSize(IDrive *iface, VARIANT *v)
963 {
964     struct drive *This = impl_from_IDrive(iface);
965     ULARGE_INTEGER total;
966 
967     TRACE("(%p)->(%p)\n", This, v);
968 
969     if (!v)
970         return E_POINTER;
971 
972     if (!GetDiskFreeSpaceExW(This->root, NULL, &total, NULL))
973         return E_FAIL;
974 
975     return variant_from_largeint(&total, v);
976 }
977 
978 static HRESULT WINAPI drive_get_VolumeName(IDrive *iface, BSTR *name)
979 {
980     struct drive *This = impl_from_IDrive(iface);
981     WCHAR nameW[MAX_PATH+1];
982     BOOL ret;
983 
984     TRACE("(%p)->(%p)\n", This, name);
985 
986     if (!name)
987         return E_POINTER;
988 
989     *name = NULL;
990     ret = GetVolumeInformationW(This->root, nameW, sizeof(nameW)/sizeof(WCHAR), NULL, NULL, NULL, NULL, 0);
991     if (ret)
992         *name = SysAllocString(nameW);
993     return ret ? S_OK : E_FAIL;
994 }
995 
996 static HRESULT WINAPI drive_put_VolumeName(IDrive *iface, BSTR name)
997 {
998     struct drive *This = impl_from_IDrive(iface);
999     FIXME("(%p)->(%s): stub\n", This, debugstr_w(name));
1000     return E_NOTIMPL;
1001 }
1002 
1003 static HRESULT WINAPI drive_get_FileSystem(IDrive *iface, BSTR *fs)
1004 {
1005     struct drive *This = impl_from_IDrive(iface);
1006     WCHAR nameW[MAX_PATH+1];
1007     BOOL ret;
1008 
1009     TRACE("(%p)->(%p)\n", This, fs);
1010 
1011     if (!fs)
1012         return E_POINTER;
1013 
1014     *fs = NULL;
1015     ret = GetVolumeInformationW(This->root, NULL, 0, NULL, NULL, NULL, nameW, sizeof(nameW)/sizeof(WCHAR));
1016     if (ret)
1017         *fs = SysAllocString(nameW);
1018     return ret ? S_OK : E_FAIL;
1019 }
1020 
1021 static HRESULT WINAPI drive_get_SerialNumber(IDrive *iface, LONG *serial)
1022 {
1023     struct drive *This = impl_from_IDrive(iface);
1024     BOOL ret;
1025 
1026     TRACE("(%p)->(%p)\n", This, serial);
1027 
1028     if (!serial)
1029         return E_POINTER;
1030 
1031     ret = GetVolumeInformationW(This->root, NULL, 0, (DWORD*)serial, NULL, NULL, NULL, 0);
1032     return ret ? S_OK : E_FAIL;
1033 }
1034 
1035 static HRESULT WINAPI drive_get_IsReady(IDrive *iface, VARIANT_BOOL *ready)
1036 {
1037     struct drive *This = impl_from_IDrive(iface);
1038     ULARGE_INTEGER freespace;
1039     BOOL ret;
1040 
1041     TRACE("(%p)->(%p)\n", This, ready);
1042 
1043     if (!ready)
1044         return E_POINTER;
1045 
1046     ret = GetDiskFreeSpaceExW(This->root, &freespace, NULL, NULL);
1047     *ready = ret ? VARIANT_TRUE : VARIANT_FALSE;
1048     return S_OK;
1049 }
1050 
1051 static const IDriveVtbl drivevtbl = {
1052     drive_QueryInterface,
1053     drive_AddRef,
1054     drive_Release,
1055     drive_GetTypeInfoCount,
1056     drive_GetTypeInfo,
1057     drive_GetIDsOfNames,
1058     drive_Invoke,
1059     drive_get_Path,
1060     drive_get_DriveLetter,
1061     drive_get_ShareName,
1062     drive_get_DriveType,
1063     drive_get_RootFolder,
1064     drive_get_AvailableSpace,
1065     drive_get_FreeSpace,
1066     drive_get_TotalSize,
1067     drive_get_VolumeName,
1068     drive_put_VolumeName,
1069     drive_get_FileSystem,
1070     drive_get_SerialNumber,
1071     drive_get_IsReady
1072 };
1073 
1074 static HRESULT create_drive(WCHAR letter, IDrive **drive)
1075 {
1076     struct drive *This;
1077 
1078     *drive = NULL;
1079 
1080     This = heap_alloc(sizeof(*This));
1081     if (!This) return E_OUTOFMEMORY;
1082 
1083     This->IDrive_iface.lpVtbl = &drivevtbl;
1084     This->ref = 1;
1085     This->root = SysAllocStringLen(NULL, 3);
1086     if (!This->root)
1087     {
1088         heap_free(This);
1089         return E_OUTOFMEMORY;
1090     }
1091     This->root[0] = letter;
1092     This->root[1] = ':';
1093     This->root[2] = '\\';
1094     This->root[3] = 0;
1095 
1096     init_classinfo(&CLSID_Drive, (IUnknown *)&This->IDrive_iface, &This->classinfo);
1097     *drive = &This->IDrive_iface;
1098     return S_OK;
1099 }
1100 
1101 static HRESULT WINAPI enumvariant_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **obj)
1102 {
1103     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1104 
1105     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1106 
1107     *obj = NULL;
1108 
1109     if (IsEqualIID( riid, &IID_IEnumVARIANT ) ||
1110         IsEqualIID( riid, &IID_IUnknown ))
1111     {
1112         *obj = iface;
1113         IEnumVARIANT_AddRef(iface);
1114     }
1115     else
1116         return E_NOINTERFACE;
1117 
1118     return S_OK;
1119 }
1120 
1121 static ULONG WINAPI enumvariant_AddRef(IEnumVARIANT *iface)
1122 {
1123     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1124     ULONG ref = InterlockedIncrement(&This->ref);
1125     TRACE("(%p)->(%d)\n", This, ref);
1126     return ref;
1127 }
1128 
1129 static ULONG WINAPI foldercoll_enumvariant_Release(IEnumVARIANT *iface)
1130 {
1131     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1132     ULONG ref = InterlockedDecrement(&This->ref);
1133 
1134     TRACE("(%p)->(%d)\n", This, ref);
1135 
1136     if (!ref)
1137     {
1138         IFolderCollection_Release(&This->data.u.foldercoll.coll->IFolderCollection_iface);
1139         FindClose(This->data.u.foldercoll.find);
1140         heap_free(This);
1141     }
1142 
1143     return ref;
1144 }
1145 
1146 static HANDLE start_enumeration(const WCHAR *path, WIN32_FIND_DATAW *data, BOOL file)
1147 {
1148     static const WCHAR allW[] = {'*',0};
1149     WCHAR pathW[MAX_PATH];
1150     int len;
1151     HANDLE handle;
1152 
1153     strcpyW(pathW, path);
1154     len = strlenW(pathW);
1155     if (len && pathW[len-1] != '\\')
1156         strcatW(pathW, bsW);
1157     strcatW(pathW, allW);
1158     handle = FindFirstFileW(pathW, data);
1159     if (handle == INVALID_HANDLE_VALUE) return 0;
1160 
1161     /* find first dir/file */
1162     while (1)
1163     {
1164         if (file ? is_file_data(data) : is_dir_data(data))
1165             break;
1166 
1167         if (!FindNextFileW(handle, data))
1168         {
1169             FindClose(handle);
1170             return 0;
1171         }
1172     }
1173     return handle;
1174 }
1175 
1176 static HRESULT WINAPI foldercoll_enumvariant_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *var, ULONG *fetched)
1177 {
1178     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1179     HANDLE handle = This->data.u.foldercoll.find;
1180     WIN32_FIND_DATAW data;
1181     ULONG count = 0;
1182 
1183     TRACE("(%p)->(%d %p %p)\n", This, celt, var, fetched);
1184 
1185     if (fetched)
1186         *fetched = 0;
1187 
1188     if (!celt) return S_OK;
1189 
1190     if (!handle)
1191     {
1192         handle = start_enumeration(This->data.u.foldercoll.coll->path, &data, FALSE);
1193         if (!handle) return S_FALSE;
1194 
1195         This->data.u.foldercoll.find = handle;
1196     }
1197     else
1198     {
1199         if (!FindNextFileW(handle, &data))
1200             return S_FALSE;
1201     }
1202 
1203     do
1204     {
1205         if (is_dir_data(&data))
1206         {
1207             IFolder *folder;
1208             HRESULT hr;
1209             BSTR str;
1210 
1211             str = get_full_path(This->data.u.foldercoll.coll->path, &data);
1212             hr = create_folder(str, &folder);
1213             SysFreeString(str);
1214             if (FAILED(hr)) return hr;
1215 
1216             V_VT(&var[count]) = VT_DISPATCH;
1217             V_DISPATCH(&var[count]) = (IDispatch*)folder;
1218             count++;
1219 
1220             if (count >= celt) break;
1221         }
1222     } while (FindNextFileW(handle, &data));
1223 
1224     if (fetched)
1225         *fetched = count;
1226 
1227     return (count < celt) ? S_FALSE : S_OK;
1228 }
1229 
1230 static HRESULT WINAPI foldercoll_enumvariant_Skip(IEnumVARIANT *iface, ULONG celt)
1231 {
1232     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1233     HANDLE handle = This->data.u.foldercoll.find;
1234     WIN32_FIND_DATAW data;
1235 
1236     TRACE("(%p)->(%d)\n", This, celt);
1237 
1238     if (!celt) return S_OK;
1239 
1240     if (!handle)
1241     {
1242         handle = start_enumeration(This->data.u.foldercoll.coll->path, &data, FALSE);
1243         if (!handle) return S_FALSE;
1244 
1245         This->data.u.foldercoll.find = handle;
1246     }
1247     else
1248     {
1249         if (!FindNextFileW(handle, &data))
1250             return S_FALSE;
1251     }
1252 
1253     do
1254     {
1255         if (is_dir_data(&data))
1256             --celt;
1257 
1258         if (!celt) break;
1259     } while (FindNextFileW(handle, &data));
1260 
1261     return celt ? S_FALSE : S_OK;
1262 }
1263 
1264 static HRESULT WINAPI foldercoll_enumvariant_Reset(IEnumVARIANT *iface)
1265 {
1266     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1267 
1268     TRACE("(%p)\n", This);
1269 
1270     FindClose(This->data.u.foldercoll.find);
1271     This->data.u.foldercoll.find = NULL;
1272 
1273     return S_OK;
1274 }
1275 
1276 static HRESULT WINAPI foldercoll_enumvariant_Clone(IEnumVARIANT *iface, IEnumVARIANT **pclone)
1277 {
1278     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1279     TRACE("(%p)->(%p)\n", This, pclone);
1280     return create_foldercoll_enum(This->data.u.foldercoll.coll, (IUnknown**)pclone);
1281 }
1282 
1283 static const IEnumVARIANTVtbl foldercollenumvariantvtbl = {
1284     enumvariant_QueryInterface,
1285     enumvariant_AddRef,
1286     foldercoll_enumvariant_Release,
1287     foldercoll_enumvariant_Next,
1288     foldercoll_enumvariant_Skip,
1289     foldercoll_enumvariant_Reset,
1290     foldercoll_enumvariant_Clone
1291 };
1292 
1293 static HRESULT create_foldercoll_enum(struct foldercollection *collection, IUnknown **newenum)
1294 {
1295     struct enumvariant *This;
1296 
1297     *newenum = NULL;
1298 
1299     This = heap_alloc(sizeof(*This));
1300     if (!This) return E_OUTOFMEMORY;
1301 
1302     This->IEnumVARIANT_iface.lpVtbl = &foldercollenumvariantvtbl;
1303     This->ref = 1;
1304     This->data.u.foldercoll.find = NULL;
1305     This->data.u.foldercoll.coll = collection;
1306     IFolderCollection_AddRef(&collection->IFolderCollection_iface);
1307 
1308     *newenum = (IUnknown*)&This->IEnumVARIANT_iface;
1309 
1310     return S_OK;
1311 }
1312 
1313 static ULONG WINAPI filecoll_enumvariant_Release(IEnumVARIANT *iface)
1314 {
1315     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1316     ULONG ref = InterlockedDecrement(&This->ref);
1317 
1318     TRACE("(%p)->(%d)\n", This, ref);
1319 
1320     if (!ref)
1321     {
1322         IFileCollection_Release(&This->data.u.filecoll.coll->IFileCollection_iface);
1323         FindClose(This->data.u.filecoll.find);
1324         heap_free(This);
1325     }
1326 
1327     return ref;
1328 }
1329 
1330 static HRESULT WINAPI filecoll_enumvariant_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *var, ULONG *fetched)
1331 {
1332     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1333     HANDLE handle = This->data.u.filecoll.find;
1334     WIN32_FIND_DATAW data;
1335     ULONG count = 0;
1336 
1337     TRACE("(%p)->(%d %p %p)\n", This, celt, var, fetched);
1338 
1339     if (fetched)
1340         *fetched = 0;
1341 
1342     if (!celt) return S_OK;
1343 
1344     if (!handle)
1345     {
1346         handle = start_enumeration(This->data.u.filecoll.coll->path, &data, TRUE);
1347         if (!handle) return S_FALSE;
1348         This->data.u.filecoll.find = handle;
1349     }
1350     else if (!FindNextFileW(handle, &data))
1351         return S_FALSE;
1352 
1353     do
1354     {
1355         if (is_file_data(&data))
1356         {
1357             IFile *file;
1358             HRESULT hr;
1359             BSTR str;
1360 
1361             str = get_full_path(This->data.u.filecoll.coll->path, &data);
1362             hr = create_file(str, &file);
1363             SysFreeString(str);
1364             if (FAILED(hr)) return hr;
1365 
1366             V_VT(&var[count]) = VT_DISPATCH;
1367             V_DISPATCH(&var[count]) = (IDispatch*)file;
1368             if (++count >= celt) break;
1369         }
1370     } while (FindNextFileW(handle, &data));
1371 
1372     if (fetched)
1373         *fetched = count;
1374 
1375     return (count < celt) ? S_FALSE : S_OK;
1376 }
1377 
1378 static HRESULT WINAPI filecoll_enumvariant_Skip(IEnumVARIANT *iface, ULONG celt)
1379 {
1380     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1381     HANDLE handle = This->data.u.filecoll.find;
1382     WIN32_FIND_DATAW data;
1383 
1384     TRACE("(%p)->(%d)\n", This, celt);
1385 
1386     if (!celt) return S_OK;
1387 
1388     if (!handle)
1389     {
1390         handle = start_enumeration(This->data.u.filecoll.coll->path, &data, TRUE);
1391         if (!handle) return S_FALSE;
1392         This->data.u.filecoll.find = handle;
1393     }
1394     else if (!FindNextFileW(handle, &data))
1395         return S_FALSE;
1396 
1397     do
1398     {
1399         if (is_file_data(&data))
1400             --celt;
1401     } while (celt && FindNextFileW(handle, &data));
1402 
1403     return celt ? S_FALSE : S_OK;
1404 }
1405 
1406 static HRESULT WINAPI filecoll_enumvariant_Reset(IEnumVARIANT *iface)
1407 {
1408     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1409 
1410     TRACE("(%p)\n", This);
1411 
1412     FindClose(This->data.u.filecoll.find);
1413     This->data.u.filecoll.find = NULL;
1414 
1415     return S_OK;
1416 }
1417 
1418 static HRESULT WINAPI filecoll_enumvariant_Clone(IEnumVARIANT *iface, IEnumVARIANT **pclone)
1419 {
1420     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1421     TRACE("(%p)->(%p)\n", This, pclone);
1422     return create_filecoll_enum(This->data.u.filecoll.coll, (IUnknown**)pclone);
1423 }
1424 
1425 static const IEnumVARIANTVtbl filecollenumvariantvtbl = {
1426     enumvariant_QueryInterface,
1427     enumvariant_AddRef,
1428     filecoll_enumvariant_Release,
1429     filecoll_enumvariant_Next,
1430     filecoll_enumvariant_Skip,
1431     filecoll_enumvariant_Reset,
1432     filecoll_enumvariant_Clone
1433 };
1434 
1435 static HRESULT create_filecoll_enum(struct filecollection *collection, IUnknown **newenum)
1436 {
1437     struct enumvariant *This;
1438 
1439     *newenum = NULL;
1440 
1441     This = heap_alloc(sizeof(*This));
1442     if (!This) return E_OUTOFMEMORY;
1443 
1444     This->IEnumVARIANT_iface.lpVtbl = &filecollenumvariantvtbl;
1445     This->ref = 1;
1446     This->data.u.filecoll.find = NULL;
1447     This->data.u.filecoll.coll = collection;
1448     IFileCollection_AddRef(&collection->IFileCollection_iface);
1449 
1450     *newenum = (IUnknown*)&This->IEnumVARIANT_iface;
1451 
1452     return S_OK;
1453 }
1454 
1455 static ULONG WINAPI drivecoll_enumvariant_Release(IEnumVARIANT *iface)
1456 {
1457     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1458     ULONG ref = InterlockedDecrement(&This->ref);
1459 
1460     TRACE("(%p)->(%d)\n", This, ref);
1461 
1462     if (!ref)
1463     {
1464         IDriveCollection_Release(&This->data.u.drivecoll.coll->IDriveCollection_iface);
1465         heap_free(This);
1466     }
1467 
1468     return ref;
1469 }
1470 
1471 static HRESULT find_next_drive(struct enumvariant *penum)
1472 {
1473     int i = penum->data.u.drivecoll.cur == -1 ? 0 : penum->data.u.drivecoll.cur + 1;
1474 
1475     for (; i < 32; i++)
1476         if (penum->data.u.drivecoll.coll->drives & (1 << i))
1477         {
1478             penum->data.u.drivecoll.cur = i;
1479             return S_OK;
1480         }
1481 
1482     return S_FALSE;
1483 }
1484 
1485 static HRESULT WINAPI drivecoll_enumvariant_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *var, ULONG *fetched)
1486 {
1487     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1488     ULONG count = 0;
1489 
1490     TRACE("(%p)->(%d %p %p)\n", This, celt, var, fetched);
1491 
1492     if (fetched)
1493         *fetched = 0;
1494 
1495     if (!celt) return S_OK;
1496 
1497     while (find_next_drive(This) == S_OK)
1498     {
1499         IDrive *drive;
1500         HRESULT hr;
1501 
1502         hr = create_drive('A' + This->data.u.drivecoll.cur, &drive);
1503         if (FAILED(hr)) return hr;
1504 
1505         V_VT(&var[count]) = VT_DISPATCH;
1506         V_DISPATCH(&var[count]) = (IDispatch*)drive;
1507 
1508         if (++count >= celt) break;
1509     }
1510 
1511     if (fetched)
1512         *fetched = count;
1513 
1514     return (count < celt) ? S_FALSE : S_OK;
1515 }
1516 
1517 static HRESULT WINAPI drivecoll_enumvariant_Skip(IEnumVARIANT *iface, ULONG celt)
1518 {
1519     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1520 
1521     TRACE("(%p)->(%d)\n", This, celt);
1522 
1523     if (!celt) return S_OK;
1524 
1525     while (celt && find_next_drive(This) == S_OK)
1526         celt--;
1527 
1528     return celt ? S_FALSE : S_OK;
1529 }
1530 
1531 static HRESULT WINAPI drivecoll_enumvariant_Reset(IEnumVARIANT *iface)
1532 {
1533     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1534 
1535     TRACE("(%p)\n", This);
1536 
1537     This->data.u.drivecoll.cur = -1;
1538     return S_OK;
1539 }
1540 
1541 static HRESULT WINAPI drivecoll_enumvariant_Clone(IEnumVARIANT *iface, IEnumVARIANT **pclone)
1542 {
1543     struct enumvariant *This = impl_from_IEnumVARIANT(iface);
1544     FIXME("(%p)->(%p): stub\n", This, pclone);
1545     return E_NOTIMPL;
1546 }
1547 
1548 static const IEnumVARIANTVtbl drivecollenumvariantvtbl = {
1549     enumvariant_QueryInterface,
1550     enumvariant_AddRef,
1551     drivecoll_enumvariant_Release,
1552     drivecoll_enumvariant_Next,
1553     drivecoll_enumvariant_Skip,
1554     drivecoll_enumvariant_Reset,
1555     drivecoll_enumvariant_Clone
1556 };
1557 
1558 static HRESULT create_drivecoll_enum(struct drivecollection *collection, IUnknown **newenum)
1559 {
1560     struct enumvariant *This;
1561 
1562     *newenum = NULL;
1563 
1564     This = heap_alloc(sizeof(*This));
1565     if (!This) return E_OUTOFMEMORY;
1566 
1567     This->IEnumVARIANT_iface.lpVtbl = &drivecollenumvariantvtbl;
1568     This->ref = 1;
1569     This->data.u.drivecoll.coll = collection;
1570     This->data.u.drivecoll.cur = -1;
1571     IDriveCollection_AddRef(&collection->IDriveCollection_iface);
1572 
1573     *newenum = (IUnknown*)&This->IEnumVARIANT_iface;
1574 
1575     return S_OK;
1576 }
1577 
1578 static HRESULT WINAPI foldercoll_QueryInterface(IFolderCollection *iface, REFIID riid, void **obj)
1579 {
1580     struct foldercollection *This = impl_from_IFolderCollection(iface);
1581 
1582     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1583 
1584     *obj = NULL;
1585 
1586     if (IsEqualIID( riid, &IID_IFolderCollection ) ||
1587         IsEqualIID( riid, &IID_IDispatch ) ||
1588         IsEqualIID( riid, &IID_IUnknown ))
1589     {
1590         *obj = &This->IFolderCollection_iface;
1591     }
1592     else if (IsEqualIID( riid, &IID_IProvideClassInfo ))
1593     {
1594         *obj = &This->classinfo.IProvideClassInfo_iface;
1595     }
1596     else
1597         return E_NOINTERFACE;
1598 
1599     IUnknown_AddRef((IUnknown*)*obj);
1600     return S_OK;
1601 }
1602 
1603 static ULONG WINAPI foldercoll_AddRef(IFolderCollection *iface)
1604 {
1605     struct foldercollection *This = impl_from_IFolderCollection(iface);
1606     ULONG ref = InterlockedIncrement(&This->ref);
1607     TRACE("(%p)->(%d)\n", This, ref);
1608     return ref;
1609 }
1610 
1611 static ULONG WINAPI foldercoll_Release(IFolderCollection *iface)
1612 {
1613     struct foldercollection *This = impl_from_IFolderCollection(iface);
1614     ULONG ref = InterlockedDecrement(&This->ref);
1615     TRACE("(%p)->(%d)\n", This, ref);
1616 
1617     if (!ref)
1618     {
1619         SysFreeString(This->path);
1620         heap_free(This);
1621     }
1622 
1623     return ref;
1624 }
1625 
1626 static HRESULT WINAPI foldercoll_GetTypeInfoCount(IFolderCollection *iface, UINT *pctinfo)
1627 {
1628     struct foldercollection *This = impl_from_IFolderCollection(iface);
1629     TRACE("(%p)->(%p)\n", This, pctinfo);
1630     *pctinfo = 1;
1631     return S_OK;
1632 }
1633 
1634 static HRESULT WINAPI foldercoll_GetTypeInfo(IFolderCollection *iface, UINT iTInfo,
1635                                         LCID lcid, ITypeInfo **ppTInfo)
1636 {
1637     struct foldercollection *This = impl_from_IFolderCollection(iface);
1638     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1639     return get_typeinfo(IFolderCollection_tid, ppTInfo);
1640 }
1641 
1642 static HRESULT WINAPI foldercoll_GetIDsOfNames(IFolderCollection *iface, REFIID riid,
1643                                         LPOLESTR *rgszNames, UINT cNames,
1644                                         LCID lcid, DISPID *rgDispId)
1645 {
1646     struct foldercollection *This = impl_from_IFolderCollection(iface);
1647     ITypeInfo *typeinfo;
1648     HRESULT hr;
1649 
1650     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
1651 
1652     hr = get_typeinfo(IFolderCollection_tid, &typeinfo);
1653     if(SUCCEEDED(hr))
1654     {
1655         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
1656         ITypeInfo_Release(typeinfo);
1657     }
1658 
1659     return hr;
1660 }
1661 
1662 static HRESULT WINAPI foldercoll_Invoke(IFolderCollection *iface, DISPID dispIdMember,
1663                                       REFIID riid, LCID lcid, WORD wFlags,
1664                                       DISPPARAMS *pDispParams, VARIANT *pVarResult,
1665                                       EXCEPINFO *pExcepInfo, UINT *puArgErr)
1666 {
1667     struct foldercollection *This = impl_from_IFolderCollection(iface);
1668     ITypeInfo *typeinfo;
1669     HRESULT hr;
1670 
1671     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1672            lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1673 
1674     hr = get_typeinfo(IFolderCollection_tid, &typeinfo);
1675     if(SUCCEEDED(hr))
1676     {
1677         hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags,
1678                 pDispParams, pVarResult, pExcepInfo, puArgErr);
1679         ITypeInfo_Release(typeinfo);
1680     }
1681 
1682     return hr;
1683 }
1684 
1685 static HRESULT WINAPI foldercoll_Add(IFolderCollection *iface, BSTR name, IFolder **folder)
1686 {
1687     struct foldercollection *This = impl_from_IFolderCollection(iface);
1688     FIXME("(%p)->(%s %p): stub\n", This, debugstr_w(name), folder);
1689     return E_NOTIMPL;
1690 }
1691 
1692 static HRESULT WINAPI foldercoll_get_Item(IFolderCollection *iface, VARIANT key, IFolder **folder)
1693 {
1694     struct foldercollection *This = impl_from_IFolderCollection(iface);
1695     FIXME("(%p)->(%p): stub\n", This, folder);
1696     return E_NOTIMPL;
1697 }
1698 
1699 static HRESULT WINAPI foldercoll_get__NewEnum(IFolderCollection *iface, IUnknown **newenum)
1700 {
1701     struct foldercollection *This = impl_from_IFolderCollection(iface);
1702 
1703     TRACE("(%p)->(%p)\n", This, newenum);
1704 
1705     if(!newenum)
1706         return E_POINTER;
1707 
1708     return create_foldercoll_enum(This, newenum);
1709 }
1710 
1711 static HRESULT WINAPI foldercoll_get_Count(IFolderCollection *iface, LONG *count)
1712 {
1713     struct foldercollection *This = impl_from_IFolderCollection(iface);
1714     static const WCHAR allW[] = {'\\','*',0};
1715     WIN32_FIND_DATAW data;
1716     WCHAR pathW[MAX_PATH];
1717     HANDLE handle;
1718 
1719     TRACE("(%p)->(%p)\n", This, count);
1720 
1721     if(!count)
1722         return E_POINTER;
1723 
1724     *count = 0;
1725 
1726     strcpyW(pathW, This->path);
1727     strcatW(pathW, allW);
1728     handle = FindFirstFileW(pathW, &data);
1729     if (handle == INVALID_HANDLE_VALUE)
1730         return HRESULT_FROM_WIN32(GetLastError());
1731 
1732     do
1733     {
1734         if (is_dir_data(&data))
1735             *count += 1;
1736     } while (FindNextFileW(handle, &data));
1737     FindClose(handle);
1738 
1739     return S_OK;
1740 }
1741 
1742 static const IFolderCollectionVtbl foldercollvtbl = {
1743     foldercoll_QueryInterface,
1744     foldercoll_AddRef,
1745     foldercoll_Release,
1746     foldercoll_GetTypeInfoCount,
1747     foldercoll_GetTypeInfo,
1748     foldercoll_GetIDsOfNames,
1749     foldercoll_Invoke,
1750     foldercoll_Add,
1751     foldercoll_get_Item,
1752     foldercoll_get__NewEnum,
1753     foldercoll_get_Count
1754 };
1755 
1756 static HRESULT create_foldercoll(BSTR path, IFolderCollection **folders)
1757 {
1758     struct foldercollection *This;
1759 
1760     *folders = NULL;
1761 
1762     This = heap_alloc(sizeof(struct foldercollection));
1763     if (!This) return E_OUTOFMEMORY;
1764 
1765     This->IFolderCollection_iface.lpVtbl = &foldercollvtbl;
1766     This->ref = 1;
1767     This->path = SysAllocString(path);
1768     if (!This->path)
1769     {
1770         heap_free(This);
1771         return E_OUTOFMEMORY;
1772     }
1773 
1774     init_classinfo(&CLSID_Folders, (IUnknown *)&This->IFolderCollection_iface, &This->classinfo);
1775     *folders = &This->IFolderCollection_iface;
1776 
1777     return S_OK;
1778 }
1779 
1780 static HRESULT WINAPI filecoll_QueryInterface(IFileCollection *iface, REFIID riid, void **obj)
1781 {
1782     struct filecollection *This = impl_from_IFileCollection(iface);
1783 
1784     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1785 
1786     *obj = NULL;
1787 
1788     if (IsEqualIID( riid, &IID_IFileCollection ) ||
1789         IsEqualIID( riid, &IID_IDispatch ) ||
1790         IsEqualIID( riid, &IID_IUnknown ))
1791     {
1792         *obj = &This->IFileCollection_iface;
1793     }
1794     else if (IsEqualIID( riid, &IID_IProvideClassInfo ))
1795     {
1796         *obj = &This->classinfo.IProvideClassInfo_iface;
1797     }
1798     else
1799         return E_NOINTERFACE;
1800 
1801     IUnknown_AddRef((IUnknown*)*obj);
1802     return S_OK;
1803 }
1804 
1805 static ULONG WINAPI filecoll_AddRef(IFileCollection *iface)
1806 {
1807     struct filecollection *This = impl_from_IFileCollection(iface);
1808     ULONG ref = InterlockedIncrement(&This->ref);
1809     TRACE("(%p)->(%d)\n", This, ref);
1810     return ref;
1811 }
1812 
1813 static ULONG WINAPI filecoll_Release(IFileCollection *iface)
1814 {
1815     struct filecollection *This = impl_from_IFileCollection(iface);
1816     ULONG ref = InterlockedDecrement(&This->ref);
1817     TRACE("(%p)->(%d)\n", This, ref);
1818 
1819     if (!ref)
1820     {
1821         SysFreeString(This->path);
1822         heap_free(This);
1823     }
1824 
1825     return ref;
1826 }
1827 
1828 static HRESULT WINAPI filecoll_GetTypeInfoCount(IFileCollection *iface, UINT *pctinfo)
1829 {
1830     struct filecollection *This = impl_from_IFileCollection(iface);
1831     TRACE("(%p)->(%p)\n", This, pctinfo);
1832     *pctinfo = 1;
1833     return S_OK;
1834 }
1835 
1836 static HRESULT WINAPI filecoll_GetTypeInfo(IFileCollection *iface, UINT iTInfo,
1837                                         LCID lcid, ITypeInfo **ppTInfo)
1838 {
1839     struct filecollection *This = impl_from_IFileCollection(iface);
1840     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1841     return get_typeinfo(IFileCollection_tid, ppTInfo);
1842 }
1843 
1844 static HRESULT WINAPI filecoll_GetIDsOfNames(IFileCollection *iface, REFIID riid,
1845                                         LPOLESTR *rgszNames, UINT cNames,
1846                                         LCID lcid, DISPID *rgDispId)
1847 {
1848     struct filecollection *This = impl_from_IFileCollection(iface);
1849     ITypeInfo *typeinfo;
1850     HRESULT hr;
1851 
1852     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
1853 
1854     hr = get_typeinfo(IFileCollection_tid, &typeinfo);
1855     if(SUCCEEDED(hr))
1856     {
1857         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
1858         ITypeInfo_Release(typeinfo);
1859     }
1860 
1861     return hr;
1862 }
1863 
1864 static HRESULT WINAPI filecoll_Invoke(IFileCollection *iface, DISPID dispIdMember,
1865                                       REFIID riid, LCID lcid, WORD wFlags,
1866                                       DISPPARAMS *pDispParams, VARIANT *pVarResult,
1867                                       EXCEPINFO *pExcepInfo, UINT *puArgErr)
1868 {
1869     struct filecollection *This = impl_from_IFileCollection(iface);
1870     ITypeInfo *typeinfo;
1871     HRESULT hr;
1872 
1873     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1874            lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1875 
1876     hr = get_typeinfo(IFileCollection_tid, &typeinfo);
1877     if(SUCCEEDED(hr))
1878     {
1879         hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags,
1880                 pDispParams, pVarResult, pExcepInfo, puArgErr);
1881         ITypeInfo_Release(typeinfo);
1882     }
1883 
1884     return hr;
1885 }
1886 
1887 static HRESULT WINAPI filecoll_get_Item(IFileCollection *iface, VARIANT Key, IFile **file)
1888 {
1889     struct filecollection *This = impl_from_IFileCollection(iface);
1890     FIXME("(%p)->(%p)\n", This, file);
1891     return E_NOTIMPL;
1892 }
1893 
1894 static HRESULT WINAPI filecoll_get__NewEnum(IFileCollection *iface, IUnknown **ppenum)
1895 {
1896     struct filecollection *This = impl_from_IFileCollection(iface);
1897 
1898     TRACE("(%p)->(%p)\n", This, ppenum);
1899 
1900     if(!ppenum)
1901         return E_POINTER;
1902 
1903     return create_filecoll_enum(This, ppenum);
1904 }
1905 
1906 static HRESULT WINAPI filecoll_get_Count(IFileCollection *iface, LONG *count)
1907 {
1908     struct filecollection *This = impl_from_IFileCollection(iface);
1909     static const WCHAR allW[] = {'\\','*',0};
1910     WIN32_FIND_DATAW data;
1911     WCHAR pathW[MAX_PATH];
1912     HANDLE handle;
1913 
1914     TRACE("(%p)->(%p)\n", This, count);
1915 
1916     if(!count)
1917         return E_POINTER;
1918 
1919     *count = 0;
1920 
1921     strcpyW(pathW, This->path);
1922     strcatW(pathW, allW);
1923     handle = FindFirstFileW(pathW, &data);
1924     if (handle == INVALID_HANDLE_VALUE)
1925         return HRESULT_FROM_WIN32(GetLastError());
1926 
1927     do
1928     {
1929         if (is_file_data(&data))
1930             *count += 1;
1931     } while (FindNextFileW(handle, &data));
1932     FindClose(handle);
1933 
1934     return S_OK;
1935 }
1936 
1937 static const IFileCollectionVtbl filecollectionvtbl = {
1938     filecoll_QueryInterface,
1939     filecoll_AddRef,
1940     filecoll_Release,
1941     filecoll_GetTypeInfoCount,
1942     filecoll_GetTypeInfo,
1943     filecoll_GetIDsOfNames,
1944     filecoll_Invoke,
1945     filecoll_get_Item,
1946     filecoll_get__NewEnum,
1947     filecoll_get_Count
1948 };
1949 
1950 static HRESULT create_filecoll(BSTR path, IFileCollection **files)
1951 {
1952     struct filecollection *This;
1953 
1954     *files = NULL;
1955 
1956     This = heap_alloc(sizeof(*This));
1957     if (!This) return E_OUTOFMEMORY;
1958 
1959     This->IFileCollection_iface.lpVtbl = &filecollectionvtbl;
1960     This->ref = 1;
1961     This->path = SysAllocString(path);
1962     if (!This->path)
1963     {
1964         heap_free(This);
1965         return E_OUTOFMEMORY;
1966     }
1967 
1968     init_classinfo(&CLSID_Files, (IUnknown *)&This->IFileCollection_iface, &This->classinfo);
1969     *files = &This->IFileCollection_iface;
1970     return S_OK;
1971 }
1972 
1973 static HRESULT WINAPI drivecoll_QueryInterface(IDriveCollection *iface, REFIID riid, void **obj)
1974 {
1975     struct drivecollection *This = impl_from_IDriveCollection(iface);
1976 
1977     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1978 
1979     *obj = NULL;
1980 
1981     if (IsEqualIID( riid, &IID_IDriveCollection ) ||
1982         IsEqualIID( riid, &IID_IDispatch ) ||
1983         IsEqualIID( riid, &IID_IUnknown ))
1984     {
1985         *obj = &This->IDriveCollection_iface;
1986     }
1987     else if (IsEqualIID( riid, &IID_IProvideClassInfo ))
1988     {
1989         *obj = &This->classinfo.IProvideClassInfo_iface;
1990     }
1991     else
1992         return E_NOINTERFACE;
1993 
1994     IUnknown_AddRef((IUnknown*)*obj);
1995     return S_OK;
1996 }
1997 
1998 static ULONG WINAPI drivecoll_AddRef(IDriveCollection *iface)
1999 {
2000     struct drivecollection *This = impl_from_IDriveCollection(iface);
2001     ULONG ref = InterlockedIncrement(&This->ref);
2002     TRACE("(%p)->(%d)\n", This, ref);
2003     return ref;
2004 }
2005 
2006 static ULONG WINAPI drivecoll_Release(IDriveCollection *iface)
2007 {
2008     struct drivecollection *This = impl_from_IDriveCollection(iface);
2009     ULONG ref = InterlockedDecrement(&This->ref);
2010     TRACE("(%p)->(%d)\n", This, ref);
2011 
2012     if (!ref)
2013         heap_free(This);
2014 
2015     return ref;
2016 }
2017 
2018 static HRESULT WINAPI drivecoll_GetTypeInfoCount(IDriveCollection *iface, UINT *pctinfo)
2019 {
2020     struct drivecollection *This = impl_from_IDriveCollection(iface);
2021     TRACE("(%p)->(%p)\n", This, pctinfo);
2022     *pctinfo = 1;
2023     return S_OK;
2024 }
2025 
2026 static HRESULT WINAPI drivecoll_GetTypeInfo(IDriveCollection *iface, UINT iTInfo,
2027                                         LCID lcid, ITypeInfo **ppTInfo)
2028 {
2029     struct drivecollection *This = impl_from_IDriveCollection(iface);
2030     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
2031     return get_typeinfo(IDriveCollection_tid, ppTInfo);
2032 }
2033 
2034 static HRESULT WINAPI drivecoll_GetIDsOfNames(IDriveCollection *iface, REFIID riid,
2035                                         LPOLESTR *rgszNames, UINT cNames,
2036                                         LCID lcid, DISPID *rgDispId)
2037 {
2038     struct drivecollection *This = impl_from_IDriveCollection(iface);
2039     ITypeInfo *typeinfo;
2040     HRESULT hr;
2041 
2042     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
2043 
2044     hr = get_typeinfo(IDriveCollection_tid, &typeinfo);
2045     if(SUCCEEDED(hr))
2046     {
2047         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
2048         ITypeInfo_Release(typeinfo);
2049     }
2050 
2051     return hr;
2052 }
2053 
2054 static HRESULT WINAPI drivecoll_Invoke(IDriveCollection *iface, DISPID dispIdMember,
2055                                       REFIID riid, LCID lcid, WORD wFlags,
2056                                       DISPPARAMS *pDispParams, VARIANT *pVarResult,
2057                                       EXCEPINFO *pExcepInfo, UINT *puArgErr)
2058 {
2059     struct drivecollection *This = impl_from_IDriveCollection(iface);
2060     ITypeInfo *typeinfo;
2061     HRESULT hr;
2062 
2063     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
2064            lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
2065 
2066     hr = get_typeinfo(IDriveCollection_tid, &typeinfo);
2067     if(SUCCEEDED(hr))
2068     {
2069         hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags,
2070                 pDispParams, pVarResult, pExcepInfo, puArgErr);
2071         ITypeInfo_Release(typeinfo);
2072     }
2073 
2074     return hr;
2075 }
2076 
2077 static HRESULT WINAPI drivecoll_get_Item(IDriveCollection *iface, VARIANT key, IDrive **drive)
2078 {
2079     struct drivecollection *This = impl_from_IDriveCollection(iface);
2080     FIXME("(%p)->(%p): stub\n", This, drive);
2081     return E_NOTIMPL;
2082 }
2083 
2084 static HRESULT WINAPI drivecoll_get__NewEnum(IDriveCollection *iface, IUnknown **ppenum)
2085 {
2086     struct drivecollection *This = impl_from_IDriveCollection(iface);
2087 
2088     TRACE("(%p)->(%p)\n", This, ppenum);
2089 
2090     if(!ppenum)
2091         return E_POINTER;
2092 
2093     return create_drivecoll_enum(This, ppenum);
2094 }
2095 
2096 static HRESULT WINAPI drivecoll_get_Count(IDriveCollection *iface, LONG *count)
2097 {
2098     struct drivecollection *This = impl_from_IDriveCollection(iface);
2099 
2100     TRACE("(%p)->(%p)\n", This, count);
2101 
2102     if (!count) return E_POINTER;
2103 
2104     *count = This->count;
2105     return S_OK;
2106 }
2107 
2108 static const IDriveCollectionVtbl drivecollectionvtbl = {
2109     drivecoll_QueryInterface,
2110     drivecoll_AddRef,
2111     drivecoll_Release,
2112     drivecoll_GetTypeInfoCount,
2113     drivecoll_GetTypeInfo,
2114     drivecoll_GetIDsOfNames,
2115     drivecoll_Invoke,
2116     drivecoll_get_Item,
2117     drivecoll_get__NewEnum,
2118     drivecoll_get_Count
2119 };
2120 
2121 static HRESULT create_drivecoll(IDriveCollection **drives)
2122 {
2123     struct drivecollection *This;
2124     DWORD mask;
2125 
2126     *drives = NULL;
2127 
2128     This = heap_alloc(sizeof(*This));
2129     if (!This) return E_OUTOFMEMORY;
2130 
2131     This->IDriveCollection_iface.lpVtbl = &drivecollectionvtbl;
2132     This->ref = 1;
2133     This->drives = mask = GetLogicalDrives();
2134     /* count set bits */
2135     for (This->count = 0; mask; This->count++)
2136         mask &= mask - 1;
2137 
2138     init_classinfo(&CLSID_Drives, (IUnknown *)&This->IDriveCollection_iface, &This->classinfo);
2139     *drives = &This->IDriveCollection_iface;
2140     return S_OK;
2141 }
2142 
2143 static HRESULT WINAPI folder_QueryInterface(IFolder *iface, REFIID riid, void **obj)
2144 {
2145     struct folder *This = impl_from_IFolder(iface);
2146 
2147     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
2148 
2149     *obj = NULL;
2150 
2151     if (IsEqualIID( riid, &IID_IFolder ) ||
2152         IsEqualIID( riid, &IID_IDispatch ) ||
2153         IsEqualIID( riid, &IID_IUnknown))
2154     {
2155         *obj = &This->IFolder_iface;
2156     }
2157     else if (IsEqualIID( riid, &IID_IProvideClassInfo ))
2158     {
2159         *obj = &This->classinfo.IProvideClassInfo_iface;
2160     }
2161     else
2162         return E_NOINTERFACE;
2163 
2164     IUnknown_AddRef((IUnknown*)*obj);
2165     return S_OK;
2166 }
2167 
2168 static ULONG WINAPI folder_AddRef(IFolder *iface)
2169 {
2170     struct folder *This = impl_from_IFolder(iface);
2171     ULONG ref = InterlockedIncrement(&This->ref);
2172     TRACE("(%p)->(%d)\n", This, ref);
2173     return ref;
2174 }
2175 
2176 static ULONG WINAPI folder_Release(IFolder *iface)
2177 {
2178     struct folder *This = impl_from_IFolder(iface);
2179     ULONG ref = InterlockedDecrement(&This->ref);
2180     TRACE("(%p)->(%d)\n", This, ref);
2181 
2182     if (!ref)
2183     {
2184         SysFreeString(This->path);
2185         heap_free(This);
2186     }
2187 
2188     return ref;
2189 }
2190 
2191 static HRESULT WINAPI folder_GetTypeInfoCount(IFolder *iface, UINT *pctinfo)
2192 {
2193     struct folder *This = impl_from_IFolder(iface);
2194     TRACE("(%p)->(%p)\n", This, pctinfo);
2195     *pctinfo = 1;
2196     return S_OK;
2197 }
2198 
2199 static HRESULT WINAPI folder_GetTypeInfo(IFolder *iface, UINT iTInfo,
2200                                         LCID lcid, ITypeInfo **ppTInfo)
2201 {
2202     struct folder *This = impl_from_IFolder(iface);
2203     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
2204     return get_typeinfo(IFolder_tid, ppTInfo);
2205 }
2206 
2207 static HRESULT WINAPI folder_GetIDsOfNames(IFolder *iface, REFIID riid,
2208                                         LPOLESTR *rgszNames, UINT cNames,
2209                                         LCID lcid, DISPID *rgDispId)
2210 {
2211     struct folder *This = impl_from_IFolder(iface);
2212     ITypeInfo *typeinfo;
2213     HRESULT hr;
2214 
2215     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
2216 
2217     hr = get_typeinfo(IFolder_tid, &typeinfo);
2218     if(SUCCEEDED(hr))
2219     {
2220         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
2221         ITypeInfo_Release(typeinfo);
2222     }
2223 
2224     return hr;
2225 }
2226 
2227 static HRESULT WINAPI folder_Invoke(IFolder *iface, DISPID dispIdMember,
2228                                       REFIID riid, LCID lcid, WORD wFlags,
2229                                       DISPPARAMS *pDispParams, VARIANT *pVarResult,
2230                                       EXCEPINFO *pExcepInfo, UINT *puArgErr)
2231 {
2232     struct folder *This = impl_from_IFolder(iface);
2233     ITypeInfo *typeinfo;
2234     HRESULT hr;
2235 
2236     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
2237            lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
2238 
2239     hr = get_typeinfo(IFolder_tid, &typeinfo);
2240     if(SUCCEEDED(hr))
2241     {
2242         hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags,
2243                 pDispParams, pVarResult, pExcepInfo, puArgErr);
2244         ITypeInfo_Release(typeinfo);
2245     }
2246 
2247     return hr;
2248 }
2249 
2250 static HRESULT WINAPI folder_get_Path(IFolder *iface, BSTR *path)
2251 {
2252     struct folder *This = impl_from_IFolder(iface);
2253 
2254     TRACE("(%p)->(%p)\n", This, path);
2255 
2256     if(!path)
2257         return E_POINTER;
2258 
2259     *path = SysAllocString(This->path);
2260     return *path ? S_OK : E_OUTOFMEMORY;
2261 }
2262 
2263 static HRESULT WINAPI folder_get_Name(IFolder *iface, BSTR *name)
2264 {
2265     struct folder *This = impl_from_IFolder(iface);
2266     WCHAR *ptr;
2267 
2268     TRACE("(%p)->(%p)\n", This, name);
2269 
2270     if(!name)
2271         return E_POINTER;
2272 
2273     *name = NULL;
2274 
2275     ptr = strrchrW(This->path, '\\');
2276     if (ptr)
2277     {
2278         *name = SysAllocString(ptr+1);
2279         TRACE("%s\n", debugstr_w(*name));
2280         if (!*name) return E_OUTOFMEMORY;
2281     }
2282     else
2283         return E_FAIL;
2284 
2285     return S_OK;
2286 }
2287 
2288 static HRESULT WINAPI folder_put_Name(IFolder *iface, BSTR name)
2289 {
2290     struct folder *This = impl_from_IFolder(iface);
2291     FIXME("(%p)->(%s): stub\n", This, debugstr_w(name));
2292     return E_NOTIMPL;
2293 }
2294 
2295 static HRESULT WINAPI folder_get_ShortPath(IFolder *iface, BSTR *path)
2296 {
2297     struct folder *This = impl_from_IFolder(iface);
2298     FIXME("(%p)->(%p): stub\n", This, path);
2299     return E_NOTIMPL;
2300 }
2301 
2302 static HRESULT WINAPI folder_get_ShortName(IFolder *iface, BSTR *name)
2303 {
2304     struct folder *This = impl_from_IFolder(iface);
2305     FIXME("(%p)->(%p): stub\n", This, name);
2306     return E_NOTIMPL;
2307 }
2308 
2309 static HRESULT WINAPI folder_get_Drive(IFolder *iface, IDrive **drive)
2310 {
2311     struct folder *This = impl_from_IFolder(iface);
2312     FIXME("(%p)->(%p): stub\n", This, drive);
2313     return E_NOTIMPL;
2314 }
2315 
2316 static HRESULT WINAPI folder_get_ParentFolder(IFolder *iface, IFolder **parent)
2317 {
2318     struct folder *This = impl_from_IFolder(iface);
2319     FIXME("(%p)->(%p): stub\n", This, parent);
2320     return E_NOTIMPL;
2321 }
2322 
2323 static HRESULT WINAPI folder_get_Attributes(IFolder *iface, FileAttribute *attr)
2324 {
2325     struct folder *This = impl_from_IFolder(iface);
2326     FIXME("(%p)->(%p): stub\n", This, attr);
2327     return E_NOTIMPL;
2328 }
2329 
2330 static HRESULT WINAPI folder_put_Attributes(IFolder *iface, FileAttribute attr)
2331 {
2332     struct folder *This = impl_from_IFolder(iface);
2333     FIXME("(%p)->(0x%x): stub\n", This, attr);
2334     return E_NOTIMPL;
2335 }
2336 
2337 static HRESULT WINAPI folder_get_DateCreated(IFolder *iface, DATE *date)
2338 {
2339     struct folder *This = impl_from_IFolder(iface);
2340     FIXME("(%p)->(%p): stub\n", This, date);
2341     return E_NOTIMPL;
2342 }
2343 
2344 static HRESULT WINAPI folder_get_DateLastModified(IFolder *iface, DATE *date)
2345 {
2346     struct folder *This = impl_from_IFolder(iface);
2347     FIXME("(%p)->(%p): stub\n", This, date);
2348     return E_NOTIMPL;
2349 }
2350 
2351 static HRESULT WINAPI folder_get_DateLastAccessed(IFolder *iface, DATE *date)
2352 {
2353     struct folder *This = impl_from_IFolder(iface);
2354     FIXME("(%p)->(%p): stub\n", This, date);
2355     return E_NOTIMPL;
2356 }
2357 
2358 static HRESULT WINAPI folder_get_Type(IFolder *iface, BSTR *type)
2359 {
2360     struct folder *This = impl_from_IFolder(iface);
2361     FIXME("(%p)->(%p): stub\n", This, type);
2362     return E_NOTIMPL;
2363 }
2364 
2365 static HRESULT WINAPI folder_Delete(IFolder *iface, VARIANT_BOOL force)
2366 {
2367     struct folder *This = impl_from_IFolder(iface);
2368     FIXME("(%p)->(%x): stub\n", This, force);
2369     return E_NOTIMPL;
2370 }
2371 
2372 static HRESULT WINAPI folder_Copy(IFolder *iface, BSTR dest, VARIANT_BOOL overwrite)
2373 {
2374     struct folder *This = impl_from_IFolder(iface);
2375     FIXME("(%p)->(%s %x): stub\n", This, debugstr_w(dest), overwrite);
2376     return E_NOTIMPL;
2377 }
2378 
2379 static HRESULT WINAPI folder_Move(IFolder *iface, BSTR dest)
2380 {
2381     struct folder *This = impl_from_IFolder(iface);
2382     FIXME("(%p)->(%s): stub\n", This, debugstr_w(dest));
2383     return E_NOTIMPL;
2384 }
2385 
2386 static HRESULT WINAPI folder_get_IsRootFolder(IFolder *iface, VARIANT_BOOL *isroot)
2387 {
2388     struct folder *This = impl_from_IFolder(iface);
2389     FIXME("(%p)->(%p): stub\n", This, isroot);
2390     return E_NOTIMPL;
2391 }
2392 
2393 static HRESULT WINAPI folder_get_Size(IFolder *iface, VARIANT *size)
2394 {
2395     struct folder *This = impl_from_IFolder(iface);
2396     FIXME("(%p)->(%p): stub\n", This, size);
2397     return E_NOTIMPL;
2398 }
2399 
2400 static HRESULT WINAPI folder_get_SubFolders(IFolder *iface, IFolderCollection **folders)
2401 {
2402     struct folder *This = impl_from_IFolder(iface);
2403 
2404     TRACE("(%p)->(%p)\n", This, folders);
2405 
2406     if(!folders)
2407         return E_POINTER;
2408 
2409     return create_foldercoll(This->path, folders);
2410 }
2411 
2412 static HRESULT WINAPI folder_get_Files(IFolder *iface, IFileCollection **files)
2413 {
2414     struct folder *This = impl_from_IFolder(iface);
2415 
2416     TRACE("(%p)->(%p)\n", This, files);
2417 
2418     if(!files)
2419         return E_POINTER;
2420 
2421     return create_filecoll(This->path, files);
2422 }
2423 
2424 static HRESULT WINAPI folder_CreateTextFile(IFolder *iface, BSTR filename, VARIANT_BOOL overwrite,
2425     VARIANT_BOOL unicode, ITextStream **stream)
2426 {
2427     struct folder *This = impl_from_IFolder(iface);
2428     FIXME("(%p)->(%s %x %x %p): stub\n", This, debugstr_w(filename), overwrite, unicode, stream);
2429     return E_NOTIMPL;
2430 }
2431 
2432 static const IFolderVtbl foldervtbl = {
2433     folder_QueryInterface,
2434     folder_AddRef,
2435     folder_Release,
2436     folder_GetTypeInfoCount,
2437     folder_GetTypeInfo,
2438     folder_GetIDsOfNames,
2439     folder_Invoke,
2440     folder_get_Path,
2441     folder_get_Name,
2442     folder_put_Name,
2443     folder_get_ShortPath,
2444     folder_get_ShortName,
2445     folder_get_Drive,
2446     folder_get_ParentFolder,
2447     folder_get_Attributes,
2448     folder_put_Attributes,
2449     folder_get_DateCreated,
2450     folder_get_DateLastModified,
2451     folder_get_DateLastAccessed,
2452     folder_get_Type,
2453     folder_Delete,
2454     folder_Copy,
2455     folder_Move,
2456     folder_get_IsRootFolder,
2457     folder_get_Size,
2458     folder_get_SubFolders,
2459     folder_get_Files,
2460     folder_CreateTextFile
2461 };
2462 
2463 HRESULT create_folder(const WCHAR *path, IFolder **folder)
2464 {
2465     struct folder *This;
2466 
2467     *folder = NULL;
2468 
2469     TRACE("%s\n", debugstr_w(path));
2470 
2471     This = heap_alloc(sizeof(struct folder));
2472     if (!This) return E_OUTOFMEMORY;
2473 
2474     This->IFolder_iface.lpVtbl = &foldervtbl;
2475     This->ref = 1;
2476     This->path = SysAllocString(path);
2477     if (!This->path)
2478     {
2479         heap_free(This);
2480         return E_OUTOFMEMORY;
2481     }
2482 
2483     init_classinfo(&CLSID_Folder, (IUnknown *)&This->IFolder_iface, &This->classinfo);
2484     *folder = &This->IFolder_iface;
2485 
2486     return S_OK;
2487 }
2488 
2489 static HRESULT WINAPI file_QueryInterface(IFile *iface, REFIID riid, void **obj)
2490 {
2491     struct file *This = impl_from_IFile(iface);
2492 
2493     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
2494 
2495     *obj = NULL;
2496 
2497     if (IsEqualIID(riid, &IID_IFile) ||
2498             IsEqualIID(riid, &IID_IDispatch) ||
2499             IsEqualIID(riid, &IID_IUnknown))
2500     {
2501         *obj = &This->IFile_iface;
2502     }
2503     else if (IsEqualIID( riid, &IID_IProvideClassInfo ))
2504     {
2505         *obj = &This->classinfo.IProvideClassInfo_iface;
2506     }
2507     else
2508         return E_NOINTERFACE;
2509 
2510     IUnknown_AddRef((IUnknown*)*obj);
2511     return S_OK;
2512 }
2513 
2514 static ULONG WINAPI file_AddRef(IFile *iface)
2515 {
2516     struct file *This = impl_from_IFile(iface);
2517     LONG ref = InterlockedIncrement(&This->ref);
2518 
2519     TRACE("(%p) ref=%d\n", This, ref);
2520 
2521     return ref;
2522 }
2523 
2524 static ULONG WINAPI file_Release(IFile *iface)
2525 {
2526     struct file *This = impl_from_IFile(iface);
2527     LONG ref = InterlockedDecrement(&This->ref);
2528 
2529     TRACE("(%p) ref=%d\n", This, ref);
2530 
2531     if(!ref)
2532     {
2533         heap_free(This->path);
2534         heap_free(This);
2535     }
2536 
2537     return ref;
2538 }
2539 
2540 static HRESULT WINAPI file_GetTypeInfoCount(IFile *iface, UINT *pctinfo)
2541 {
2542     struct file *This = impl_from_IFile(iface);
2543 
2544     TRACE("(%p)->(%p)\n", This, pctinfo);
2545 
2546     *pctinfo = 1;
2547     return S_OK;
2548 }
2549 
2550 static HRESULT WINAPI file_GetTypeInfo(IFile *iface,
2551         UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
2552 {
2553     struct file *This = impl_from_IFile(iface);
2554 
2555     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
2556 
2557     return get_typeinfo(IFile_tid, ppTInfo);
2558 }
2559 
2560 static HRESULT WINAPI file_GetIDsOfNames(IFile *iface, REFIID riid,
2561         LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
2562 {
2563     struct file *This = impl_from_IFile(iface);
2564     ITypeInfo *typeinfo;
2565     HRESULT hr;
2566 
2567     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid),
2568             rgszNames, cNames, lcid, rgDispId);
2569 
2570     hr = get_typeinfo(IFile_tid, &typeinfo);
2571     if(SUCCEEDED(hr)) {
2572         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
2573         ITypeInfo_Release(typeinfo);
2574     }
2575     return hr;
2576 }
2577 
2578 static HRESULT WINAPI file_Invoke(IFile *iface, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
2579 {
2580     struct file *This = impl_from_IFile(iface);
2581     ITypeInfo *typeinfo;
2582     HRESULT hr;
2583 
2584     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
2585             lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
2586 
2587     hr = get_typeinfo(IFile_tid, &typeinfo);
2588     if(SUCCEEDED(hr))
2589     {
2590         hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags,
2591                 pDispParams, pVarResult, pExcepInfo, puArgErr);
2592         ITypeInfo_Release(typeinfo);
2593     }
2594     return hr;
2595 }
2596 
2597 static HRESULT WINAPI file_get_Path(IFile *iface, BSTR *path)
2598 {
2599     struct file *This = impl_from_IFile(iface);
2600 
2601     TRACE("(%p)->(%p)\n", This, path);
2602 
2603     if (!path)
2604         return E_POINTER;
2605 
2606     *path = SysAllocString(This->path);
2607     if (!*path)
2608         return E_OUTOFMEMORY;
2609 
2610     return S_OK;
2611 }
2612 
2613 static HRESULT WINAPI file_get_Name(IFile *iface, BSTR *name)
2614 {
2615     struct file *This = impl_from_IFile(iface);
2616     WCHAR *ptr;
2617 
2618     TRACE("(%p)->(%p)\n", This, name);
2619 
2620     if(!name)
2621         return E_POINTER;
2622 
2623     *name = NULL;
2624 
2625     ptr = strrchrW(This->path, '\\');
2626     if (ptr)
2627     {
2628         *name = SysAllocString(ptr+1);
2629         TRACE("%s\n", debugstr_w(*name));
2630         if (!*name) return E_OUTOFMEMORY;
2631     }
2632     else
2633         return E_FAIL;
2634 
2635     return S_OK;
2636 }
2637 
2638 static HRESULT WINAPI file_put_Name(IFile *iface, BSTR pbstrName)
2639 {
2640     struct file *This = impl_from_IFile(iface);
2641     FIXME("(%p)->(%s)\n", This, debugstr_w(pbstrName));
2642     return E_NOTIMPL;
2643 }
2644 
2645 static HRESULT WINAPI file_get_ShortPath(IFile *iface, BSTR *pbstrPath)
2646 {
2647     struct file *This = impl_from_IFile(iface);
2648     FIXME("(%p)->(%p)\n", This, pbstrPath);
2649     return E_NOTIMPL;
2650 }
2651 
2652 static HRESULT WINAPI file_get_ShortName(IFile *iface, BSTR *pbstrName)
2653 {
2654     struct file *This = impl_from_IFile(iface);
2655     FIXME("(%p)->(%p)\n", This, pbstrName);
2656     return E_NOTIMPL;
2657 }
2658 
2659 static HRESULT WINAPI file_get_Drive(IFile *iface, IDrive **ppdrive)
2660 {
2661     struct file *This = impl_from_IFile(iface);
2662     FIXME("(%p)->(%p)\n", This, ppdrive);
2663     return E_NOTIMPL;
2664 }
2665 
2666 static HRESULT WINAPI file_get_ParentFolder(IFile *iface, IFolder **ppfolder)
2667 {
2668     struct file *This = impl_from_IFile(iface);
2669     FIXME("(%p)->(%p)\n", This, ppfolder);
2670     return E_NOTIMPL;
2671 }
2672 
2673 static HRESULT WINAPI file_get_Attributes(IFile *iface, FileAttribute *pfa)
2674 {
2675     struct file *This = impl_from_IFile(iface);
2676     DWORD fa;
2677 
2678     TRACE("(%p)->(%p)\n", This, pfa);
2679 
2680     if(!pfa)
2681         return E_POINTER;
2682 
2683     fa = GetFileAttributesW(This->path);
2684     if(fa == INVALID_FILE_ATTRIBUTES)
2685         return create_error(GetLastError());
2686 
2687     *pfa = fa & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN |
2688             FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE |
2689             FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_COMPRESSED);
2690     return S_OK;
2691 }
2692 
2693 static HRESULT WINAPI file_put_Attributes(IFile *iface, FileAttribute pfa)
2694 {
2695     struct file *This = impl_from_IFile(iface);
2696 
2697     TRACE("(%p)->(%x)\n", This, pfa);
2698 
2699     return SetFileAttributesW(This->path, pfa) ? S_OK : create_error(GetLastError());
2700 }
2701 
2702 static HRESULT get_date_from_filetime(const FILETIME *ft, DATE *date)
2703 {
2704     FILETIME ftlocal;
2705     SYSTEMTIME st;
2706 
2707     if (!date)
2708         return E_POINTER;
2709 
2710     FileTimeToLocalFileTime(ft, &ftlocal);
2711     FileTimeToSystemTime(&ftlocal, &st);
2712     SystemTimeToVariantTime(&st, date);
2713 
2714     return S_OK;
2715 }
2716 
2717 static HRESULT WINAPI file_get_DateCreated(IFile *iface, DATE *pdate)
2718 {
2719     struct file *This = impl_from_IFile(iface);
2720     FIXME("(%p)->(%p)\n", This, pdate);
2721     return E_NOTIMPL;
2722 }
2723 
2724 static HRESULT WINAPI file_get_DateLastModified(IFile *iface, DATE *date)
2725 {
2726     struct file *This = impl_from_IFile(iface);
2727     WIN32_FILE_ATTRIBUTE_DATA attrs;
2728 
2729     TRACE("(%p)->(%p)\n", This, date);
2730 
2731     if (GetFileAttributesExW(This->path, GetFileExInfoStandard, &attrs))
2732         return get_date_from_filetime(&attrs.ftLastWriteTime, date);
2733 
2734     return E_FAIL;
2735 }
2736 
2737 static HRESULT WINAPI file_get_DateLastAccessed(IFile *iface, DATE *pdate)
2738 {
2739     struct file *This = impl_from_IFile(iface);
2740     FIXME("(%p)->(%p)\n", This, pdate);
2741     return E_NOTIMPL;
2742 }
2743 
2744 static HRESULT WINAPI file_get_Size(IFile *iface, VARIANT *pvarSize)
2745 {
2746     struct file *This = impl_from_IFile(iface);
2747     ULARGE_INTEGER size;
2748     WIN32_FIND_DATAW fd;
2749     HANDLE f;
2750 
2751     TRACE("(%p)->(%p)\n", This, pvarSize);
2752 
2753     if(!pvarSize)
2754         return E_POINTER;
2755 
2756     f = FindFirstFileW(This->path, &fd);
2757     if(f == INVALID_HANDLE_VALUE)
2758         return create_error(GetLastError());
2759     FindClose(f);
2760 
2761     size.u.LowPart = fd.nFileSizeLow;
2762     size.u.HighPart = fd.nFileSizeHigh;
2763 
2764     return variant_from_largeint(&size, pvarSize);
2765 }
2766 
2767 static HRESULT WINAPI file_get_Type(IFile *iface, BSTR *pbstrType)
2768 {
2769     struct file *This = impl_from_IFile(iface);
2770     FIXME("(%p)->(%p)\n", This, pbstrType);
2771     return E_NOTIMPL;
2772 }
2773 
2774 static HRESULT WINAPI file_Delete(IFile *iface, VARIANT_BOOL Force)
2775 {
2776     struct file *This = impl_from_IFile(iface);
2777     FIXME("(%p)->(%x)\n", This, Force);
2778     return E_NOTIMPL;
2779 }
2780 
2781 static HRESULT WINAPI file_Copy(IFile *iface, BSTR Destination, VARIANT_BOOL OverWriteFiles)
2782 {
2783     struct file *This = impl_from_IFile(iface);
2784     FIXME("(%p)->(%s %x)\n", This, debugstr_w(Destination), OverWriteFiles);
2785     return E_NOTIMPL;
2786 }
2787 
2788 static HRESULT WINAPI file_Move(IFile *iface, BSTR Destination)
2789 {
2790     struct file *This = impl_from_IFile(iface);
2791     FIXME("(%p)->(%s)\n", This, debugstr_w(Destination));
2792     return E_NOTIMPL;
2793 }
2794 
2795 static HRESULT WINAPI file_OpenAsTextStream(IFile *iface, IOMode mode, Tristate format, ITextStream **stream)
2796 {
2797     struct file *This = impl_from_IFile(iface);
2798 
2799     TRACE("(%p)->(%d %d %p)\n", This, mode, format, stream);
2800 
2801     if (format == TristateUseDefault) {
2802         FIXME("default format not handled, defaulting to unicode\n");
2803         format = TristateTrue;
2804     }
2805 
2806     return create_textstream(This->path, OPEN_EXISTING, mode, format == TristateTrue, stream);
2807 }
2808 
2809 static const IFileVtbl file_vtbl = {
2810     file_QueryInterface,
2811     file_AddRef,
2812     file_Release,
2813     file_GetTypeInfoCount,
2814     file_GetTypeInfo,
2815     file_GetIDsOfNames,
2816     file_Invoke,
2817     file_get_Path,
2818     file_get_Name,
2819     file_put_Name,
2820     file_get_ShortPath,
2821     file_get_ShortName,
2822     file_get_Drive,
2823     file_get_ParentFolder,
2824     file_get_Attributes,
2825     file_put_Attributes,
2826     file_get_DateCreated,
2827     file_get_DateLastModified,
2828     file_get_DateLastAccessed,
2829     file_get_Size,
2830     file_get_Type,
2831     file_Delete,
2832     file_Copy,
2833     file_Move,
2834     file_OpenAsTextStream
2835 };
2836 
2837 static HRESULT create_file(BSTR path, IFile **file)
2838 {
2839     struct file *f;
2840     DWORD len, attrs;
2841 
2842     *file = NULL;
2843 
2844     f = heap_alloc(sizeof(struct file));
2845     if(!f)
2846         return E_OUTOFMEMORY;
2847 
2848     f->IFile_iface.lpVtbl = &file_vtbl;
2849     f->ref = 1;
2850 
2851     len = GetFullPathNameW(path, 0, NULL, NULL);
2852     if(!len) {
2853         heap_free(f);
2854         return E_FAIL;
2855     }
2856 
2857     f->path = heap_alloc(len*sizeof(WCHAR));
2858     if(!f->path) {
2859         heap_free(f);
2860         return E_OUTOFMEMORY;
2861     }
2862 
2863     if(!GetFullPathNameW(path, len, f->path, NULL)) {
2864         heap_free(f->path);
2865         heap_free(f);
2866         return E_FAIL;
2867     }
2868 
2869     attrs = GetFileAttributesW(f->path);
2870     if(attrs==INVALID_FILE_ATTRIBUTES ||
2871             (attrs&(FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE))) {
2872         heap_free(f->path);
2873         heap_free(f);
2874         return create_error(GetLastError());
2875     }
2876 
2877     init_classinfo(&CLSID_File, (IUnknown *)&f->IFile_iface, &f->classinfo);
2878     *file = &f->IFile_iface;
2879     return S_OK;
2880 }
2881 
2882 static HRESULT WINAPI filesys_QueryInterface(IFileSystem3 *iface, REFIID riid, void **ppvObject)
2883 {
2884     struct filesystem *This = impl_from_IFileSystem3(iface);
2885 
2886     TRACE("%p %s %p\n", iface, debugstr_guid(riid), ppvObject);
2887 
2888     if ( IsEqualGUID( riid, &IID_IFileSystem3 ) ||
2889          IsEqualGUID( riid, &IID_IFileSystem ) ||
2890          IsEqualGUID( riid, &IID_IDispatch ) ||
2891          IsEqualGUID( riid, &IID_IUnknown ) )
2892     {
2893         *ppvObject = &This->IFileSystem3_iface;
2894     }
2895     else if (IsEqualGUID( riid, &IID_IProvideClassInfo ))
2896     {
2897         *ppvObject = &This->classinfo.IProvideClassInfo_iface;
2898     }
2899     else if ( IsEqualGUID( riid, &IID_IDispatchEx ))
2900     {
2901         TRACE("Interface IDispatchEx not supported - returning NULL\n");
2902         *ppvObject = NULL;
2903         return E_NOINTERFACE;
2904     }
2905     else if ( IsEqualGUID( riid, &IID_IObjectWithSite ))
2906     {
2907         TRACE("Interface IObjectWithSite not supported - returning NULL\n");
2908         *ppvObject = NULL;
2909         return E_NOINTERFACE;
2910     }
2911     else
2912     {
2913         FIXME("Unsupported interface %s\n", debugstr_guid(riid));
2914         return E_NOINTERFACE;
2915     }
2916 
2917     IUnknown_AddRef((IUnknown*)*ppvObject);
2918 
2919     return S_OK;
2920 }
2921 
2922 static ULONG WINAPI filesys_AddRef(IFileSystem3 *iface)
2923 {
2924     TRACE("%p\n", iface);
2925 
2926     return 2;
2927 }
2928 
2929 static ULONG WINAPI filesys_Release(IFileSystem3 *iface)
2930 {
2931     TRACE("%p\n", iface);
2932 
2933     return 1;
2934 }
2935 
2936 static HRESULT WINAPI filesys_GetTypeInfoCount(IFileSystem3 *iface, UINT *pctinfo)
2937 {
2938     TRACE("(%p)->(%p)\n", iface, pctinfo);
2939 
2940     *pctinfo = 1;
2941     return S_OK;
2942 }
2943 
2944 static HRESULT WINAPI filesys_GetTypeInfo(IFileSystem3 *iface, UINT iTInfo,
2945                                         LCID lcid, ITypeInfo **ppTInfo)
2946 {
2947     TRACE("(%p)->(%u %u %p)\n", iface, iTInfo, lcid, ppTInfo);
2948     return get_typeinfo(IFileSystem3_tid, ppTInfo);
2949 }
2950 
2951 static HRESULT WINAPI filesys_GetIDsOfNames(IFileSystem3 *iface, REFIID riid,
2952                                         LPOLESTR *rgszNames, UINT cNames,
2953                                         LCID lcid, DISPID *rgDispId)
2954 {
2955     ITypeInfo *typeinfo;
2956     HRESULT hr;
2957 
2958     TRACE("(%p)->(%s %p %u %u %p)\n", iface, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId);
2959 
2960     hr = get_typeinfo(IFileSystem3_tid, &typeinfo);
2961     if(SUCCEEDED(hr))
2962     {
2963         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
2964         ITypeInfo_Release(typeinfo);
2965     }
2966 
2967     return hr;
2968 }
2969 
2970 static HRESULT WINAPI filesys_Invoke(IFileSystem3 *iface, DISPID dispIdMember,
2971                                       REFIID riid, LCID lcid, WORD wFlags,
2972                                       DISPPARAMS *pDispParams, VARIANT *pVarResult,
2973                                       EXCEPINFO *pExcepInfo, UINT *puArgErr)
2974 {
2975     ITypeInfo *typeinfo;
2976     HRESULT hr;
2977 
2978     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", iface, dispIdMember, debugstr_guid(riid),
2979            lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
2980 
2981     hr = get_typeinfo(IFileSystem3_tid, &typeinfo);
2982     if(SUCCEEDED(hr))
2983     {
2984         hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags,
2985                 pDispParams, pVarResult, pExcepInfo, puArgErr);
2986         ITypeInfo_Release(typeinfo);
2987     }
2988 
2989     return hr;
2990 }
2991 
2992 static HRESULT WINAPI filesys_get_Drives(IFileSystem3 *iface, IDriveCollection **ppdrives)
2993 {
2994     TRACE("%p %p\n", iface, ppdrives);
2995     return create_drivecoll(ppdrives);
2996 }
2997 
2998 static HRESULT WINAPI filesys_BuildPath(IFileSystem3 *iface, BSTR Path,
2999                                             BSTR Name, BSTR *Result)
3000 {
3001     BSTR ret;
3002 
3003     TRACE("%p %s %s %p\n", iface, debugstr_w(Path), debugstr_w(Name), Result);
3004 
3005     if (!Result) return E_POINTER;
3006 
3007     if (Path && Name)
3008     {
3009         int path_len = SysStringLen(Path), name_len = SysStringLen(Name);
3010 
3011         /* if both parts have backslashes strip one from Path */
3012         if (Path[path_len-1] == '\\' && Name[0] == '\\')
3013         {
3014             path_len -= 1;
3015 
3016             ret = SysAllocStringLen(NULL, path_len + name_len);
3017             if (ret)
3018             {
3019                 strcpyW(ret, Path);
3020                 ret[path_len] = 0;
3021                 strcatW(ret, Name);
3022             }
3023         }
3024         else if (Path[path_len-1] != '\\' && Name[0] != '\\')
3025         {
3026             ret = SysAllocStringLen(NULL, path_len + name_len + 1);
3027             if (ret)
3028             {
3029                 strcpyW(ret, Path);
3030                 if (Path[path_len-1] != ':')
3031                     strcatW(ret, bsW);
3032                 strcatW(ret, Name);
3033             }
3034         }
3035         else
3036         {
3037             ret = SysAllocStringLen(NULL, path_len + name_len);
3038             if (ret)
3039             {
3040                 strcpyW(ret, Path);
3041                 strcatW(ret, Name);
3042             }
3043         }
3044     }
3045     else if (Path || Name)
3046         ret = SysAllocString(Path ? Path : Name);
3047     else
3048         ret = SysAllocStringLen(NULL, 0);
3049 
3050     if (!ret) return E_OUTOFMEMORY;
3051     *Result = ret;
3052 
3053     return S_OK;
3054 }
3055 
3056 static HRESULT WINAPI filesys_GetDriveName(IFileSystem3 *iface, BSTR path, BSTR *drive)
3057 {
3058     TRACE("(%p)->(%s %p)\n", iface, debugstr_w(path), drive);
3059 
3060     if (!drive)
3061         return E_POINTER;
3062 
3063     *drive = NULL;
3064 
3065     if (path && strlenW(path) > 1 && path[1] == ':')
3066         *drive = SysAllocStringLen(path, 2);
3067 
3068     return S_OK;
3069 }
3070 
3071 static inline DWORD get_parent_folder_name(const WCHAR *path, DWORD len)
3072 {
3073     int i;
3074 
3075     if(!path)
3076         return 0;
3077 
3078     for(i=len-1; i>=0; i--)
3079         if(path[i]!='/' && path[i]!='\\')
3080             break;
3081 
3082     for(; i>=0; i--)
3083         if(path[i]=='/' || path[i]=='\\')
3084             break;
3085 
3086     for(; i>=0; i--)
3087         if(path[i]!='/' && path[i]!='\\')
3088             break;
3089 
3090     if(i < 0)
3091         return 0;
3092 
3093     if(path[i]==':' && i==1)
3094         i++;
3095     return i+1;
3096 }
3097 
3098 static HRESULT WINAPI filesys_GetParentFolderName(IFileSystem3 *iface, BSTR Path,
3099                                             BSTR *pbstrResult)
3100 {
3101     DWORD len;
3102 
3103     TRACE("%p %s %p\n", iface, debugstr_w(Path), pbstrResult);
3104 
3105     if(!pbstrResult)
3106         return E_POINTER;
3107 
3108     len = get_parent_folder_name(Path, SysStringLen(Path));
3109     if(!len) {
3110         *pbstrResult = NULL;
3111         return S_OK;
3112     }
3113 
3114     *pbstrResult = SysAllocStringLen(Path, len);
3115     if(!*pbstrResult)
3116         return E_OUTOFMEMORY;
3117     return S_OK;
3118 }
3119 
3120 static HRESULT WINAPI filesys_GetFileName(IFileSystem3 *iface, BSTR Path,
3121                                             BSTR *pbstrResult)
3122 {
3123     int i, end;
3124 
3125     TRACE("%p %s %p\n", iface, debugstr_w(Path), pbstrResult);
3126 
3127     if(!pbstrResult)
3128         return E_POINTER;
3129 
3130     if(!Path) {
3131         *pbstrResult = NULL;
3132         return S_OK;
3133     }
3134 
3135     for(end=strlenW(Path)-1; end>=0; end--)
3136         if(Path[end]!='/' && Path[end]!='\\')
3137             break;
3138 
3139     for(i=end; i>=0; i--)
3140         if(Path[i]=='/' || Path[i]=='\\')
3141             break;
3142     i++;
3143 
3144     if(i>end || (i==0 && end==1 && Path[1]==':')) {
3145         *pbstrResult = NULL;
3146         return S_OK;
3147     }
3148 
3149     *pbstrResult = SysAllocStringLen(Path+i, end-i+1);
3150     if(!*pbstrResult)
3151         return E_OUTOFMEMORY;
3152     return S_OK;
3153 }
3154 
3155 static HRESULT WINAPI filesys_GetBaseName(IFileSystem3 *iface, BSTR Path,
3156                                             BSTR *pbstrResult)
3157 {
3158     int i, end;
3159 
3160     TRACE("%p %s %p\n", iface, debugstr_w(Path), pbstrResult);
3161 
3162     if(!pbstrResult)
3163         return E_POINTER;
3164 
3165     if(!Path) {
3166         *pbstrResult = NULL;
3167         return S_OK;
3168     }
3169 
3170     for(end=strlenW(Path)-1; end>=0; end--)
3171         if(Path[end]!='/' && Path[end]!='\\')
3172             break;
3173 
3174     for(i=end; i>=0; i--) {
3175         if(Path[i]=='.' && Path[end+1]!='.')
3176             end = i-1;
3177         if(Path[i]=='/' || Path[i]=='\\')
3178             break;
3179     }
3180     i++;
3181 
3182     if((i>end && Path[end+1]!='.') || (i==0 && end==1 && Path[1]==':')) {
3183         *pbstrResult = NULL;
3184         return S_OK;
3185     }
3186 
3187     *pbstrResult = SysAllocStringLen(Path+i, end-i+1);
3188     if(!*pbstrResult)
3189         return E_OUTOFMEMORY;
3190     return S_OK;
3191 }
3192 
3193 static HRESULT WINAPI filesys_GetExtensionName(IFileSystem3 *iface, BSTR path,
3194                                             BSTR *ext)
3195 {
3196     INT len;
3197 
3198     TRACE("%p %s %p\n", iface, debugstr_w(path), ext);
3199 
3200     *ext = NULL;
3201     len = SysStringLen(path);
3202     while (len) {
3203         if (path[len-1] == '.') {
3204             *ext = SysAllocString(&path[len]);
3205             if (!*ext)
3206                 return E_OUTOFMEMORY;
3207             break;
3208         }
3209         len--;
3210     }
3211 
3212     return S_OK;
3213 }
3214 
3215 static HRESULT WINAPI filesys_GetAbsolutePathName(IFileSystem3 *iface, BSTR Path,
3216                                             BSTR *pbstrResult)
3217 {
3218     static const WCHAR cur_path[] = {'.',0};
3219 
3220     WCHAR buf[MAX_PATH], ch;
3221     const WCHAR *path;
3222     DWORD i, beg, len, exp_len;
3223     WIN32_FIND_DATAW fdata;
3224     HANDLE fh;
3225 
3226     TRACE("%p %s %p\n", iface, debugstr_w(Path), pbstrResult);
3227 
3228     if(!pbstrResult)
3229         return E_POINTER;
3230 
3231     if(!Path)
3232         path = cur_path;
3233     else
3234         path = Path;
3235 
3236     len = GetFullPathNameW(path, MAX_PATH, buf, NULL);
3237     if(!len)
3238         return E_FAIL;
3239 
3240     buf[0] = toupperW(buf[0]);
3241     if(len>3 && buf[len-1] == '\\')
3242         buf[--len] = 0;
3243 
3244     for(beg=3, i=3; i<=len; i++) {
3245         if(buf[i]!='\\' && buf[i])
3246             continue;
3247 
3248         ch = buf[i];
3249         buf[i] = 0;
3250         fh = FindFirstFileW(buf, &fdata);
3251         if(fh == INVALID_HANDLE_VALUE)
3252             break;
3253 
3254         exp_len = strlenW(fdata.cFileName);
3255         if(exp_len == i-beg)
3256             memcpy(buf+beg, fdata.cFileName, exp_len*sizeof(WCHAR));
3257         FindClose(fh);
3258         buf[i] = ch;
3259         beg = i+1;
3260     }
3261 
3262     *pbstrResult = SysAllocString(buf);
3263     if(!*pbstrResult)
3264         return E_OUTOFMEMORY;
3265     return S_OK;
3266 }
3267 
3268 static HRESULT WINAPI filesys_GetTempName(IFileSystem3 *iface, BSTR *pbstrResult)
3269 {
3270     static const WCHAR fmt[] = {'r','a','d','%','0','5','X','.','t','x','t',0};
3271 
3272     DWORD random;
3273 
3274     TRACE("%p %p\n", iface, pbstrResult);
3275 
3276     if(!pbstrResult)
3277         return E_POINTER;
3278 
3279     *pbstrResult = SysAllocStringLen(NULL, 12);
3280     if(!*pbstrResult)
3281         return E_OUTOFMEMORY;
3282 
3283     if(!RtlGenRandom(&random, sizeof(random)))
3284         return E_FAIL;
3285     sprintfW(*pbstrResult, fmt, random & 0xfffff);
3286     return S_OK;
3287 }
3288 
3289 static HRESULT WINAPI filesys_DriveExists(IFileSystem3 *iface, BSTR DriveSpec,
3290                                             VARIANT_BOOL *pfExists)
3291 {
3292     UINT len;
3293     WCHAR driveletter;
3294     TRACE("%p %s %p\n", iface, debugstr_w(DriveSpec), pfExists);
3295 
3296     if (!pfExists) return E_POINTER;
3297 
3298     *pfExists = VARIANT_FALSE;
3299     len = SysStringLen(DriveSpec);
3300 
3301     if (len >= 1) {
3302         driveletter = toupperW(DriveSpec[0]);
3303         if (driveletter >= 'A' && driveletter <= 'Z'
3304                 && (len < 2 || DriveSpec[1] == ':')
3305                 && (len < 3 || DriveSpec[2] == '\\')) {
3306             const WCHAR root[] = {driveletter, ':', '\\', 0};
3307             UINT drivetype = GetDriveTypeW(root);
3308             *pfExists = drivetype != DRIVE_NO_ROOT_DIR && drivetype != DRIVE_UNKNOWN ? VARIANT_TRUE : VARIANT_FALSE;
3309         }
3310     }
3311 
3312     return S_OK;
3313 }
3314 
3315 static HRESULT WINAPI filesys_FileExists(IFileSystem3 *iface, BSTR path, VARIANT_BOOL *ret)
3316 {
3317     DWORD attrs;
3318     TRACE("%p %s %p\n", iface, debugstr_w(path), ret);
3319 
3320     if (!ret) return E_POINTER;
3321 
3322     attrs = GetFileAttributesW(path);
3323     *ret = attrs != INVALID_FILE_ATTRIBUTES && !(attrs & FILE_ATTRIBUTE_DIRECTORY) ? VARIANT_TRUE : VARIANT_FALSE;
3324     return S_OK;
3325 }
3326 
3327 static HRESULT WINAPI filesys_FolderExists(IFileSystem3 *iface, BSTR path, VARIANT_BOOL *ret)
3328 {
3329     DWORD attrs;
3330     TRACE("%p %s %p\n", iface, debugstr_w(path), ret);
3331 
3332     if (!ret) return E_POINTER;
3333 
3334     attrs = GetFileAttributesW(path);
3335     *ret = attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY) ? VARIANT_TRUE : VARIANT_FALSE;
3336 
3337     return S_OK;
3338 }
3339 
3340 static HRESULT WINAPI filesys_GetDrive(IFileSystem3 *iface, BSTR DriveSpec,
3341                                             IDrive **ppdrive)
3342 {
3343     UINT len;
3344     HRESULT hr;
3345     WCHAR driveletter;
3346     VARIANT_BOOL drive_exists;
3347 
3348     TRACE("%p %s %p\n", iface, debugstr_w(DriveSpec), ppdrive);
3349 
3350     if (!ppdrive)
3351         return E_POINTER;
3352 
3353     *ppdrive = NULL;
3354 
3355     /* DriveSpec may be one of: 'x', 'x:', 'x:\', '\\computer\share' */
3356     len = SysStringLen(DriveSpec);
3357     if (!len)
3358         return E_INVALIDARG;
3359     else if (len <= 3) {
3360         driveletter = toupperW(DriveSpec[0]);
3361         if (driveletter < 'A' || driveletter > 'Z'
3362                 || (len >= 2 && DriveSpec[1] != ':')
3363                 || (len == 3 && DriveSpec[2] != '\\'))
3364             return E_INVALIDARG;
3365         hr = IFileSystem3_DriveExists(iface, DriveSpec, &drive_exists);
3366         if (FAILED(hr))
3367             return hr;
3368         if (drive_exists == VARIANT_FALSE)
3369             return CTL_E_DEVICEUNAVAILABLE;
3370         return create_drive(driveletter, ppdrive);
3371     } else {
3372         if (DriveSpec[0] != '\\' || DriveSpec[1] != '\\')
3373             return E_INVALIDARG;
3374         FIXME("%s not implemented yet\n", debugstr_w(DriveSpec));
3375         return E_NOTIMPL;
3376     }
3377 }
3378 
3379 static HRESULT WINAPI filesys_GetFile(IFileSystem3 *iface, BSTR FilePath,
3380                                             IFile **ppfile)
3381 {
3382     TRACE("%p %s %p\n", iface, debugstr_w(FilePath), ppfile);
3383 
3384     if(!ppfile)
3385         return E_POINTER;
3386     if(!FilePath)
3387         return E_INVALIDARG;
3388 
3389     return create_file(FilePath, ppfile);
3390 }
3391 
3392 static HRESULT WINAPI filesys_GetFolder(IFileSystem3 *iface, BSTR FolderPath,
3393                                             IFolder **folder)
3394 {
3395     DWORD attrs;
3396 
3397     TRACE("%p %s %p\n", iface, debugstr_w(FolderPath), folder);
3398 
3399     if(!folder)
3400         return E_POINTER;
3401 
3402     *folder = NULL;
3403     if(!FolderPath)
3404         return E_INVALIDARG;
3405 
3406     attrs = GetFileAttributesW(FolderPath);
3407     if((attrs == INVALID_FILE_ATTRIBUTES) || !(attrs & FILE_ATTRIBUTE_DIRECTORY))
3408         return CTL_E_PATHNOTFOUND;
3409 
3410     return create_folder(FolderPath, folder);
3411 }
3412 
3413 static HRESULT WINAPI filesys_GetSpecialFolder(IFileSystem3 *iface,
3414                                             SpecialFolderConst SpecialFolder,
3415                                             IFolder **folder)
3416 {
3417     WCHAR pathW[MAX_PATH];
3418     DWORD ret;
3419 
3420     TRACE("%p %d %p\n", iface, SpecialFolder, folder);
3421 
3422     if (!folder)
3423         return E_POINTER;
3424 
3425     *folder = NULL;
3426 
3427     switch (SpecialFolder)
3428     {
3429     case WindowsFolder:
3430         ret = GetWindowsDirectoryW(pathW, sizeof(pathW)/sizeof(WCHAR));
3431         break;
3432     case SystemFolder:
3433         ret = GetSystemDirectoryW(pathW, sizeof(pathW)/sizeof(WCHAR));
3434         break;
3435     case TemporaryFolder:
3436         ret = GetTempPathW(sizeof(pathW)/sizeof(WCHAR), pathW);
3437         /* we don't want trailing backslash */
3438         if (ret && pathW[ret-1] == '\\')
3439             pathW[ret-1] = 0;
3440         break;
3441     default:
3442         FIXME("unknown special folder type, %d\n", SpecialFolder);
3443         return E_INVALIDARG;
3444     }
3445 
3446     if (!ret)
3447         return HRESULT_FROM_WIN32(GetLastError());
3448 
3449     return create_folder(pathW, folder);
3450 }
3451 
3452 static inline HRESULT delete_file(const WCHAR *file, DWORD file_len, VARIANT_BOOL force)
3453 {
3454     WCHAR path[MAX_PATH];
3455     DWORD len, name_len;
3456     WIN32_FIND_DATAW ffd;
3457     HANDLE f;
3458 
3459     f = FindFirstFileW(file, &ffd);
3460     if(f == INVALID_HANDLE_VALUE)
3461         return create_error(GetLastError());
3462 
3463     len = get_parent_folder_name(file, file_len);
3464     if(len+1 >= MAX_PATH) {
3465         FindClose(f);
3466         return E_FAIL;
3467     }
3468     if(len) {
3469         memcpy(path, file, len*sizeof(WCHAR));
3470         path[len++] = '\\';
3471     }
3472 
3473     do {
3474         if(ffd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE))
3475             continue;
3476 
3477         name_len = strlenW(ffd.cFileName);
3478         if(len+name_len+1 >= MAX_PATH) {
3479             FindClose(f);
3480             return E_FAIL;
3481         }
3482         memcpy(path+len, ffd.cFileName, (name_len+1)*sizeof(WCHAR));
3483 
3484         TRACE("deleting %s\n", debugstr_w(path));
3485 
3486         if(!DeleteFileW(path)) {
3487             if(!force || !SetFileAttributesW(path, FILE_ATTRIBUTE_NORMAL)
3488                     || !DeleteFileW(path)) {
3489                 FindClose(f);
3490                 return create_error(GetLastError());
3491             }
3492         }
3493     } while(FindNextFileW(f, &ffd));
3494     FindClose(f);
3495 
3496     return S_OK;
3497 }
3498 
3499 static HRESULT WINAPI filesys_DeleteFile(IFileSystem3 *iface, BSTR FileSpec,
3500                                             VARIANT_BOOL Force)
3501 {
3502     TRACE("%p %s %d\n", iface, debugstr_w(FileSpec), Force);
3503 
3504     if(!FileSpec)
3505         return E_POINTER;
3506 
3507     return delete_file(FileSpec, SysStringLen(FileSpec), Force);
3508 }
3509 
3510 static HRESULT delete_folder(const WCHAR *folder, DWORD folder_len, VARIANT_BOOL force)
3511 {
3512     WCHAR path[MAX_PATH];
3513     DWORD len, name_len;
3514     WIN32_FIND_DATAW ffd;
3515     HANDLE f;
3516     HRESULT hr;
3517 
3518     f = FindFirstFileW(folder, &ffd);
3519     if(f == INVALID_HANDLE_VALUE)
3520         return create_error(GetLastError());
3521 
3522     len = get_parent_folder_name(folder, folder_len);
3523     if(len+1 >= MAX_PATH) {
3524         FindClose(f);
3525         return E_FAIL;
3526     }
3527     if(len) {
3528         memcpy(path, folder, len*sizeof(WCHAR));
3529         path[len++] = '\\';
3530     }
3531 
3532     do {
3533         if(!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
3534             continue;
3535         if(ffd.cFileName[0]=='.' && (ffd.cFileName[1]==0 ||
3536                     (ffd.cFileName[1]=='.' && ffd.cFileName[2]==0)))
3537             continue;
3538 
3539         name_len = strlenW(ffd.cFileName);
3540         if(len+name_len+3 >= MAX_PATH) {
3541             FindClose(f);
3542             return E_FAIL;
3543         }
3544         memcpy(path+len, ffd.cFileName, name_len*sizeof(WCHAR));
3545         path[len+name_len] = '\\';
3546         path[len+name_len+1] = '*';
3547         path[len+name_len+2] = 0;
3548 
3549         hr = delete_file(path, len+name_len+2, force);
3550         if(FAILED(hr)) {
3551             FindClose(f);
3552             return hr;
3553         }
3554 
3555         hr = delete_folder(path, len+name_len+2, force);
3556         if(FAILED(hr)) {
3557             FindClose(f);
3558             return hr;
3559         }
3560 
3561         path[len+name_len] = 0;
3562         TRACE("deleting %s\n", debugstr_w(path));
3563 
3564         if(!RemoveDirectoryW(path)) {
3565             FindClose(f);
3566             return create_error(GetLastError());
3567         }
3568     } while(FindNextFileW(f, &ffd));
3569     FindClose(f);
3570 
3571     return S_OK;
3572 }
3573 
3574 static HRESULT WINAPI filesys_DeleteFolder(IFileSystem3 *iface, BSTR FolderSpec,
3575                                             VARIANT_BOOL Force)
3576 {
3577     TRACE("%p %s %d\n", iface, debugstr_w(FolderSpec), Force);
3578 
3579     if(!FolderSpec)
3580         return E_POINTER;
3581 
3582     return delete_folder(FolderSpec, SysStringLen(FolderSpec), Force);
3583 }
3584 
3585 static HRESULT WINAPI filesys_MoveFile(IFileSystem3 *iface, BSTR Source,
3586                                             BSTR Destination)
3587 {
3588     FIXME("%p %s %s\n", iface, debugstr_w(Source), debugstr_w(Destination));
3589 
3590     return E_NOTIMPL;
3591 }
3592 
3593 static HRESULT WINAPI filesys_MoveFolder(IFileSystem3 *iface,BSTR Source,
3594                                             BSTR Destination)
3595 {
3596     FIXME("%p %s %s\n", iface, debugstr_w(Source), debugstr_w(Destination));
3597 
3598     return E_NOTIMPL;
3599 }
3600 
3601 static inline HRESULT copy_file(const WCHAR *source, DWORD source_len,
3602         const WCHAR *destination, DWORD destination_len, VARIANT_BOOL overwrite)
3603 {
3604     DWORD attrs;
3605     WCHAR src_path[MAX_PATH], dst_path[MAX_PATH];
3606     DWORD src_len, dst_len, name_len;
3607     WIN32_FIND_DATAW ffd;
3608     HANDLE f;
3609     HRESULT hr;
3610 
3611     if(!source[0] || !destination[0])
3612         return E_INVALIDARG;
3613 
3614     attrs = GetFileAttributesW(destination);
3615     if(attrs==INVALID_FILE_ATTRIBUTES || !(attrs & FILE_ATTRIBUTE_DIRECTORY)) {
3616         attrs = GetFileAttributesW(source);
3617         if(attrs == INVALID_FILE_ATTRIBUTES)
3618             return create_error(GetLastError());
3619         else if(attrs & FILE_ATTRIBUTE_DIRECTORY)
3620             return CTL_E_FILENOTFOUND;
3621 
3622         if(!CopyFileW(source, destination, !overwrite))
3623             return create_error(GetLastError());
3624         return S_OK;
3625     }
3626 
3627     f = FindFirstFileW(source, &ffd);
3628     if(f == INVALID_HANDLE_VALUE)
3629         return CTL_E_FILENOTFOUND;
3630 
3631     src_len = get_parent_folder_name(source, source_len);
3632     if(src_len+1 >= MAX_PATH) {
3633         FindClose(f);
3634         return E_FAIL;
3635     }
3636     if(src_len) {
3637         memcpy(src_path, source, src_len*sizeof(WCHAR));
3638         src_path[src_len++] = '\\';
3639     }
3640 
3641     dst_len = destination_len;
3642     if(dst_len+1 >= MAX_PATH) {
3643         FindClose(f);
3644         return E_FAIL;
3645     }
3646     memcpy(dst_path, destination, dst_len*sizeof(WCHAR));
3647     if(dst_path[dst_len-1]!= '\\' && dst_path[dst_len-1]!='/')
3648         dst_path[dst_len++] = '\\';
3649 
3650     hr = CTL_E_FILENOTFOUND;
3651     do {
3652         if(ffd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE))
3653             continue;
3654 
3655         name_len = strlenW(ffd.cFileName);
3656         if(src_len+name_len+1>=MAX_PATH || dst_len+name_len+1>=MAX_PATH) {
3657             FindClose(f);
3658             return E_FAIL;
3659         }
3660         memcpy(src_path+src_len, ffd.cFileName, (name_len+1)*sizeof(WCHAR));
3661         memcpy(dst_path+dst_len, ffd.cFileName, (name_len+1)*sizeof(WCHAR));
3662 
3663         TRACE("copying %s to %s\n", debugstr_w(src_path), debugstr_w(dst_path));
3664 
3665         if(!CopyFileW(src_path, dst_path, !overwrite)) {
3666             FindClose(f);
3667             return create_error(GetLastError());
3668         }else {
3669             hr = S_OK;
3670         }
3671     } while(FindNextFileW(f, &ffd));
3672     FindClose(f);
3673 
3674     return hr;
3675 }
3676 
3677 static HRESULT WINAPI filesys_CopyFile(IFileSystem3 *iface, BSTR Source,
3678                                             BSTR Destination, VARIANT_BOOL OverWriteFiles)
3679 {
3680     TRACE("%p %s %s %d\n", iface, debugstr_w(Source), debugstr_w(Destination), OverWriteFiles);
3681 
3682     if(!Source || !Destination)
3683         return E_POINTER;
3684 
3685     return copy_file(Source, SysStringLen(Source), Destination,
3686             SysStringLen(Destination), OverWriteFiles);
3687 }
3688 
3689 static HRESULT copy_folder(const WCHAR *source, DWORD source_len, const WCHAR *destination,
3690         DWORD destination_len, VARIANT_BOOL overwrite)
3691 {
3692     DWORD tmp, src_len, dst_len, name_len;
3693     WCHAR src[MAX_PATH], dst[MAX_PATH];
3694     WIN32_FIND_DATAW ffd;
3695     HANDLE f;
3696     HRESULT hr;
3697     BOOL copied = FALSE;
3698 
3699     if(!source[0] || !destination[0])
3700         return E_INVALIDARG;
3701 
3702     dst_len = destination_len;
3703     if(dst_len+1 >= MAX_PATH)
3704         return E_FAIL;
3705     memcpy(dst, destination, (dst_len+1)*sizeof(WCHAR));
3706 
3707     if(dst[dst_len-1]!='\\' && dst[dst_len-1]!='/' &&
3708             (tmp = GetFileAttributesW(source))!=INVALID_FILE_ATTRIBUTES &&
3709             tmp&FILE_ATTRIBUTE_DIRECTORY) {
3710         if(!CreateDirectoryW(dst, NULL)) {
3711             if(overwrite && GetLastError()==ERROR_ALREADY_EXISTS) {
3712                 tmp = GetFileAttributesW(dst);
3713                 if(tmp==INVALID_FILE_ATTRIBUTES || !(tmp&FILE_ATTRIBUTE_DIRECTORY))
3714                     return CTL_E_FILEALREADYEXISTS;
3715             }else {
3716                 return create_error(GetLastError());
3717             }
3718         }
3719         copied = TRUE;
3720 
3721         src_len = source_len;
3722         if(src_len+2 >= MAX_PATH)
3723             return E_FAIL;
3724         memcpy(src, source, src_len*sizeof(WCHAR));
3725         src[src_len++] = '\\';
3726         src[src_len] = '*';
3727         src[src_len+1] = 0;
3728 
3729         hr = copy_file(src, src_len+1, dst, dst_len, overwrite);
3730         if(FAILED(hr) && hr!=CTL_E_FILENOTFOUND)
3731             return create_error(GetLastError());
3732 
3733         f = FindFirstFileW(src, &ffd);
3734     }else {
3735         src_len = get_parent_folder_name(source, source_len);
3736         if(src_len+2 >= MAX_PATH)
3737             return E_FAIL;
3738         memcpy(src, source, src_len*sizeof(WCHAR));
3739         if(src_len)
3740             src[src_len++] = '\\';
3741 
3742         f = FindFirstFileW(source, &ffd);
3743     }
3744     if(f == INVALID_HANDLE_VALUE)
3745         return CTL_E_PATHNOTFOUND;
3746 
3747     dst[dst_len++] = '\\';
3748     dst[dst_len] = 0;
3749 
3750     do {
3751         if(!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
3752             continue;
3753         if(ffd.cFileName[0]=='.' && (ffd.cFileName[1]==0 ||
3754                     (ffd.cFileName[1]=='.' && ffd.cFileName[2]==0)))
3755             continue;
3756 
3757         name_len = strlenW(ffd.cFileName);
3758         if(dst_len+name_len>=MAX_PATH || src_len+name_len+2>=MAX_PATH) {
3759             FindClose(f);
3760             return E_FAIL;
3761         }
3762         memcpy(dst+dst_len, ffd.cFileName, name_len*sizeof(WCHAR));
3763         dst[dst_len+name_len] = 0;
3764         memcpy(src+src_len, ffd.cFileName, name_len*sizeof(WCHAR));
3765         src[src_len+name_len] = '\\';
3766         src[src_len+name_len+1] = '*';
3767         src[src_len+name_len+2] = 0;
3768 
3769         TRACE("copying %s to %s\n", debugstr_w(src), debugstr_w(dst));
3770 
3771         if(!CreateDirectoryW(dst, NULL)) {
3772             if(overwrite && GetLastError()==ERROR_ALREADY_EXISTS) {
3773                 tmp = GetFileAttributesW(dst);
3774                 if(tmp==INVALID_FILE_ATTRIBUTES || !(tmp&FILE_ATTRIBUTE_DIRECTORY)) {
3775                     FindClose(f);
3776                     return CTL_E_FILEALREADYEXISTS;
3777                 }
3778             }
3779 
3780             FindClose(f);
3781             return create_error(GetLastError());
3782         }
3783         copied = TRUE;
3784 
3785         hr = copy_file(src, src_len+name_len+2, dst, dst_len+name_len, overwrite);
3786         if(FAILED(hr) && hr!=CTL_E_FILENOTFOUND) {
3787             FindClose(f);
3788             return hr;
3789         }
3790 
3791         hr = copy_folder(src, src_len+name_len+2, dst, dst_len+name_len, overwrite);
3792         if(FAILED(hr) && hr!=CTL_E_PATHNOTFOUND) {
3793             FindClose(f);
3794             return hr;
3795         }
3796     } while(FindNextFileW(f, &ffd));
3797     FindClose(f);
3798 
3799     return copied ? S_OK : CTL_E_PATHNOTFOUND;
3800 }
3801 
3802 static HRESULT WINAPI filesys_CopyFolder(IFileSystem3 *iface, BSTR Source,
3803                                             BSTR Destination, VARIANT_BOOL OverWriteFiles)
3804 {
3805     TRACE("%p %s %s %d\n", iface, debugstr_w(Source), debugstr_w(Destination), OverWriteFiles);
3806 
3807     if(!Source || !Destination)
3808         return E_POINTER;
3809 
3810     return copy_folder(Source, SysStringLen(Source), Destination,
3811             SysStringLen(Destination), OverWriteFiles);
3812 }
3813 
3814 static HRESULT WINAPI filesys_CreateFolder(IFileSystem3 *iface, BSTR path,
3815                                             IFolder **folder)
3816 {
3817     BOOL ret;
3818 
3819     TRACE("(%p)->(%s %p)\n", iface, debugstr_w(path), folder);
3820 
3821     ret = CreateDirectoryW(path, NULL);
3822     if (!ret)
3823     {
3824         *folder = NULL;
3825         if (GetLastError() == ERROR_ALREADY_EXISTS) return CTL_E_FILEALREADYEXISTS;
3826         return HRESULT_FROM_WIN32(GetLastError());
3827     }
3828 
3829     return create_folder(path, folder);
3830 }
3831 
3832 static HRESULT WINAPI filesys_CreateTextFile(IFileSystem3 *iface, BSTR filename,
3833                                             VARIANT_BOOL overwrite, VARIANT_BOOL unicode,
3834                                             ITextStream **stream)
3835 {
3836     DWORD disposition;
3837 
3838     TRACE("%p %s %d %d %p\n", iface, debugstr_w(filename), overwrite, unicode, stream);
3839 
3840     disposition = overwrite == VARIANT_TRUE ? CREATE_ALWAYS : CREATE_NEW;
3841     return create_textstream(filename, disposition, ForWriting, !!unicode, stream);
3842 }
3843 
3844 static HRESULT WINAPI filesys_OpenTextFile(IFileSystem3 *iface, BSTR filename,
3845                                             IOMode mode, VARIANT_BOOL create,
3846                                             Tristate format, ITextStream **stream)
3847 {
3848     DWORD disposition;
3849 
3850     TRACE("(%p)->(%s %d %d %d %p)\n", iface, debugstr_w(filename), mode, create, format, stream);
3851     disposition = create == VARIANT_TRUE ? OPEN_ALWAYS : OPEN_EXISTING;
3852 
3853     if (format == TristateUseDefault) {
3854         FIXME("default format not handled, defaulting to unicode\n");
3855         format = TristateTrue;
3856     }
3857 
3858     return create_textstream(filename, disposition, mode, format == TristateTrue, stream);
3859 }
3860 
3861 static HRESULT WINAPI filesys_GetStandardStream(IFileSystem3 *iface,
3862                                             StandardStreamTypes StandardStreamType,
3863                                             VARIANT_BOOL Unicode,
3864                                             ITextStream **ppts)
3865 {
3866     FIXME("%p %d %d %p\n", iface, StandardStreamType, Unicode, ppts);
3867 
3868     return E_NOTIMPL;
3869 }
3870 
3871 static void get_versionstring(VS_FIXEDFILEINFO *info, WCHAR *ver)
3872 {
3873     static const WCHAR fmtW[] = {'%','d','.','%','d','.','%','d','.','%','d',0};
3874     DWORDLONG version;
3875     WORD a, b, c, d;
3876 
3877     version = (((DWORDLONG)info->dwFileVersionMS) << 32) + info->dwFileVersionLS;
3878     a = (WORD)( version >> 48);
3879     b = (WORD)((version >> 32) & 0xffff);
3880     c = (WORD)((version >> 16) & 0xffff);
3881     d = (WORD)( version & 0xffff);
3882 
3883     sprintfW(ver, fmtW, a, b, c, d);
3884 }
3885 
3886 static HRESULT WINAPI filesys_GetFileVersion(IFileSystem3 *iface, BSTR name, BSTR *version)
3887 {
3888     static const WCHAR rootW[] = {'\\',0};
3889     VS_FIXEDFILEINFO *info;
3890     WCHAR ver[30];
3891     void *ptr;
3892     DWORD len;
3893     BOOL ret;
3894 
3895     TRACE("%p %s %p\n", iface, debugstr_w(name), version);
3896 
3897     len = GetFileVersionInfoSizeW(name, NULL);
3898     if (!len)
3899         return HRESULT_FROM_WIN32(GetLastError());
3900 
3901     ptr = heap_alloc(len);
3902     if (!GetFileVersionInfoW(name, 0, len, ptr))
3903     {
3904         heap_free(ptr);
3905         return HRESULT_FROM_WIN32(GetLastError());
3906     }
3907 
3908     ret = VerQueryValueW(ptr, rootW, (void**)&info, &len);
3909     if (!ret)
3910     {
3911         heap_free(ptr);
3912         return HRESULT_FROM_WIN32(GetLastError());
3913     }
3914 
3915     get_versionstring(info, ver);
3916     heap_free(ptr);
3917 
3918     *version = SysAllocString(ver);
3919     TRACE("version=%s\n", debugstr_w(ver));
3920 
3921     return S_OK;
3922 }
3923 
3924 static const struct IFileSystem3Vtbl filesys_vtbl =
3925 {
3926     filesys_QueryInterface,
3927     filesys_AddRef,
3928     filesys_Release,
3929     filesys_GetTypeInfoCount,
3930     filesys_GetTypeInfo,
3931     filesys_GetIDsOfNames,
3932     filesys_Invoke,
3933     filesys_get_Drives,
3934     filesys_BuildPath,
3935     filesys_GetDriveName,
3936     filesys_GetParentFolderName,
3937     filesys_GetFileName,
3938     filesys_GetBaseName,
3939     filesys_GetExtensionName,
3940     filesys_GetAbsolutePathName,
3941     filesys_GetTempName,
3942     filesys_DriveExists,
3943     filesys_FileExists,
3944     filesys_FolderExists,
3945     filesys_GetDrive,
3946     filesys_GetFile,
3947     filesys_GetFolder,
3948     filesys_GetSpecialFolder,
3949     filesys_DeleteFile,
3950     filesys_DeleteFolder,
3951     filesys_MoveFile,
3952     filesys_MoveFolder,
3953     filesys_CopyFile,
3954     filesys_CopyFolder,
3955     filesys_CreateFolder,
3956     filesys_CreateTextFile,
3957     filesys_OpenTextFile,
3958     filesys_GetStandardStream,
3959     filesys_GetFileVersion
3960 };
3961 
3962 static struct filesystem filesystem;
3963 
3964 HRESULT WINAPI FileSystem_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv)
3965 {
3966     TRACE("(%p %s %p)\n", outer, debugstr_guid(riid), ppv);
3967 
3968     filesystem.IFileSystem3_iface.lpVtbl = &filesys_vtbl;
3969     init_classinfo(&CLSID_FileSystemObject, (IUnknown *)&filesystem.IFileSystem3_iface, &filesystem.classinfo);
3970     return IFileSystem3_QueryInterface(&filesystem.IFileSystem3_iface, riid, ppv);
3971 }
3972