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