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