/* * Copyright 2012 Alistair Leslie-Hughes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define COBJMACROS #include #include #include #include #include "windef.h" #include "winbase.h" #include "ole2.h" #include "olectl.h" #include "dispex.h" #include "ntsecapi.h" #include "scrrun.h" #include "scrrun_private.h" #include "wine/debug.h" #include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(scrrun); static const WCHAR bsW[] = {'\\',0}; static const WCHAR utf16bom = 0xfeff; struct filesystem { struct provideclassinfo classinfo; IFileSystem3 IFileSystem3_iface; }; struct foldercollection { struct provideclassinfo classinfo; IFolderCollection IFolderCollection_iface; LONG ref; BSTR path; }; struct filecollection { struct provideclassinfo classinfo; IFileCollection IFileCollection_iface; LONG ref; BSTR path; }; struct drivecollection { struct provideclassinfo classinfo; IDriveCollection IDriveCollection_iface; LONG ref; DWORD drives; LONG count; }; struct enumdata { union { struct { struct foldercollection *coll; HANDLE find; } foldercoll; struct { struct filecollection *coll; HANDLE find; } filecoll; struct { struct drivecollection *coll; INT cur; } drivecoll; } u; }; struct enumvariant { IEnumVARIANT IEnumVARIANT_iface; LONG ref; struct enumdata data; }; struct drive { struct provideclassinfo classinfo; IDrive IDrive_iface; LONG ref; BSTR root; }; struct folder { struct provideclassinfo classinfo; IFolder IFolder_iface; LONG ref; BSTR path; }; struct file { struct provideclassinfo classinfo; IFile IFile_iface; LONG ref; WCHAR *path; }; struct textstream { struct provideclassinfo classinfo; ITextStream ITextStream_iface; LONG ref; IOMode mode; BOOL unicode; LARGE_INTEGER size; HANDLE file; BOOL eof; WCHAR *read_buf; size_t read_buf_size; }; enum iotype { IORead, IOWrite }; static inline struct filesystem *impl_from_IFileSystem3(IFileSystem3 *iface) { return CONTAINING_RECORD(iface, struct filesystem, IFileSystem3_iface); } static inline struct drive *impl_from_IDrive(IDrive *iface) { return CONTAINING_RECORD(iface, struct drive, IDrive_iface); } static inline struct folder *impl_from_IFolder(IFolder *iface) { return CONTAINING_RECORD(iface, struct folder, IFolder_iface); } static inline struct file *impl_from_IFile(IFile *iface) { return CONTAINING_RECORD(iface, struct file, IFile_iface); } static inline struct textstream *impl_from_ITextStream(ITextStream *iface) { return CONTAINING_RECORD(iface, struct textstream, ITextStream_iface); } static inline struct foldercollection *impl_from_IFolderCollection(IFolderCollection *iface) { return CONTAINING_RECORD(iface, struct foldercollection, IFolderCollection_iface); } static inline struct filecollection *impl_from_IFileCollection(IFileCollection *iface) { return CONTAINING_RECORD(iface, struct filecollection, IFileCollection_iface); } static inline struct drivecollection *impl_from_IDriveCollection(IDriveCollection *iface) { return CONTAINING_RECORD(iface, struct drivecollection, IDriveCollection_iface); } static inline struct enumvariant *impl_from_IEnumVARIANT(IEnumVARIANT *iface) { return CONTAINING_RECORD(iface, struct enumvariant, IEnumVARIANT_iface); } static inline HRESULT create_error(DWORD err) { switch(err) { case ERROR_FILE_NOT_FOUND: return CTL_E_FILENOTFOUND; case ERROR_PATH_NOT_FOUND: return CTL_E_PATHNOTFOUND; case ERROR_ACCESS_DENIED: return CTL_E_PERMISSIONDENIED; case ERROR_FILE_EXISTS: return CTL_E_FILEALREADYEXISTS; case ERROR_ALREADY_EXISTS: return CTL_E_FILEALREADYEXISTS; default: FIXME("Unsupported error code: %d\n", err); return E_FAIL; } } static HRESULT create_folder(const WCHAR*, IFolder**); static HRESULT create_file(BSTR, IFile**); static HRESULT create_foldercoll_enum(struct foldercollection*, IUnknown**); static HRESULT create_filecoll_enum(struct filecollection*, IUnknown**); static HRESULT create_drivecoll_enum(struct drivecollection*, IUnknown**); static inline BOOL is_dir_data(const WIN32_FIND_DATAW *data) { static const WCHAR dotdotW[] = {'.','.',0}; static const WCHAR dotW[] = {'.',0}; return (data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && wcscmp(data->cFileName, dotdotW) && wcscmp(data->cFileName, dotW); } static inline BOOL is_file_data(const WIN32_FIND_DATAW *data) { return !(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); } static BSTR get_full_path(BSTR path, const WIN32_FIND_DATAW *data) { int len = SysStringLen(path); WCHAR buffW[MAX_PATH]; lstrcpyW(buffW, path); if (path[len-1] != '\\') lstrcatW(buffW, bsW); lstrcatW(buffW, data->cFileName); return SysAllocString(buffW); } static BOOL textstream_check_iomode(struct textstream *This, enum iotype type) { if (type == IORead) return This->mode == ForWriting || This->mode == ForAppending; else return This->mode == ForReading; } static HRESULT WINAPI textstream_QueryInterface(ITextStream *iface, REFIID riid, void **obj) { struct textstream *This = impl_from_ITextStream(iface); TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); if (IsEqualIID(riid, &IID_ITextStream) || IsEqualIID(riid, &IID_IDispatch) || IsEqualIID(riid, &IID_IUnknown)) { *obj = &This->ITextStream_iface; } else if (IsEqualIID(riid, &IID_IProvideClassInfo)) { *obj = &This->classinfo.IProvideClassInfo_iface; } else return E_NOINTERFACE; IUnknown_AddRef((IUnknown*)*obj); return S_OK; } static ULONG WINAPI textstream_AddRef(ITextStream *iface) { struct textstream *This = impl_from_ITextStream(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); return ref; } static ULONG WINAPI textstream_Release(ITextStream *iface) { struct textstream *This = impl_from_ITextStream(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); if (!ref) { if (This->read_buf_size) heap_free(This->read_buf); CloseHandle(This->file); heap_free(This); } return ref; } static HRESULT WINAPI textstream_GetTypeInfoCount(ITextStream *iface, UINT *pctinfo) { struct textstream *This = impl_from_ITextStream(iface); TRACE("(%p)->(%p)\n", This, pctinfo); *pctinfo = 1; return S_OK; } static HRESULT WINAPI textstream_GetTypeInfo(ITextStream *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { struct textstream *This = impl_from_ITextStream(iface); TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); return get_typeinfo(ITextStream_tid, ppTInfo); } static HRESULT WINAPI textstream_GetIDsOfNames(ITextStream *iface, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { struct textstream *This = impl_from_ITextStream(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); hr = get_typeinfo(ITextStream_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI textstream_Invoke(ITextStream *iface, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { struct textstream *This = impl_from_ITextStream(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); hr = get_typeinfo(ITextStream_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI textstream_get_Line(ITextStream *iface, LONG *line) { struct textstream *This = impl_from_ITextStream(iface); FIXME("(%p)->(%p): stub\n", This, line); return E_NOTIMPL; } static HRESULT WINAPI textstream_get_Column(ITextStream *iface, LONG *column) { struct textstream *This = impl_from_ITextStream(iface); FIXME("(%p)->(%p): stub\n", This, column); return E_NOTIMPL; } static HRESULT WINAPI textstream_get_AtEndOfStream(ITextStream *iface, VARIANT_BOOL *eos) { struct textstream *This = impl_from_ITextStream(iface); TRACE("(%p)->(%p)\n", This, eos); if (!eos) return E_POINTER; if (textstream_check_iomode(This, IORead)) { *eos = VARIANT_TRUE; return CTL_E_BADFILEMODE; } *eos = (This->eof && !This->read_buf_size) ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } static HRESULT WINAPI textstream_get_AtEndOfLine(ITextStream *iface, VARIANT_BOOL *eol) { struct textstream *This = impl_from_ITextStream(iface); FIXME("(%p)->(%p): stub\n", This, eol); return E_NOTIMPL; } static HRESULT append_read_data(struct textstream *stream, const char *buf, size_t buf_size) { LARGE_INTEGER revert; size_t len; WCHAR *new_buf; revert.QuadPart = 0; if (stream->unicode) { len = buf_size / sizeof(WCHAR); if (buf_size & 1) revert.QuadPart = -1; } else { for (len = 0; len < buf_size; len++) { if (!IsDBCSLeadByte(buf[len])) continue; if (len + 1 == buf_size) { revert.QuadPart = -1; buf_size--; break; } len++; } len = MultiByteToWideChar(CP_ACP, 0, buf, buf_size, NULL, 0); } if (!len) return S_OK; if (revert.QuadPart) SetFilePointerEx(stream->file, revert, NULL, FILE_CURRENT); if (!stream->read_buf_size) new_buf = heap_alloc(len * sizeof(WCHAR)); else new_buf = heap_realloc(stream->read_buf, (len + stream->read_buf_size) * sizeof(WCHAR)); if (!new_buf) return E_OUTOFMEMORY; if (stream->unicode) memcpy(new_buf + stream->read_buf_size, buf, len * sizeof(WCHAR)); else MultiByteToWideChar(CP_ACP, 0, buf, buf_size, new_buf + stream->read_buf_size, len); stream->read_buf = new_buf; stream->read_buf_size += len; return S_OK; } static HRESULT read_more_data(struct textstream *stream) { char buf[256]; DWORD read; if (stream->eof) return S_OK; if (!ReadFile(stream->file, buf, sizeof(buf), &read, NULL)) { ITextStream_Release(&stream->ITextStream_iface); return create_error(GetLastError()); } stream->eof = read != sizeof(buf); return append_read_data(stream, buf, read); } static BOOL read_from_buffer(struct textstream *stream, size_t len, BSTR *ret, size_t skip) { assert(len + skip <= stream->read_buf_size); if (!(*ret = SysAllocStringLen(stream->read_buf, len))) return FALSE; len += skip; stream->read_buf_size -= len; if (stream->read_buf_size) memmove(stream->read_buf, stream->read_buf + len, stream->read_buf_size * sizeof(WCHAR)); else heap_free(stream->read_buf); return TRUE; } static HRESULT WINAPI textstream_Read(ITextStream *iface, LONG len, BSTR *text) { struct textstream *This = impl_from_ITextStream(iface); HRESULT hr = S_OK; TRACE("(%p)->(%d %p)\n", This, len, text); if (!text) return E_POINTER; *text = NULL; if (len <= 0) return len == 0 ? S_OK : E_INVALIDARG; if (textstream_check_iomode(This, IORead)) return CTL_E_BADFILEMODE; while (!This->eof && len > This->read_buf_size) { if (FAILED(hr = read_more_data(This))) return hr; } if (This->eof && !This->read_buf_size) return CTL_E_ENDOFFILE; if (len > This->read_buf_size) { len = This->read_buf_size; hr = S_FALSE; } return read_from_buffer(This, len, text, 0) ? hr : E_OUTOFMEMORY; } static HRESULT WINAPI textstream_ReadLine(ITextStream *iface, BSTR *text) { struct textstream *This = impl_from_ITextStream(iface); unsigned int skip = 0; const WCHAR *nl; HRESULT hr = S_OK; TRACE("(%p)->(%p)\n", This, text); if (!text) return E_POINTER; *text = NULL; if (textstream_check_iomode(This, IORead)) return CTL_E_BADFILEMODE; while (!(nl = wmemchr(This->read_buf, '\n', This->read_buf_size)) && !This->eof) { if (FAILED(hr = read_more_data(This))) return hr; } if (This->eof && !This->read_buf_size) return CTL_E_ENDOFFILE; if (!nl) { nl = This->read_buf + This->read_buf_size; hr = S_FALSE; } else if (nl > This->read_buf && nl[-1] == '\r') { nl--; skip = 2; } else skip = 1; return read_from_buffer(This, nl - This->read_buf, text, skip) ? hr : E_OUTOFMEMORY; } static HRESULT WINAPI textstream_ReadAll(ITextStream *iface, BSTR *text) { struct textstream *This = impl_from_ITextStream(iface); HRESULT hr; TRACE("(%p)->(%p)\n", This, text); if (!text) return E_POINTER; *text = NULL; if (textstream_check_iomode(This, IORead)) return CTL_E_BADFILEMODE; while (!This->eof) { if (FAILED(hr = read_more_data(This))) return hr; } if (This->eof && !This->read_buf_size) return CTL_E_ENDOFFILE; return read_from_buffer(This, This->read_buf_size, text, 0) ? S_FALSE : E_OUTOFMEMORY; } static HRESULT textstream_writestr(struct textstream *stream, BSTR text) { DWORD written = 0; BOOL ret; if (stream->unicode) { ret = WriteFile(stream->file, text, SysStringByteLen(text), &written, NULL); return (ret && written == SysStringByteLen(text)) ? S_OK : create_error(GetLastError()); } else { DWORD len = WideCharToMultiByte(CP_ACP, 0, text, SysStringLen(text), NULL, 0, NULL, NULL); char *buffA; HRESULT hr; buffA = heap_alloc(len); if (!buffA) return E_OUTOFMEMORY; WideCharToMultiByte(CP_ACP, 0, text, SysStringLen(text), buffA, len, NULL, NULL); ret = WriteFile(stream->file, buffA, len, &written, NULL); hr = (ret && written == len) ? S_OK : create_error(GetLastError()); heap_free(buffA); return hr; } } static HRESULT WINAPI textstream_Write(ITextStream *iface, BSTR text) { struct textstream *This = impl_from_ITextStream(iface); TRACE("(%p)->(%s)\n", This, debugstr_w(text)); if (textstream_check_iomode(This, IOWrite)) return CTL_E_BADFILEMODE; return textstream_writestr(This, text); } static HRESULT textstream_writecrlf(struct textstream *stream) { static const WCHAR crlfW[] = {'\r','\n'}; static const char crlfA[] = {'\r','\n'}; DWORD written = 0, len; const void *ptr; BOOL ret; if (stream->unicode) { ptr = crlfW; len = sizeof(crlfW); } else { ptr = crlfA; len = sizeof(crlfA); } ret = WriteFile(stream->file, ptr, len, &written, NULL); return (ret && written == len) ? S_OK : create_error(GetLastError()); } static HRESULT WINAPI textstream_WriteLine(ITextStream *iface, BSTR text) { struct textstream *This = impl_from_ITextStream(iface); HRESULT hr; TRACE("(%p)->(%s)\n", This, debugstr_w(text)); if (textstream_check_iomode(This, IOWrite)) return CTL_E_BADFILEMODE; hr = textstream_writestr(This, text); if (SUCCEEDED(hr)) hr = textstream_writecrlf(This); return hr; } static HRESULT WINAPI textstream_WriteBlankLines(ITextStream *iface, LONG lines) { struct textstream *This = impl_from_ITextStream(iface); FIXME("(%p)->(%d): stub\n", This, lines); return E_NOTIMPL; } static HRESULT WINAPI textstream_Skip(ITextStream *iface, LONG count) { struct textstream *This = impl_from_ITextStream(iface); FIXME("(%p)->(%d): stub\n", This, count); return E_NOTIMPL; } static HRESULT WINAPI textstream_SkipLine(ITextStream *iface) { struct textstream *This = impl_from_ITextStream(iface); FIXME("(%p): stub\n", This); return E_NOTIMPL; } static HRESULT WINAPI textstream_Close(ITextStream *iface) { struct textstream *This = impl_from_ITextStream(iface); HRESULT hr = S_OK; TRACE("(%p)\n", This); if(!CloseHandle(This->file)) hr = S_FALSE; This->file = NULL; return hr; } static const ITextStreamVtbl textstreamvtbl = { textstream_QueryInterface, textstream_AddRef, textstream_Release, textstream_GetTypeInfoCount, textstream_GetTypeInfo, textstream_GetIDsOfNames, textstream_Invoke, textstream_get_Line, textstream_get_Column, textstream_get_AtEndOfStream, textstream_get_AtEndOfLine, textstream_Read, textstream_ReadLine, textstream_ReadAll, textstream_Write, textstream_WriteLine, textstream_WriteBlankLines, textstream_Skip, textstream_SkipLine, textstream_Close }; static HRESULT create_textstream(const WCHAR *filename, DWORD disposition, IOMode mode, Tristate format, ITextStream **ret) { struct textstream *stream; DWORD access = 0; HRESULT hr; /* map access mode */ switch (mode) { case ForReading: access = GENERIC_READ; break; case ForWriting: access = GENERIC_WRITE; break; case ForAppending: access = GENERIC_READ | GENERIC_WRITE; break; default: return E_INVALIDARG; } stream = heap_alloc(sizeof(struct textstream)); if (!stream) return E_OUTOFMEMORY; stream->ITextStream_iface.lpVtbl = &textstreamvtbl; stream->ref = 1; stream->mode = mode; stream->eof = FALSE; stream->read_buf = NULL; stream->read_buf_size = 0; stream->file = CreateFileW(filename, access, 0, NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL); if (stream->file == INVALID_HANDLE_VALUE) { HRESULT hr = create_error(GetLastError()); heap_free(stream); return hr; } if (mode == ForReading) GetFileSizeEx(stream->file, &stream->size); else stream->size.QuadPart = 0; if (mode == ForWriting) { stream->unicode = format == TristateTrue; /* Write Unicode BOM */ if (stream->unicode && (disposition == CREATE_ALWAYS || disposition == CREATE_NEW)) { DWORD written = 0; BOOL ret = WriteFile(stream->file, &utf16bom, sizeof(utf16bom), &written, NULL); if (!ret || written != sizeof(utf16bom)) { ITextStream_Release(&stream->ITextStream_iface); return create_error(GetLastError()); } } } else { DWORD read, buf_offset = 0; BYTE buf[64]; if (format == TristateUseDefault || mode == ForReading) { if (!ReadFile(stream->file, buf, sizeof(buf), &read, NULL)) { ITextStream_Release(&stream->ITextStream_iface); return create_error(GetLastError()); } } if (format == TristateUseDefault) stream->unicode = IsTextUnicode(buf, read, NULL); else stream->unicode = format != TristateFalse; if (mode == ForReading) { if (stream->unicode && read >= 2 && buf[0] == 0xff && buf[1] == 0xfe) buf_offset += 2; /* skip utf16 BOM */ hr = append_read_data(stream, (const char *)buf + buf_offset, read - buf_offset); if (FAILED(hr)) { ITextStream_Release(&stream->ITextStream_iface); return hr; } stream->eof = read != sizeof(buf); } else SetFilePointer(stream->file, 0, 0, FILE_END); } init_classinfo(&CLSID_TextStream, (IUnknown *)&stream->ITextStream_iface, &stream->classinfo); *ret = &stream->ITextStream_iface; return S_OK; } static HRESULT WINAPI drive_QueryInterface(IDrive *iface, REFIID riid, void **obj) { struct drive *This = impl_from_IDrive(iface); TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); *obj = NULL; if (IsEqualIID( riid, &IID_IDrive ) || IsEqualIID( riid, &IID_IDispatch ) || IsEqualIID( riid, &IID_IUnknown)) { *obj = &This->IDrive_iface; } else if (IsEqualIID( riid, &IID_IProvideClassInfo )) { *obj = &This->classinfo.IProvideClassInfo_iface; } else return E_NOINTERFACE; IUnknown_AddRef((IUnknown*)*obj); return S_OK; } static ULONG WINAPI drive_AddRef(IDrive *iface) { struct drive *This = impl_from_IDrive(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); return ref; } static ULONG WINAPI drive_Release(IDrive *iface) { struct drive *This = impl_from_IDrive(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); if (!ref) { SysFreeString(This->root); heap_free(This); } return ref; } static HRESULT WINAPI drive_GetTypeInfoCount(IDrive *iface, UINT *pctinfo) { struct drive *This = impl_from_IDrive(iface); TRACE("(%p)->(%p)\n", This, pctinfo); *pctinfo = 1; return S_OK; } static HRESULT WINAPI drive_GetTypeInfo(IDrive *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { struct drive *This = impl_from_IDrive(iface); TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); return get_typeinfo(IDrive_tid, ppTInfo); } static HRESULT WINAPI drive_GetIDsOfNames(IDrive *iface, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { struct drive *This = impl_from_IDrive(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); hr = get_typeinfo(IDrive_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI drive_Invoke(IDrive *iface, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { struct drive *This = impl_from_IDrive(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); hr = get_typeinfo(IDrive_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI drive_get_Path(IDrive *iface, BSTR *path) { struct drive *This = impl_from_IDrive(iface); FIXME("(%p)->(%p): stub\n", This, path); return E_NOTIMPL; } static HRESULT WINAPI drive_get_DriveLetter(IDrive *iface, BSTR *letter) { struct drive *This = impl_from_IDrive(iface); TRACE("(%p)->(%p)\n", This, letter); if (!letter) return E_POINTER; *letter = SysAllocStringLen(This->root, 1); if (!*letter) return E_OUTOFMEMORY; return S_OK; } static HRESULT WINAPI drive_get_ShareName(IDrive *iface, BSTR *share_name) { struct drive *This = impl_from_IDrive(iface); FIXME("(%p)->(%p): stub\n", This, share_name); return E_NOTIMPL; } static HRESULT WINAPI drive_get_DriveType(IDrive *iface, DriveTypeConst *type) { struct drive *This = impl_from_IDrive(iface); TRACE("(%p)->(%p)\n", This, type); switch (GetDriveTypeW(This->root)) { case DRIVE_REMOVABLE: *type = Removable; break; case DRIVE_FIXED: *type = Fixed; break; case DRIVE_REMOTE: *type = Remote; break; case DRIVE_CDROM: *type = CDRom; break; case DRIVE_RAMDISK: *type = RamDisk; break; default: *type = UnknownType; break; } return S_OK; } static HRESULT WINAPI drive_get_RootFolder(IDrive *iface, IFolder **folder) { struct drive *This = impl_from_IDrive(iface); FIXME("(%p)->(%p): stub\n", This, folder); return E_NOTIMPL; } static HRESULT variant_from_largeint(const ULARGE_INTEGER *src, VARIANT *v) { HRESULT hr = S_OK; if (src->u.HighPart || src->u.LowPart > INT_MAX) { V_VT(v) = VT_R8; hr = VarR8FromUI8(src->QuadPart, &V_R8(v)); } else { V_VT(v) = VT_I4; V_I4(v) = src->u.LowPart; } return hr; } static HRESULT WINAPI drive_get_AvailableSpace(IDrive *iface, VARIANT *v) { struct drive *This = impl_from_IDrive(iface); ULARGE_INTEGER avail; TRACE("(%p)->(%p)\n", This, v); if (!v) return E_POINTER; if (!GetDiskFreeSpaceExW(This->root, &avail, NULL, NULL)) return E_FAIL; return variant_from_largeint(&avail, v); } static HRESULT WINAPI drive_get_FreeSpace(IDrive *iface, VARIANT *v) { struct drive *This = impl_from_IDrive(iface); ULARGE_INTEGER freespace; TRACE("(%p)->(%p)\n", This, v); if (!v) return E_POINTER; if (!GetDiskFreeSpaceExW(This->root, &freespace, NULL, NULL)) return E_FAIL; return variant_from_largeint(&freespace, v); } static HRESULT WINAPI drive_get_TotalSize(IDrive *iface, VARIANT *v) { struct drive *This = impl_from_IDrive(iface); ULARGE_INTEGER total; TRACE("(%p)->(%p)\n", This, v); if (!v) return E_POINTER; if (!GetDiskFreeSpaceExW(This->root, NULL, &total, NULL)) return E_FAIL; return variant_from_largeint(&total, v); } static HRESULT WINAPI drive_get_VolumeName(IDrive *iface, BSTR *name) { struct drive *This = impl_from_IDrive(iface); WCHAR nameW[MAX_PATH+1]; BOOL ret; TRACE("(%p)->(%p)\n", This, name); if (!name) return E_POINTER; *name = NULL; ret = GetVolumeInformationW(This->root, nameW, ARRAY_SIZE(nameW), NULL, NULL, NULL, NULL, 0); if (ret) *name = SysAllocString(nameW); return ret ? S_OK : E_FAIL; } static HRESULT WINAPI drive_put_VolumeName(IDrive *iface, BSTR name) { struct drive *This = impl_from_IDrive(iface); FIXME("(%p)->(%s): stub\n", This, debugstr_w(name)); return E_NOTIMPL; } static HRESULT WINAPI drive_get_FileSystem(IDrive *iface, BSTR *fs) { struct drive *This = impl_from_IDrive(iface); WCHAR nameW[MAX_PATH+1]; BOOL ret; TRACE("(%p)->(%p)\n", This, fs); if (!fs) return E_POINTER; *fs = NULL; ret = GetVolumeInformationW(This->root, NULL, 0, NULL, NULL, NULL, nameW, ARRAY_SIZE(nameW)); if (ret) *fs = SysAllocString(nameW); return ret ? S_OK : E_FAIL; } static HRESULT WINAPI drive_get_SerialNumber(IDrive *iface, LONG *serial) { struct drive *This = impl_from_IDrive(iface); BOOL ret; TRACE("(%p)->(%p)\n", This, serial); if (!serial) return E_POINTER; ret = GetVolumeInformationW(This->root, NULL, 0, (DWORD*)serial, NULL, NULL, NULL, 0); return ret ? S_OK : E_FAIL; } static HRESULT WINAPI drive_get_IsReady(IDrive *iface, VARIANT_BOOL *ready) { struct drive *This = impl_from_IDrive(iface); ULARGE_INTEGER freespace; BOOL ret; TRACE("(%p)->(%p)\n", This, ready); if (!ready) return E_POINTER; ret = GetDiskFreeSpaceExW(This->root, &freespace, NULL, NULL); *ready = ret ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } static const IDriveVtbl drivevtbl = { drive_QueryInterface, drive_AddRef, drive_Release, drive_GetTypeInfoCount, drive_GetTypeInfo, drive_GetIDsOfNames, drive_Invoke, drive_get_Path, drive_get_DriveLetter, drive_get_ShareName, drive_get_DriveType, drive_get_RootFolder, drive_get_AvailableSpace, drive_get_FreeSpace, drive_get_TotalSize, drive_get_VolumeName, drive_put_VolumeName, drive_get_FileSystem, drive_get_SerialNumber, drive_get_IsReady }; static HRESULT create_drive(WCHAR letter, IDrive **drive) { struct drive *This; *drive = NULL; This = heap_alloc(sizeof(*This)); if (!This) return E_OUTOFMEMORY; This->IDrive_iface.lpVtbl = &drivevtbl; This->ref = 1; This->root = SysAllocStringLen(NULL, 3); if (!This->root) { heap_free(This); return E_OUTOFMEMORY; } This->root[0] = letter; This->root[1] = ':'; This->root[2] = '\\'; This->root[3] = 0; init_classinfo(&CLSID_Drive, (IUnknown *)&This->IDrive_iface, &This->classinfo); *drive = &This->IDrive_iface; return S_OK; } static HRESULT WINAPI enumvariant_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **obj) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); *obj = NULL; if (IsEqualIID( riid, &IID_IEnumVARIANT ) || IsEqualIID( riid, &IID_IUnknown )) { *obj = iface; IEnumVARIANT_AddRef(iface); } else return E_NOINTERFACE; return S_OK; } static ULONG WINAPI enumvariant_AddRef(IEnumVARIANT *iface) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); return ref; } static ULONG WINAPI foldercoll_enumvariant_Release(IEnumVARIANT *iface) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); if (!ref) { IFolderCollection_Release(&This->data.u.foldercoll.coll->IFolderCollection_iface); FindClose(This->data.u.foldercoll.find); heap_free(This); } return ref; } static HANDLE start_enumeration(const WCHAR *path, WIN32_FIND_DATAW *data, BOOL file) { static const WCHAR allW[] = {'*',0}; WCHAR pathW[MAX_PATH]; int len; HANDLE handle; lstrcpyW(pathW, path); len = lstrlenW(pathW); if (len && pathW[len-1] != '\\') lstrcatW(pathW, bsW); lstrcatW(pathW, allW); handle = FindFirstFileW(pathW, data); if (handle == INVALID_HANDLE_VALUE) return 0; /* find first dir/file */ while (1) { if (file ? is_file_data(data) : is_dir_data(data)) break; if (!FindNextFileW(handle, data)) { FindClose(handle); return 0; } } return handle; } static HRESULT WINAPI foldercoll_enumvariant_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *var, ULONG *fetched) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); HANDLE handle = This->data.u.foldercoll.find; WIN32_FIND_DATAW data; ULONG count = 0; TRACE("(%p)->(%d %p %p)\n", This, celt, var, fetched); if (fetched) *fetched = 0; if (!celt) return S_OK; if (!handle) { handle = start_enumeration(This->data.u.foldercoll.coll->path, &data, FALSE); if (!handle) return S_FALSE; This->data.u.foldercoll.find = handle; } else { if (!FindNextFileW(handle, &data)) return S_FALSE; } do { if (is_dir_data(&data)) { IFolder *folder; HRESULT hr; BSTR str; str = get_full_path(This->data.u.foldercoll.coll->path, &data); hr = create_folder(str, &folder); SysFreeString(str); if (FAILED(hr)) return hr; V_VT(&var[count]) = VT_DISPATCH; V_DISPATCH(&var[count]) = (IDispatch*)folder; count++; if (count >= celt) break; } } while (FindNextFileW(handle, &data)); if (fetched) *fetched = count; return (count < celt) ? S_FALSE : S_OK; } static HRESULT WINAPI foldercoll_enumvariant_Skip(IEnumVARIANT *iface, ULONG celt) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); HANDLE handle = This->data.u.foldercoll.find; WIN32_FIND_DATAW data; TRACE("(%p)->(%d)\n", This, celt); if (!celt) return S_OK; if (!handle) { handle = start_enumeration(This->data.u.foldercoll.coll->path, &data, FALSE); if (!handle) return S_FALSE; This->data.u.foldercoll.find = handle; } else { if (!FindNextFileW(handle, &data)) return S_FALSE; } do { if (is_dir_data(&data)) --celt; if (!celt) break; } while (FindNextFileW(handle, &data)); return celt ? S_FALSE : S_OK; } static HRESULT WINAPI foldercoll_enumvariant_Reset(IEnumVARIANT *iface) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); TRACE("(%p)\n", This); FindClose(This->data.u.foldercoll.find); This->data.u.foldercoll.find = NULL; return S_OK; } static HRESULT WINAPI foldercoll_enumvariant_Clone(IEnumVARIANT *iface, IEnumVARIANT **pclone) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); TRACE("(%p)->(%p)\n", This, pclone); return create_foldercoll_enum(This->data.u.foldercoll.coll, (IUnknown**)pclone); } static const IEnumVARIANTVtbl foldercollenumvariantvtbl = { enumvariant_QueryInterface, enumvariant_AddRef, foldercoll_enumvariant_Release, foldercoll_enumvariant_Next, foldercoll_enumvariant_Skip, foldercoll_enumvariant_Reset, foldercoll_enumvariant_Clone }; static HRESULT create_foldercoll_enum(struct foldercollection *collection, IUnknown **newenum) { struct enumvariant *This; *newenum = NULL; This = heap_alloc(sizeof(*This)); if (!This) return E_OUTOFMEMORY; This->IEnumVARIANT_iface.lpVtbl = &foldercollenumvariantvtbl; This->ref = 1; This->data.u.foldercoll.find = NULL; This->data.u.foldercoll.coll = collection; IFolderCollection_AddRef(&collection->IFolderCollection_iface); *newenum = (IUnknown*)&This->IEnumVARIANT_iface; return S_OK; } static ULONG WINAPI filecoll_enumvariant_Release(IEnumVARIANT *iface) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); if (!ref) { IFileCollection_Release(&This->data.u.filecoll.coll->IFileCollection_iface); FindClose(This->data.u.filecoll.find); heap_free(This); } return ref; } static HRESULT WINAPI filecoll_enumvariant_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *var, ULONG *fetched) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); HANDLE handle = This->data.u.filecoll.find; WIN32_FIND_DATAW data; ULONG count = 0; TRACE("(%p)->(%d %p %p)\n", This, celt, var, fetched); if (fetched) *fetched = 0; if (!celt) return S_OK; if (!handle) { handle = start_enumeration(This->data.u.filecoll.coll->path, &data, TRUE); if (!handle) return S_FALSE; This->data.u.filecoll.find = handle; } else if (!FindNextFileW(handle, &data)) return S_FALSE; do { if (is_file_data(&data)) { IFile *file; HRESULT hr; BSTR str; str = get_full_path(This->data.u.filecoll.coll->path, &data); hr = create_file(str, &file); SysFreeString(str); if (FAILED(hr)) return hr; V_VT(&var[count]) = VT_DISPATCH; V_DISPATCH(&var[count]) = (IDispatch*)file; if (++count >= celt) break; } } while (FindNextFileW(handle, &data)); if (fetched) *fetched = count; return (count < celt) ? S_FALSE : S_OK; } static HRESULT WINAPI filecoll_enumvariant_Skip(IEnumVARIANT *iface, ULONG celt) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); HANDLE handle = This->data.u.filecoll.find; WIN32_FIND_DATAW data; TRACE("(%p)->(%d)\n", This, celt); if (!celt) return S_OK; if (!handle) { handle = start_enumeration(This->data.u.filecoll.coll->path, &data, TRUE); if (!handle) return S_FALSE; This->data.u.filecoll.find = handle; } else if (!FindNextFileW(handle, &data)) return S_FALSE; do { if (is_file_data(&data)) --celt; } while (celt && FindNextFileW(handle, &data)); return celt ? S_FALSE : S_OK; } static HRESULT WINAPI filecoll_enumvariant_Reset(IEnumVARIANT *iface) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); TRACE("(%p)\n", This); FindClose(This->data.u.filecoll.find); This->data.u.filecoll.find = NULL; return S_OK; } static HRESULT WINAPI filecoll_enumvariant_Clone(IEnumVARIANT *iface, IEnumVARIANT **pclone) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); TRACE("(%p)->(%p)\n", This, pclone); return create_filecoll_enum(This->data.u.filecoll.coll, (IUnknown**)pclone); } static const IEnumVARIANTVtbl filecollenumvariantvtbl = { enumvariant_QueryInterface, enumvariant_AddRef, filecoll_enumvariant_Release, filecoll_enumvariant_Next, filecoll_enumvariant_Skip, filecoll_enumvariant_Reset, filecoll_enumvariant_Clone }; static HRESULT create_filecoll_enum(struct filecollection *collection, IUnknown **newenum) { struct enumvariant *This; *newenum = NULL; This = heap_alloc(sizeof(*This)); if (!This) return E_OUTOFMEMORY; This->IEnumVARIANT_iface.lpVtbl = &filecollenumvariantvtbl; This->ref = 1; This->data.u.filecoll.find = NULL; This->data.u.filecoll.coll = collection; IFileCollection_AddRef(&collection->IFileCollection_iface); *newenum = (IUnknown*)&This->IEnumVARIANT_iface; return S_OK; } static ULONG WINAPI drivecoll_enumvariant_Release(IEnumVARIANT *iface) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); if (!ref) { IDriveCollection_Release(&This->data.u.drivecoll.coll->IDriveCollection_iface); heap_free(This); } return ref; } static HRESULT find_next_drive(struct enumvariant *penum) { int i = penum->data.u.drivecoll.cur == -1 ? 0 : penum->data.u.drivecoll.cur + 1; for (; i < 32; i++) if (penum->data.u.drivecoll.coll->drives & (1 << i)) { penum->data.u.drivecoll.cur = i; return S_OK; } return S_FALSE; } static HRESULT WINAPI drivecoll_enumvariant_Next(IEnumVARIANT *iface, ULONG celt, VARIANT *var, ULONG *fetched) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); ULONG count = 0; TRACE("(%p)->(%d %p %p)\n", This, celt, var, fetched); if (fetched) *fetched = 0; if (!celt) return S_OK; while (find_next_drive(This) == S_OK) { IDrive *drive; HRESULT hr; hr = create_drive('A' + This->data.u.drivecoll.cur, &drive); if (FAILED(hr)) return hr; V_VT(&var[count]) = VT_DISPATCH; V_DISPATCH(&var[count]) = (IDispatch*)drive; if (++count >= celt) break; } if (fetched) *fetched = count; return (count < celt) ? S_FALSE : S_OK; } static HRESULT WINAPI drivecoll_enumvariant_Skip(IEnumVARIANT *iface, ULONG celt) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); TRACE("(%p)->(%d)\n", This, celt); if (!celt) return S_OK; while (celt && find_next_drive(This) == S_OK) celt--; return celt ? S_FALSE : S_OK; } static HRESULT WINAPI drivecoll_enumvariant_Reset(IEnumVARIANT *iface) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); TRACE("(%p)\n", This); This->data.u.drivecoll.cur = -1; return S_OK; } static HRESULT WINAPI drivecoll_enumvariant_Clone(IEnumVARIANT *iface, IEnumVARIANT **pclone) { struct enumvariant *This = impl_from_IEnumVARIANT(iface); TRACE("(%p)->(%p)\n", This, pclone); return create_drivecoll_enum(This->data.u.drivecoll.coll, (IUnknown**)pclone); } static const IEnumVARIANTVtbl drivecollenumvariantvtbl = { enumvariant_QueryInterface, enumvariant_AddRef, drivecoll_enumvariant_Release, drivecoll_enumvariant_Next, drivecoll_enumvariant_Skip, drivecoll_enumvariant_Reset, drivecoll_enumvariant_Clone }; static HRESULT create_drivecoll_enum(struct drivecollection *collection, IUnknown **newenum) { struct enumvariant *This; *newenum = NULL; This = heap_alloc(sizeof(*This)); if (!This) return E_OUTOFMEMORY; This->IEnumVARIANT_iface.lpVtbl = &drivecollenumvariantvtbl; This->ref = 1; This->data.u.drivecoll.coll = collection; This->data.u.drivecoll.cur = -1; IDriveCollection_AddRef(&collection->IDriveCollection_iface); *newenum = (IUnknown*)&This->IEnumVARIANT_iface; return S_OK; } static HRESULT WINAPI foldercoll_QueryInterface(IFolderCollection *iface, REFIID riid, void **obj) { struct foldercollection *This = impl_from_IFolderCollection(iface); TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); *obj = NULL; if (IsEqualIID( riid, &IID_IFolderCollection ) || IsEqualIID( riid, &IID_IDispatch ) || IsEqualIID( riid, &IID_IUnknown )) { *obj = &This->IFolderCollection_iface; } else if (IsEqualIID( riid, &IID_IProvideClassInfo )) { *obj = &This->classinfo.IProvideClassInfo_iface; } else return E_NOINTERFACE; IUnknown_AddRef((IUnknown*)*obj); return S_OK; } static ULONG WINAPI foldercoll_AddRef(IFolderCollection *iface) { struct foldercollection *This = impl_from_IFolderCollection(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); return ref; } static ULONG WINAPI foldercoll_Release(IFolderCollection *iface) { struct foldercollection *This = impl_from_IFolderCollection(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); if (!ref) { SysFreeString(This->path); heap_free(This); } return ref; } static HRESULT WINAPI foldercoll_GetTypeInfoCount(IFolderCollection *iface, UINT *pctinfo) { struct foldercollection *This = impl_from_IFolderCollection(iface); TRACE("(%p)->(%p)\n", This, pctinfo); *pctinfo = 1; return S_OK; } static HRESULT WINAPI foldercoll_GetTypeInfo(IFolderCollection *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { struct foldercollection *This = impl_from_IFolderCollection(iface); TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); return get_typeinfo(IFolderCollection_tid, ppTInfo); } static HRESULT WINAPI foldercoll_GetIDsOfNames(IFolderCollection *iface, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { struct foldercollection *This = impl_from_IFolderCollection(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); hr = get_typeinfo(IFolderCollection_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI foldercoll_Invoke(IFolderCollection *iface, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { struct foldercollection *This = impl_from_IFolderCollection(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); hr = get_typeinfo(IFolderCollection_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI foldercoll_Add(IFolderCollection *iface, BSTR name, IFolder **folder) { struct foldercollection *This = impl_from_IFolderCollection(iface); FIXME("(%p)->(%s %p): stub\n", This, debugstr_w(name), folder); return E_NOTIMPL; } static HRESULT WINAPI foldercoll_get_Item(IFolderCollection *iface, VARIANT key, IFolder **folder) { struct foldercollection *This = impl_from_IFolderCollection(iface); FIXME("(%p)->(%p): stub\n", This, folder); return E_NOTIMPL; } static HRESULT WINAPI foldercoll_get__NewEnum(IFolderCollection *iface, IUnknown **newenum) { struct foldercollection *This = impl_from_IFolderCollection(iface); TRACE("(%p)->(%p)\n", This, newenum); if(!newenum) return E_POINTER; return create_foldercoll_enum(This, newenum); } static HRESULT WINAPI foldercoll_get_Count(IFolderCollection *iface, LONG *count) { struct foldercollection *This = impl_from_IFolderCollection(iface); static const WCHAR allW[] = {'\\','*',0}; WIN32_FIND_DATAW data; WCHAR pathW[MAX_PATH]; HANDLE handle; TRACE("(%p)->(%p)\n", This, count); if(!count) return E_POINTER; *count = 0; lstrcpyW(pathW, This->path); lstrcatW(pathW, allW); handle = FindFirstFileW(pathW, &data); if (handle == INVALID_HANDLE_VALUE) return HRESULT_FROM_WIN32(GetLastError()); do { if (is_dir_data(&data)) *count += 1; } while (FindNextFileW(handle, &data)); FindClose(handle); return S_OK; } static const IFolderCollectionVtbl foldercollvtbl = { foldercoll_QueryInterface, foldercoll_AddRef, foldercoll_Release, foldercoll_GetTypeInfoCount, foldercoll_GetTypeInfo, foldercoll_GetIDsOfNames, foldercoll_Invoke, foldercoll_Add, foldercoll_get_Item, foldercoll_get__NewEnum, foldercoll_get_Count }; static HRESULT create_foldercoll(BSTR path, IFolderCollection **folders) { struct foldercollection *This; *folders = NULL; This = heap_alloc(sizeof(struct foldercollection)); if (!This) return E_OUTOFMEMORY; This->IFolderCollection_iface.lpVtbl = &foldercollvtbl; This->ref = 1; This->path = SysAllocString(path); if (!This->path) { heap_free(This); return E_OUTOFMEMORY; } init_classinfo(&CLSID_Folders, (IUnknown *)&This->IFolderCollection_iface, &This->classinfo); *folders = &This->IFolderCollection_iface; return S_OK; } static HRESULT WINAPI filecoll_QueryInterface(IFileCollection *iface, REFIID riid, void **obj) { struct filecollection *This = impl_from_IFileCollection(iface); TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); *obj = NULL; if (IsEqualIID( riid, &IID_IFileCollection ) || IsEqualIID( riid, &IID_IDispatch ) || IsEqualIID( riid, &IID_IUnknown )) { *obj = &This->IFileCollection_iface; } else if (IsEqualIID( riid, &IID_IProvideClassInfo )) { *obj = &This->classinfo.IProvideClassInfo_iface; } else return E_NOINTERFACE; IUnknown_AddRef((IUnknown*)*obj); return S_OK; } static ULONG WINAPI filecoll_AddRef(IFileCollection *iface) { struct filecollection *This = impl_from_IFileCollection(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); return ref; } static ULONG WINAPI filecoll_Release(IFileCollection *iface) { struct filecollection *This = impl_from_IFileCollection(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); if (!ref) { SysFreeString(This->path); heap_free(This); } return ref; } static HRESULT WINAPI filecoll_GetTypeInfoCount(IFileCollection *iface, UINT *pctinfo) { struct filecollection *This = impl_from_IFileCollection(iface); TRACE("(%p)->(%p)\n", This, pctinfo); *pctinfo = 1; return S_OK; } static HRESULT WINAPI filecoll_GetTypeInfo(IFileCollection *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { struct filecollection *This = impl_from_IFileCollection(iface); TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); return get_typeinfo(IFileCollection_tid, ppTInfo); } static HRESULT WINAPI filecoll_GetIDsOfNames(IFileCollection *iface, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { struct filecollection *This = impl_from_IFileCollection(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); hr = get_typeinfo(IFileCollection_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI filecoll_Invoke(IFileCollection *iface, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { struct filecollection *This = impl_from_IFileCollection(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); hr = get_typeinfo(IFileCollection_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI filecoll_get_Item(IFileCollection *iface, VARIANT Key, IFile **file) { struct filecollection *This = impl_from_IFileCollection(iface); FIXME("(%p)->(%p)\n", This, file); return E_NOTIMPL; } static HRESULT WINAPI filecoll_get__NewEnum(IFileCollection *iface, IUnknown **ppenum) { struct filecollection *This = impl_from_IFileCollection(iface); TRACE("(%p)->(%p)\n", This, ppenum); if(!ppenum) return E_POINTER; return create_filecoll_enum(This, ppenum); } static HRESULT WINAPI filecoll_get_Count(IFileCollection *iface, LONG *count) { struct filecollection *This = impl_from_IFileCollection(iface); static const WCHAR allW[] = {'\\','*',0}; WIN32_FIND_DATAW data; WCHAR pathW[MAX_PATH]; HANDLE handle; TRACE("(%p)->(%p)\n", This, count); if(!count) return E_POINTER; *count = 0; lstrcpyW(pathW, This->path); lstrcatW(pathW, allW); handle = FindFirstFileW(pathW, &data); if (handle == INVALID_HANDLE_VALUE) return HRESULT_FROM_WIN32(GetLastError()); do { if (is_file_data(&data)) *count += 1; } while (FindNextFileW(handle, &data)); FindClose(handle); return S_OK; } static const IFileCollectionVtbl filecollectionvtbl = { filecoll_QueryInterface, filecoll_AddRef, filecoll_Release, filecoll_GetTypeInfoCount, filecoll_GetTypeInfo, filecoll_GetIDsOfNames, filecoll_Invoke, filecoll_get_Item, filecoll_get__NewEnum, filecoll_get_Count }; static HRESULT create_filecoll(BSTR path, IFileCollection **files) { struct filecollection *This; *files = NULL; This = heap_alloc(sizeof(*This)); if (!This) return E_OUTOFMEMORY; This->IFileCollection_iface.lpVtbl = &filecollectionvtbl; This->ref = 1; This->path = SysAllocString(path); if (!This->path) { heap_free(This); return E_OUTOFMEMORY; } init_classinfo(&CLSID_Files, (IUnknown *)&This->IFileCollection_iface, &This->classinfo); *files = &This->IFileCollection_iface; return S_OK; } static HRESULT WINAPI drivecoll_QueryInterface(IDriveCollection *iface, REFIID riid, void **obj) { struct drivecollection *This = impl_from_IDriveCollection(iface); TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); *obj = NULL; if (IsEqualIID( riid, &IID_IDriveCollection ) || IsEqualIID( riid, &IID_IDispatch ) || IsEqualIID( riid, &IID_IUnknown )) { *obj = &This->IDriveCollection_iface; } else if (IsEqualIID( riid, &IID_IProvideClassInfo )) { *obj = &This->classinfo.IProvideClassInfo_iface; } else return E_NOINTERFACE; IUnknown_AddRef((IUnknown*)*obj); return S_OK; } static ULONG WINAPI drivecoll_AddRef(IDriveCollection *iface) { struct drivecollection *This = impl_from_IDriveCollection(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); return ref; } static ULONG WINAPI drivecoll_Release(IDriveCollection *iface) { struct drivecollection *This = impl_from_IDriveCollection(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); if (!ref) heap_free(This); return ref; } static HRESULT WINAPI drivecoll_GetTypeInfoCount(IDriveCollection *iface, UINT *pctinfo) { struct drivecollection *This = impl_from_IDriveCollection(iface); TRACE("(%p)->(%p)\n", This, pctinfo); *pctinfo = 1; return S_OK; } static HRESULT WINAPI drivecoll_GetTypeInfo(IDriveCollection *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { struct drivecollection *This = impl_from_IDriveCollection(iface); TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); return get_typeinfo(IDriveCollection_tid, ppTInfo); } static HRESULT WINAPI drivecoll_GetIDsOfNames(IDriveCollection *iface, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { struct drivecollection *This = impl_from_IDriveCollection(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); hr = get_typeinfo(IDriveCollection_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI drivecoll_Invoke(IDriveCollection *iface, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { struct drivecollection *This = impl_from_IDriveCollection(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); hr = get_typeinfo(IDriveCollection_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI drivecoll_get_Item(IDriveCollection *iface, VARIANT key, IDrive **drive) { struct drivecollection *This = impl_from_IDriveCollection(iface); FIXME("(%p)->(%p): stub\n", This, drive); return E_NOTIMPL; } static HRESULT WINAPI drivecoll_get__NewEnum(IDriveCollection *iface, IUnknown **ppenum) { struct drivecollection *This = impl_from_IDriveCollection(iface); TRACE("(%p)->(%p)\n", This, ppenum); if(!ppenum) return E_POINTER; return create_drivecoll_enum(This, ppenum); } static HRESULT WINAPI drivecoll_get_Count(IDriveCollection *iface, LONG *count) { struct drivecollection *This = impl_from_IDriveCollection(iface); TRACE("(%p)->(%p)\n", This, count); if (!count) return E_POINTER; *count = This->count; return S_OK; } static const IDriveCollectionVtbl drivecollectionvtbl = { drivecoll_QueryInterface, drivecoll_AddRef, drivecoll_Release, drivecoll_GetTypeInfoCount, drivecoll_GetTypeInfo, drivecoll_GetIDsOfNames, drivecoll_Invoke, drivecoll_get_Item, drivecoll_get__NewEnum, drivecoll_get_Count }; static HRESULT create_drivecoll(IDriveCollection **drives) { struct drivecollection *This; DWORD mask; *drives = NULL; This = heap_alloc(sizeof(*This)); if (!This) return E_OUTOFMEMORY; This->IDriveCollection_iface.lpVtbl = &drivecollectionvtbl; This->ref = 1; This->drives = mask = GetLogicalDrives(); /* count set bits */ for (This->count = 0; mask; This->count++) mask &= mask - 1; init_classinfo(&CLSID_Drives, (IUnknown *)&This->IDriveCollection_iface, &This->classinfo); *drives = &This->IDriveCollection_iface; return S_OK; } static HRESULT WINAPI folder_QueryInterface(IFolder *iface, REFIID riid, void **obj) { struct folder *This = impl_from_IFolder(iface); TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); *obj = NULL; if (IsEqualIID( riid, &IID_IFolder ) || IsEqualIID( riid, &IID_IDispatch ) || IsEqualIID( riid, &IID_IUnknown)) { *obj = &This->IFolder_iface; } else if (IsEqualIID( riid, &IID_IProvideClassInfo )) { *obj = &This->classinfo.IProvideClassInfo_iface; } else return E_NOINTERFACE; IUnknown_AddRef((IUnknown*)*obj); return S_OK; } static ULONG WINAPI folder_AddRef(IFolder *iface) { struct folder *This = impl_from_IFolder(iface); ULONG ref = InterlockedIncrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); return ref; } static ULONG WINAPI folder_Release(IFolder *iface) { struct folder *This = impl_from_IFolder(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p)->(%d)\n", This, ref); if (!ref) { SysFreeString(This->path); heap_free(This); } return ref; } static HRESULT WINAPI folder_GetTypeInfoCount(IFolder *iface, UINT *pctinfo) { struct folder *This = impl_from_IFolder(iface); TRACE("(%p)->(%p)\n", This, pctinfo); *pctinfo = 1; return S_OK; } static HRESULT WINAPI folder_GetTypeInfo(IFolder *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { struct folder *This = impl_from_IFolder(iface); TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); return get_typeinfo(IFolder_tid, ppTInfo); } static HRESULT WINAPI folder_GetIDsOfNames(IFolder *iface, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { struct folder *This = impl_from_IFolder(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); hr = get_typeinfo(IFolder_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI folder_Invoke(IFolder *iface, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { struct folder *This = impl_from_IFolder(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); hr = get_typeinfo(IFolder_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI folder_get_Path(IFolder *iface, BSTR *path) { struct folder *This = impl_from_IFolder(iface); TRACE("(%p)->(%p)\n", This, path); if(!path) return E_POINTER; *path = SysAllocString(This->path); return *path ? S_OK : E_OUTOFMEMORY; } static HRESULT WINAPI folder_get_Name(IFolder *iface, BSTR *name) { struct folder *This = impl_from_IFolder(iface); WCHAR *ptr; TRACE("(%p)->(%p)\n", This, name); if(!name) return E_POINTER; *name = NULL; ptr = wcsrchr(This->path, '\\'); if (ptr) { *name = SysAllocString(ptr+1); TRACE("%s\n", debugstr_w(*name)); if (!*name) return E_OUTOFMEMORY; } else return E_FAIL; return S_OK; } static HRESULT WINAPI folder_put_Name(IFolder *iface, BSTR name) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%s): stub\n", This, debugstr_w(name)); return E_NOTIMPL; } static HRESULT WINAPI folder_get_ShortPath(IFolder *iface, BSTR *path) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%p): stub\n", This, path); return E_NOTIMPL; } static HRESULT WINAPI folder_get_ShortName(IFolder *iface, BSTR *name) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%p): stub\n", This, name); return E_NOTIMPL; } static HRESULT WINAPI folder_get_Drive(IFolder *iface, IDrive **drive) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%p): stub\n", This, drive); return E_NOTIMPL; } static HRESULT WINAPI folder_get_ParentFolder(IFolder *iface, IFolder **parent) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%p): stub\n", This, parent); return E_NOTIMPL; } static HRESULT WINAPI folder_get_Attributes(IFolder *iface, FileAttribute *attr) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%p): stub\n", This, attr); return E_NOTIMPL; } static HRESULT WINAPI folder_put_Attributes(IFolder *iface, FileAttribute attr) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(0x%x): stub\n", This, attr); return E_NOTIMPL; } static HRESULT WINAPI folder_get_DateCreated(IFolder *iface, DATE *date) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%p): stub\n", This, date); return E_NOTIMPL; } static HRESULT WINAPI folder_get_DateLastModified(IFolder *iface, DATE *date) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%p): stub\n", This, date); return E_NOTIMPL; } static HRESULT WINAPI folder_get_DateLastAccessed(IFolder *iface, DATE *date) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%p): stub\n", This, date); return E_NOTIMPL; } static HRESULT WINAPI folder_get_Type(IFolder *iface, BSTR *type) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%p): stub\n", This, type); return E_NOTIMPL; } static HRESULT WINAPI folder_Delete(IFolder *iface, VARIANT_BOOL force) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%x): stub\n", This, force); return E_NOTIMPL; } static HRESULT WINAPI folder_Copy(IFolder *iface, BSTR dest, VARIANT_BOOL overwrite) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%s %x): stub\n", This, debugstr_w(dest), overwrite); return E_NOTIMPL; } static HRESULT WINAPI folder_Move(IFolder *iface, BSTR dest) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%s): stub\n", This, debugstr_w(dest)); return E_NOTIMPL; } static HRESULT WINAPI folder_get_IsRootFolder(IFolder *iface, VARIANT_BOOL *isroot) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%p): stub\n", This, isroot); return E_NOTIMPL; } static HRESULT WINAPI folder_get_Size(IFolder *iface, VARIANT *size) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%p): stub\n", This, size); return E_NOTIMPL; } static HRESULT WINAPI folder_get_SubFolders(IFolder *iface, IFolderCollection **folders) { struct folder *This = impl_from_IFolder(iface); TRACE("(%p)->(%p)\n", This, folders); if(!folders) return E_POINTER; return create_foldercoll(This->path, folders); } static HRESULT WINAPI folder_get_Files(IFolder *iface, IFileCollection **files) { struct folder *This = impl_from_IFolder(iface); TRACE("(%p)->(%p)\n", This, files); if(!files) return E_POINTER; return create_filecoll(This->path, files); } static HRESULT WINAPI folder_CreateTextFile(IFolder *iface, BSTR filename, VARIANT_BOOL overwrite, VARIANT_BOOL unicode, ITextStream **stream) { struct folder *This = impl_from_IFolder(iface); FIXME("(%p)->(%s %x %x %p): stub\n", This, debugstr_w(filename), overwrite, unicode, stream); return E_NOTIMPL; } static const IFolderVtbl foldervtbl = { folder_QueryInterface, folder_AddRef, folder_Release, folder_GetTypeInfoCount, folder_GetTypeInfo, folder_GetIDsOfNames, folder_Invoke, folder_get_Path, folder_get_Name, folder_put_Name, folder_get_ShortPath, folder_get_ShortName, folder_get_Drive, folder_get_ParentFolder, folder_get_Attributes, folder_put_Attributes, folder_get_DateCreated, folder_get_DateLastModified, folder_get_DateLastAccessed, folder_get_Type, folder_Delete, folder_Copy, folder_Move, folder_get_IsRootFolder, folder_get_Size, folder_get_SubFolders, folder_get_Files, folder_CreateTextFile }; HRESULT create_folder(const WCHAR *path, IFolder **folder) { struct folder *This; *folder = NULL; TRACE("%s\n", debugstr_w(path)); This = heap_alloc(sizeof(struct folder)); if (!This) return E_OUTOFMEMORY; This->IFolder_iface.lpVtbl = &foldervtbl; This->ref = 1; This->path = SysAllocString(path); if (!This->path) { heap_free(This); return E_OUTOFMEMORY; } init_classinfo(&CLSID_Folder, (IUnknown *)&This->IFolder_iface, &This->classinfo); *folder = &This->IFolder_iface; return S_OK; } static HRESULT WINAPI file_QueryInterface(IFile *iface, REFIID riid, void **obj) { struct file *This = impl_from_IFile(iface); TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj); *obj = NULL; if (IsEqualIID(riid, &IID_IFile) || IsEqualIID(riid, &IID_IDispatch) || IsEqualIID(riid, &IID_IUnknown)) { *obj = &This->IFile_iface; } else if (IsEqualIID( riid, &IID_IProvideClassInfo )) { *obj = &This->classinfo.IProvideClassInfo_iface; } else return E_NOINTERFACE; IUnknown_AddRef((IUnknown*)*obj); return S_OK; } static ULONG WINAPI file_AddRef(IFile *iface) { struct file *This = impl_from_IFile(iface); LONG ref = InterlockedIncrement(&This->ref); TRACE("(%p) ref=%d\n", This, ref); return ref; } static ULONG WINAPI file_Release(IFile *iface) { struct file *This = impl_from_IFile(iface); LONG ref = InterlockedDecrement(&This->ref); TRACE("(%p) ref=%d\n", This, ref); if(!ref) { heap_free(This->path); heap_free(This); } return ref; } static HRESULT WINAPI file_GetTypeInfoCount(IFile *iface, UINT *pctinfo) { struct file *This = impl_from_IFile(iface); TRACE("(%p)->(%p)\n", This, pctinfo); *pctinfo = 1; return S_OK; } static HRESULT WINAPI file_GetTypeInfo(IFile *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { struct file *This = impl_from_IFile(iface); TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); return get_typeinfo(IFile_tid, ppTInfo); } static HRESULT WINAPI file_GetIDsOfNames(IFile *iface, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { struct file *This = impl_from_IFile(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); hr = get_typeinfo(IFile_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI file_Invoke(IFile *iface, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { struct file *This = impl_from_IFile(iface); ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); hr = get_typeinfo(IFile_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI file_get_Path(IFile *iface, BSTR *path) { struct file *This = impl_from_IFile(iface); TRACE("(%p)->(%p)\n", This, path); if (!path) return E_POINTER; *path = SysAllocString(This->path); if (!*path) return E_OUTOFMEMORY; return S_OK; } static HRESULT WINAPI file_get_Name(IFile *iface, BSTR *name) { struct file *This = impl_from_IFile(iface); WCHAR *ptr; TRACE("(%p)->(%p)\n", This, name); if(!name) return E_POINTER; *name = NULL; ptr = wcsrchr(This->path, '\\'); if (ptr) { *name = SysAllocString(ptr+1); TRACE("%s\n", debugstr_w(*name)); if (!*name) return E_OUTOFMEMORY; } else return E_FAIL; return S_OK; } static HRESULT WINAPI file_put_Name(IFile *iface, BSTR pbstrName) { struct file *This = impl_from_IFile(iface); FIXME("(%p)->(%s)\n", This, debugstr_w(pbstrName)); return E_NOTIMPL; } static HRESULT WINAPI file_get_ShortPath(IFile *iface, BSTR *pbstrPath) { struct file *This = impl_from_IFile(iface); FIXME("(%p)->(%p)\n", This, pbstrPath); return E_NOTIMPL; } static HRESULT WINAPI file_get_ShortName(IFile *iface, BSTR *pbstrName) { struct file *This = impl_from_IFile(iface); FIXME("(%p)->(%p)\n", This, pbstrName); return E_NOTIMPL; } static HRESULT WINAPI file_get_Drive(IFile *iface, IDrive **ppdrive) { struct file *This = impl_from_IFile(iface); FIXME("(%p)->(%p)\n", This, ppdrive); return E_NOTIMPL; } static HRESULT WINAPI file_get_ParentFolder(IFile *iface, IFolder **ppfolder) { struct file *This = impl_from_IFile(iface); FIXME("(%p)->(%p)\n", This, ppfolder); return E_NOTIMPL; } static HRESULT WINAPI file_get_Attributes(IFile *iface, FileAttribute *pfa) { struct file *This = impl_from_IFile(iface); DWORD fa; TRACE("(%p)->(%p)\n", This, pfa); if(!pfa) return E_POINTER; fa = GetFileAttributesW(This->path); if(fa == INVALID_FILE_ATTRIBUTES) return create_error(GetLastError()); *pfa = fa & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_COMPRESSED); return S_OK; } static HRESULT WINAPI file_put_Attributes(IFile *iface, FileAttribute pfa) { struct file *This = impl_from_IFile(iface); TRACE("(%p)->(%x)\n", This, pfa); return SetFileAttributesW(This->path, pfa) ? S_OK : create_error(GetLastError()); } static HRESULT get_date_from_filetime(const FILETIME *ft, DATE *date) { FILETIME ftlocal; SYSTEMTIME st; if (!date) return E_POINTER; FileTimeToLocalFileTime(ft, &ftlocal); FileTimeToSystemTime(&ftlocal, &st); SystemTimeToVariantTime(&st, date); return S_OK; } static HRESULT WINAPI file_get_DateCreated(IFile *iface, DATE *pdate) { struct file *This = impl_from_IFile(iface); FIXME("(%p)->(%p)\n", This, pdate); return E_NOTIMPL; } static HRESULT WINAPI file_get_DateLastModified(IFile *iface, DATE *date) { struct file *This = impl_from_IFile(iface); WIN32_FILE_ATTRIBUTE_DATA attrs; TRACE("(%p)->(%p)\n", This, date); if (GetFileAttributesExW(This->path, GetFileExInfoStandard, &attrs)) return get_date_from_filetime(&attrs.ftLastWriteTime, date); return E_FAIL; } static HRESULT WINAPI file_get_DateLastAccessed(IFile *iface, DATE *pdate) { struct file *This = impl_from_IFile(iface); FIXME("(%p)->(%p)\n", This, pdate); return E_NOTIMPL; } static HRESULT WINAPI file_get_Size(IFile *iface, VARIANT *pvarSize) { struct file *This = impl_from_IFile(iface); ULARGE_INTEGER size; WIN32_FIND_DATAW fd; HANDLE f; TRACE("(%p)->(%p)\n", This, pvarSize); if(!pvarSize) return E_POINTER; f = FindFirstFileW(This->path, &fd); if(f == INVALID_HANDLE_VALUE) return create_error(GetLastError()); FindClose(f); size.u.LowPart = fd.nFileSizeLow; size.u.HighPart = fd.nFileSizeHigh; return variant_from_largeint(&size, pvarSize); } static HRESULT WINAPI file_get_Type(IFile *iface, BSTR *pbstrType) { struct file *This = impl_from_IFile(iface); FIXME("(%p)->(%p)\n", This, pbstrType); return E_NOTIMPL; } static HRESULT WINAPI file_Delete(IFile *iface, VARIANT_BOOL Force) { struct file *This = impl_from_IFile(iface); FIXME("(%p)->(%x)\n", This, Force); return E_NOTIMPL; } static HRESULT WINAPI file_Copy(IFile *iface, BSTR Destination, VARIANT_BOOL OverWriteFiles) { struct file *This = impl_from_IFile(iface); FIXME("(%p)->(%s %x)\n", This, debugstr_w(Destination), OverWriteFiles); return E_NOTIMPL; } static HRESULT WINAPI file_Move(IFile *iface, BSTR Destination) { struct file *This = impl_from_IFile(iface); FIXME("(%p)->(%s)\n", This, debugstr_w(Destination)); return E_NOTIMPL; } static HRESULT WINAPI file_OpenAsTextStream(IFile *iface, IOMode mode, Tristate format, ITextStream **stream) { struct file *This = impl_from_IFile(iface); TRACE("(%p)->(%d %d %p)\n", This, mode, format, stream); return create_textstream(This->path, OPEN_EXISTING, mode, format, stream); } static const IFileVtbl file_vtbl = { file_QueryInterface, file_AddRef, file_Release, file_GetTypeInfoCount, file_GetTypeInfo, file_GetIDsOfNames, file_Invoke, file_get_Path, file_get_Name, file_put_Name, file_get_ShortPath, file_get_ShortName, file_get_Drive, file_get_ParentFolder, file_get_Attributes, file_put_Attributes, file_get_DateCreated, file_get_DateLastModified, file_get_DateLastAccessed, file_get_Size, file_get_Type, file_Delete, file_Copy, file_Move, file_OpenAsTextStream }; static HRESULT create_file(BSTR path, IFile **file) { struct file *f; DWORD len, attrs; *file = NULL; f = heap_alloc(sizeof(struct file)); if(!f) return E_OUTOFMEMORY; f->IFile_iface.lpVtbl = &file_vtbl; f->ref = 1; len = GetFullPathNameW(path, 0, NULL, NULL); if(!len) { heap_free(f); return E_FAIL; } f->path = heap_alloc(len*sizeof(WCHAR)); if(!f->path) { heap_free(f); return E_OUTOFMEMORY; } if(!GetFullPathNameW(path, len, f->path, NULL)) { heap_free(f->path); heap_free(f); return E_FAIL; } attrs = GetFileAttributesW(f->path); if(attrs==INVALID_FILE_ATTRIBUTES || (attrs&(FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE))) { heap_free(f->path); heap_free(f); return create_error(GetLastError()); } init_classinfo(&CLSID_File, (IUnknown *)&f->IFile_iface, &f->classinfo); *file = &f->IFile_iface; return S_OK; } static HRESULT WINAPI filesys_QueryInterface(IFileSystem3 *iface, REFIID riid, void **ppvObject) { struct filesystem *This = impl_from_IFileSystem3(iface); TRACE("%p %s %p\n", iface, debugstr_guid(riid), ppvObject); if ( IsEqualGUID( riid, &IID_IFileSystem3 ) || IsEqualGUID( riid, &IID_IFileSystem ) || IsEqualGUID( riid, &IID_IDispatch ) || IsEqualGUID( riid, &IID_IUnknown ) ) { *ppvObject = &This->IFileSystem3_iface; } else if (IsEqualGUID( riid, &IID_IProvideClassInfo )) { *ppvObject = &This->classinfo.IProvideClassInfo_iface; } else if ( IsEqualGUID( riid, &IID_IDispatchEx )) { TRACE("Interface IDispatchEx not supported - returning NULL\n"); *ppvObject = NULL; return E_NOINTERFACE; } else if ( IsEqualGUID( riid, &IID_IObjectWithSite )) { TRACE("Interface IObjectWithSite not supported - returning NULL\n"); *ppvObject = NULL; return E_NOINTERFACE; } else { FIXME("Unsupported interface %s\n", debugstr_guid(riid)); return E_NOINTERFACE; } IUnknown_AddRef((IUnknown*)*ppvObject); return S_OK; } static ULONG WINAPI filesys_AddRef(IFileSystem3 *iface) { TRACE("%p\n", iface); return 2; } static ULONG WINAPI filesys_Release(IFileSystem3 *iface) { TRACE("%p\n", iface); return 1; } static HRESULT WINAPI filesys_GetTypeInfoCount(IFileSystem3 *iface, UINT *pctinfo) { TRACE("(%p)->(%p)\n", iface, pctinfo); *pctinfo = 1; return S_OK; } static HRESULT WINAPI filesys_GetTypeInfo(IFileSystem3 *iface, UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) { TRACE("(%p)->(%u %u %p)\n", iface, iTInfo, lcid, ppTInfo); return get_typeinfo(IFileSystem3_tid, ppTInfo); } static HRESULT WINAPI filesys_GetIDsOfNames(IFileSystem3 *iface, REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) { ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%s %p %u %u %p)\n", iface, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); hr = get_typeinfo(IFileSystem3_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI filesys_Invoke(IFileSystem3 *iface, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { ITypeInfo *typeinfo; HRESULT hr; TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", iface, dispIdMember, debugstr_guid(riid), lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); hr = get_typeinfo(IFileSystem3_tid, &typeinfo); if(SUCCEEDED(hr)) { hr = ITypeInfo_Invoke(typeinfo, iface, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); ITypeInfo_Release(typeinfo); } return hr; } static HRESULT WINAPI filesys_get_Drives(IFileSystem3 *iface, IDriveCollection **ppdrives) { TRACE("%p %p\n", iface, ppdrives); return create_drivecoll(ppdrives); } static HRESULT WINAPI filesys_BuildPath(IFileSystem3 *iface, BSTR Path, BSTR Name, BSTR *Result) { BSTR ret; TRACE("%p %s %s %p\n", iface, debugstr_w(Path), debugstr_w(Name), Result); if (!Result) return E_POINTER; if (Path && Name) { int path_len = SysStringLen(Path), name_len = SysStringLen(Name); /* if both parts have backslashes strip one from Path */ if (Path[path_len-1] == '\\' && Name[0] == '\\') { path_len -= 1; ret = SysAllocStringLen(NULL, path_len + name_len); if (ret) { lstrcpyW(ret, Path); ret[path_len] = 0; lstrcatW(ret, Name); } } else if (Path[path_len-1] != '\\' && Name[0] != '\\') { ret = SysAllocStringLen(NULL, path_len + name_len + 1); if (ret) { lstrcpyW(ret, Path); if (Path[path_len-1] != ':') lstrcatW(ret, bsW); lstrcatW(ret, Name); } } else { ret = SysAllocStringLen(NULL, path_len + name_len); if (ret) { lstrcpyW(ret, Path); lstrcatW(ret, Name); } } } else if (Path || Name) ret = SysAllocString(Path ? Path : Name); else ret = SysAllocStringLen(NULL, 0); if (!ret) return E_OUTOFMEMORY; *Result = ret; return S_OK; } static HRESULT WINAPI filesys_GetDriveName(IFileSystem3 *iface, BSTR path, BSTR *drive) { TRACE("(%p)->(%s %p)\n", iface, debugstr_w(path), drive); if (!drive) return E_POINTER; *drive = NULL; if (path && lstrlenW(path) > 1 && path[1] == ':') *drive = SysAllocStringLen(path, 2); return S_OK; } static inline DWORD get_parent_folder_name(const WCHAR *path, DWORD len) { int i; if(!path) return 0; for(i=len-1; i>=0; i--) if(path[i]!='/' && path[i]!='\\') break; for(; i>=0; i--) if(path[i]=='/' || path[i]=='\\') break; for(; i>=0; i--) if(path[i]!='/' && path[i]!='\\') break; if(i < 0) return 0; if(path[i]==':' && i==1) i++; return i+1; } static HRESULT WINAPI filesys_GetParentFolderName(IFileSystem3 *iface, BSTR Path, BSTR *pbstrResult) { DWORD len; TRACE("%p %s %p\n", iface, debugstr_w(Path), pbstrResult); if(!pbstrResult) return E_POINTER; len = get_parent_folder_name(Path, SysStringLen(Path)); if(!len) { *pbstrResult = NULL; return S_OK; } *pbstrResult = SysAllocStringLen(Path, len); if(!*pbstrResult) return E_OUTOFMEMORY; return S_OK; } static HRESULT WINAPI filesys_GetFileName(IFileSystem3 *iface, BSTR Path, BSTR *pbstrResult) { int i, end; TRACE("%p %s %p\n", iface, debugstr_w(Path), pbstrResult); if(!pbstrResult) return E_POINTER; if(!Path) { *pbstrResult = NULL; return S_OK; } for(end=lstrlenW(Path)-1; end>=0; end--) if(Path[end]!='/' && Path[end]!='\\') break; for(i=end; i>=0; i--) if(Path[i]=='/' || Path[i]=='\\') break; i++; if(i>end || (i==0 && end==1 && Path[1]==':')) { *pbstrResult = NULL; return S_OK; } *pbstrResult = SysAllocStringLen(Path+i, end-i+1); if(!*pbstrResult) return E_OUTOFMEMORY; return S_OK; } static HRESULT WINAPI filesys_GetBaseName(IFileSystem3 *iface, BSTR Path, BSTR *pbstrResult) { int i, end; TRACE("%p %s %p\n", iface, debugstr_w(Path), pbstrResult); if(!pbstrResult) return E_POINTER; if(!Path) { *pbstrResult = NULL; return S_OK; } for(end=lstrlenW(Path)-1; end>=0; end--) if(Path[end]!='/' && Path[end]!='\\') break; for(i=end; i>=0; i--) { if(Path[i]=='.' && Path[end+1]!='.') end = i-1; if(Path[i]=='/' || Path[i]=='\\') break; } i++; if((i>end && Path[end+1]!='.') || (i==0 && end==1 && Path[1]==':')) { *pbstrResult = NULL; return S_OK; } *pbstrResult = SysAllocStringLen(Path+i, end-i+1); if(!*pbstrResult) return E_OUTOFMEMORY; return S_OK; } static HRESULT WINAPI filesys_GetExtensionName(IFileSystem3 *iface, BSTR path, BSTR *ext) { INT len; TRACE("%p %s %p\n", iface, debugstr_w(path), ext); *ext = NULL; len = SysStringLen(path); while (len) { if (path[len-1] == '.') { *ext = SysAllocString(&path[len]); if (!*ext) return E_OUTOFMEMORY; break; } len--; } return S_OK; } static HRESULT WINAPI filesys_GetAbsolutePathName(IFileSystem3 *iface, BSTR Path, BSTR *pbstrResult) { static const WCHAR cur_path[] = {'.',0}; WCHAR buf[MAX_PATH], ch; const WCHAR *path; DWORD i, beg, len, exp_len; WIN32_FIND_DATAW fdata; HANDLE fh; TRACE("%p %s %p\n", iface, debugstr_w(Path), pbstrResult); if(!pbstrResult) return E_POINTER; if(!Path) path = cur_path; else path = Path; len = GetFullPathNameW(path, MAX_PATH, buf, NULL); if(!len) return E_FAIL; buf[0] = towupper(buf[0]); if(len>3 && buf[len-1] == '\\') buf[--len] = 0; for(beg=3, i=3; i<=len; i++) { if(buf[i]!='\\' && buf[i]) continue; ch = buf[i]; buf[i] = 0; fh = FindFirstFileW(buf, &fdata); if(fh == INVALID_HANDLE_VALUE) break; exp_len = lstrlenW(fdata.cFileName); if(exp_len == i-beg) memcpy(buf+beg, fdata.cFileName, exp_len*sizeof(WCHAR)); FindClose(fh); buf[i] = ch; beg = i+1; } *pbstrResult = SysAllocString(buf); if(!*pbstrResult) return E_OUTOFMEMORY; return S_OK; } static HRESULT WINAPI filesys_GetTempName(IFileSystem3 *iface, BSTR *pbstrResult) { static const WCHAR fmt[] = {'r','a','d','%','0','5','X','.','t','x','t',0}; DWORD random; TRACE("%p %p\n", iface, pbstrResult); if(!pbstrResult) return E_POINTER; *pbstrResult = SysAllocStringLen(NULL, 12); if(!*pbstrResult) return E_OUTOFMEMORY; if(!RtlGenRandom(&random, sizeof(random))) return E_FAIL; swprintf(*pbstrResult, 12, fmt, random & 0xfffff); return S_OK; } static HRESULT WINAPI filesys_DriveExists(IFileSystem3 *iface, BSTR DriveSpec, VARIANT_BOOL *pfExists) { UINT len; WCHAR driveletter; TRACE("%p %s %p\n", iface, debugstr_w(DriveSpec), pfExists); if (!pfExists) return E_POINTER; *pfExists = VARIANT_FALSE; len = SysStringLen(DriveSpec); if (len >= 1) { driveletter = towupper(DriveSpec[0]); if (driveletter >= 'A' && driveletter <= 'Z' && (len < 2 || DriveSpec[1] == ':') && (len < 3 || DriveSpec[2] == '\\')) { const WCHAR root[] = {driveletter, ':', '\\', 0}; UINT drivetype = GetDriveTypeW(root); *pfExists = drivetype != DRIVE_NO_ROOT_DIR && drivetype != DRIVE_UNKNOWN ? VARIANT_TRUE : VARIANT_FALSE; } } return S_OK; } static HRESULT WINAPI filesys_FileExists(IFileSystem3 *iface, BSTR path, VARIANT_BOOL *ret) { DWORD attrs; TRACE("%p %s %p\n", iface, debugstr_w(path), ret); if (!ret) return E_POINTER; attrs = GetFileAttributesW(path); *ret = attrs != INVALID_FILE_ATTRIBUTES && !(attrs & FILE_ATTRIBUTE_DIRECTORY) ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } static HRESULT WINAPI filesys_FolderExists(IFileSystem3 *iface, BSTR path, VARIANT_BOOL *ret) { DWORD attrs; TRACE("%p %s %p\n", iface, debugstr_w(path), ret); if (!ret) return E_POINTER; attrs = GetFileAttributesW(path); *ret = attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY) ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } static HRESULT WINAPI filesys_GetDrive(IFileSystem3 *iface, BSTR DriveSpec, IDrive **ppdrive) { UINT len; HRESULT hr; WCHAR driveletter; VARIANT_BOOL drive_exists; TRACE("%p %s %p\n", iface, debugstr_w(DriveSpec), ppdrive); if (!ppdrive) return E_POINTER; *ppdrive = NULL; /* DriveSpec may be one of: 'x', 'x:', 'x:\', '\\computer\share' */ len = SysStringLen(DriveSpec); if (!len) return E_INVALIDARG; else if (len <= 3) { driveletter = towupper(DriveSpec[0]); if (driveletter < 'A' || driveletter > 'Z' || (len >= 2 && DriveSpec[1] != ':') || (len == 3 && DriveSpec[2] != '\\')) return E_INVALIDARG; hr = IFileSystem3_DriveExists(iface, DriveSpec, &drive_exists); if (FAILED(hr)) return hr; if (drive_exists == VARIANT_FALSE) return CTL_E_DEVICEUNAVAILABLE; return create_drive(driveletter, ppdrive); } else { if (DriveSpec[0] != '\\' || DriveSpec[1] != '\\') return E_INVALIDARG; FIXME("%s not implemented yet\n", debugstr_w(DriveSpec)); return E_NOTIMPL; } } static HRESULT WINAPI filesys_GetFile(IFileSystem3 *iface, BSTR FilePath, IFile **ppfile) { TRACE("%p %s %p\n", iface, debugstr_w(FilePath), ppfile); if(!ppfile) return E_POINTER; if(!FilePath) return E_INVALIDARG; return create_file(FilePath, ppfile); } static HRESULT WINAPI filesys_GetFolder(IFileSystem3 *iface, BSTR FolderPath, IFolder **folder) { DWORD attrs; TRACE("%p %s %p\n", iface, debugstr_w(FolderPath), folder); if(!folder) return E_POINTER; *folder = NULL; if(!FolderPath) return E_INVALIDARG; attrs = GetFileAttributesW(FolderPath); if((attrs == INVALID_FILE_ATTRIBUTES) || !(attrs & FILE_ATTRIBUTE_DIRECTORY)) return CTL_E_PATHNOTFOUND; return create_folder(FolderPath, folder); } static HRESULT WINAPI filesys_GetSpecialFolder(IFileSystem3 *iface, SpecialFolderConst SpecialFolder, IFolder **folder) { WCHAR pathW[MAX_PATH]; DWORD ret; TRACE("%p %d %p\n", iface, SpecialFolder, folder); if (!folder) return E_POINTER; *folder = NULL; switch (SpecialFolder) { case WindowsFolder: ret = GetWindowsDirectoryW(pathW, ARRAY_SIZE(pathW)); break; case SystemFolder: ret = GetSystemDirectoryW(pathW, ARRAY_SIZE(pathW)); break; case TemporaryFolder: ret = GetTempPathW(ARRAY_SIZE(pathW), pathW); /* we don't want trailing backslash */ if (ret && pathW[ret-1] == '\\') pathW[ret-1] = 0; break; default: FIXME("unknown special folder type, %d\n", SpecialFolder); return E_INVALIDARG; } if (!ret) return HRESULT_FROM_WIN32(GetLastError()); return create_folder(pathW, folder); } static inline HRESULT delete_file(const WCHAR *file, DWORD file_len, VARIANT_BOOL force) { WCHAR path[MAX_PATH]; DWORD len, name_len; WIN32_FIND_DATAW ffd; HANDLE f; f = FindFirstFileW(file, &ffd); if(f == INVALID_HANDLE_VALUE) return create_error(GetLastError()); len = get_parent_folder_name(file, file_len); if(len+1 >= MAX_PATH) { FindClose(f); return E_FAIL; } if(len) { memcpy(path, file, len*sizeof(WCHAR)); path[len++] = '\\'; } do { if(ffd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE)) continue; name_len = lstrlenW(ffd.cFileName); if(len+name_len+1 >= MAX_PATH) { FindClose(f); return E_FAIL; } memcpy(path+len, ffd.cFileName, (name_len+1)*sizeof(WCHAR)); TRACE("deleting %s\n", debugstr_w(path)); if(!DeleteFileW(path)) { if(!force || !SetFileAttributesW(path, FILE_ATTRIBUTE_NORMAL) || !DeleteFileW(path)) { FindClose(f); return create_error(GetLastError()); } } } while(FindNextFileW(f, &ffd)); FindClose(f); return S_OK; } static HRESULT WINAPI filesys_DeleteFile(IFileSystem3 *iface, BSTR FileSpec, VARIANT_BOOL Force) { TRACE("%p %s %d\n", iface, debugstr_w(FileSpec), Force); if(!FileSpec) return E_POINTER; return delete_file(FileSpec, SysStringLen(FileSpec), Force); } static HRESULT delete_folder(const WCHAR *folder, DWORD folder_len, VARIANT_BOOL force) { WCHAR path[MAX_PATH]; DWORD len, name_len; WIN32_FIND_DATAW ffd; HANDLE f; HRESULT hr; f = FindFirstFileW(folder, &ffd); if(f == INVALID_HANDLE_VALUE) return create_error(GetLastError()); len = get_parent_folder_name(folder, folder_len); if(len+1 >= MAX_PATH) { FindClose(f); return E_FAIL; } if(len) { memcpy(path, folder, len*sizeof(WCHAR)); path[len++] = '\\'; } do { if(!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) continue; if(ffd.cFileName[0]=='.' && (ffd.cFileName[1]==0 || (ffd.cFileName[1]=='.' && ffd.cFileName[2]==0))) continue; name_len = lstrlenW(ffd.cFileName); if(len+name_len+3 >= MAX_PATH) { FindClose(f); return E_FAIL; } memcpy(path+len, ffd.cFileName, name_len*sizeof(WCHAR)); path[len+name_len] = '\\'; path[len+name_len+1] = '*'; path[len+name_len+2] = 0; hr = delete_file(path, len+name_len+2, force); if(FAILED(hr)) { FindClose(f); return hr; } hr = delete_folder(path, len+name_len+2, force); if(FAILED(hr)) { FindClose(f); return hr; } path[len+name_len] = 0; TRACE("deleting %s\n", debugstr_w(path)); if(!RemoveDirectoryW(path)) { FindClose(f); return create_error(GetLastError()); } } while(FindNextFileW(f, &ffd)); FindClose(f); return S_OK; } static HRESULT WINAPI filesys_DeleteFolder(IFileSystem3 *iface, BSTR FolderSpec, VARIANT_BOOL Force) { TRACE("%p %s %d\n", iface, debugstr_w(FolderSpec), Force); if(!FolderSpec) return E_POINTER; return delete_folder(FolderSpec, SysStringLen(FolderSpec), Force); } static HRESULT WINAPI filesys_MoveFile(IFileSystem3 *iface, BSTR source, BSTR destination) { TRACE("%p %s %s\n", iface, debugstr_w(source), debugstr_w(destination)); return MoveFileW(source, destination) ? S_OK : create_error(GetLastError()); } static HRESULT WINAPI filesys_MoveFolder(IFileSystem3 *iface,BSTR Source, BSTR Destination) { FIXME("%p %s %s\n", iface, debugstr_w(Source), debugstr_w(Destination)); return E_NOTIMPL; } static inline HRESULT copy_file(const WCHAR *source, DWORD source_len, const WCHAR *destination, DWORD destination_len, VARIANT_BOOL overwrite) { DWORD attrs; WCHAR src_path[MAX_PATH], dst_path[MAX_PATH]; DWORD src_len, dst_len, name_len; WIN32_FIND_DATAW ffd; HANDLE f; HRESULT hr; if(!source[0] || !destination[0]) return E_INVALIDARG; attrs = GetFileAttributesW(destination); if(attrs==INVALID_FILE_ATTRIBUTES || !(attrs & FILE_ATTRIBUTE_DIRECTORY)) { attrs = GetFileAttributesW(source); if(attrs == INVALID_FILE_ATTRIBUTES) return create_error(GetLastError()); else if(attrs & FILE_ATTRIBUTE_DIRECTORY) return CTL_E_FILENOTFOUND; if(!CopyFileW(source, destination, !overwrite)) return create_error(GetLastError()); return S_OK; } f = FindFirstFileW(source, &ffd); if(f == INVALID_HANDLE_VALUE) return CTL_E_FILENOTFOUND; src_len = get_parent_folder_name(source, source_len); if(src_len+1 >= MAX_PATH) { FindClose(f); return E_FAIL; } if(src_len) { memcpy(src_path, source, src_len*sizeof(WCHAR)); src_path[src_len++] = '\\'; } dst_len = destination_len; if(dst_len+1 >= MAX_PATH) { FindClose(f); return E_FAIL; } memcpy(dst_path, destination, dst_len*sizeof(WCHAR)); if(dst_path[dst_len-1]!= '\\' && dst_path[dst_len-1]!='/') dst_path[dst_len++] = '\\'; hr = CTL_E_FILENOTFOUND; do { if(ffd.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_DEVICE)) continue; name_len = lstrlenW(ffd.cFileName); if(src_len+name_len+1>=MAX_PATH || dst_len+name_len+1>=MAX_PATH) { FindClose(f); return E_FAIL; } memcpy(src_path+src_len, ffd.cFileName, (name_len+1)*sizeof(WCHAR)); memcpy(dst_path+dst_len, ffd.cFileName, (name_len+1)*sizeof(WCHAR)); TRACE("copying %s to %s\n", debugstr_w(src_path), debugstr_w(dst_path)); if(!CopyFileW(src_path, dst_path, !overwrite)) { FindClose(f); return create_error(GetLastError()); }else { hr = S_OK; } } while(FindNextFileW(f, &ffd)); FindClose(f); return hr; } static HRESULT WINAPI filesys_CopyFile(IFileSystem3 *iface, BSTR Source, BSTR Destination, VARIANT_BOOL OverWriteFiles) { TRACE("%p %s %s %d\n", iface, debugstr_w(Source), debugstr_w(Destination), OverWriteFiles); if(!Source || !Destination) return E_POINTER; return copy_file(Source, SysStringLen(Source), Destination, SysStringLen(Destination), OverWriteFiles); } static HRESULT copy_folder(const WCHAR *source, DWORD source_len, const WCHAR *destination, DWORD destination_len, VARIANT_BOOL overwrite) { DWORD tmp, src_len, dst_len, name_len; WCHAR src[MAX_PATH], dst[MAX_PATH]; WIN32_FIND_DATAW ffd; HANDLE f; HRESULT hr; BOOL copied = FALSE; if(!source[0] || !destination[0]) return E_INVALIDARG; dst_len = destination_len; if(dst_len+1 >= MAX_PATH) return E_FAIL; memcpy(dst, destination, (dst_len+1)*sizeof(WCHAR)); if(dst[dst_len-1]!='\\' && dst[dst_len-1]!='/' && (tmp = GetFileAttributesW(source))!=INVALID_FILE_ATTRIBUTES && tmp&FILE_ATTRIBUTE_DIRECTORY) { if(!CreateDirectoryW(dst, NULL)) { if(overwrite && GetLastError()==ERROR_ALREADY_EXISTS) { tmp = GetFileAttributesW(dst); if(tmp==INVALID_FILE_ATTRIBUTES || !(tmp&FILE_ATTRIBUTE_DIRECTORY)) return CTL_E_FILEALREADYEXISTS; }else { return create_error(GetLastError()); } } copied = TRUE; src_len = source_len; if(src_len+2 >= MAX_PATH) return E_FAIL; memcpy(src, source, src_len*sizeof(WCHAR)); src[src_len++] = '\\'; src[src_len] = '*'; src[src_len+1] = 0; hr = copy_file(src, src_len+1, dst, dst_len, overwrite); if(FAILED(hr) && hr!=CTL_E_FILENOTFOUND) return create_error(GetLastError()); f = FindFirstFileW(src, &ffd); }else { src_len = get_parent_folder_name(source, source_len); if(src_len+2 >= MAX_PATH) return E_FAIL; memcpy(src, source, src_len*sizeof(WCHAR)); if(src_len) src[src_len++] = '\\'; f = FindFirstFileW(source, &ffd); } if(f == INVALID_HANDLE_VALUE) return CTL_E_PATHNOTFOUND; dst[dst_len++] = '\\'; dst[dst_len] = 0; do { if(!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) continue; if(ffd.cFileName[0]=='.' && (ffd.cFileName[1]==0 || (ffd.cFileName[1]=='.' && ffd.cFileName[2]==0))) continue; name_len = lstrlenW(ffd.cFileName); if(dst_len+name_len>=MAX_PATH || src_len+name_len+2>=MAX_PATH) { FindClose(f); return E_FAIL; } memcpy(dst+dst_len, ffd.cFileName, name_len*sizeof(WCHAR)); dst[dst_len+name_len] = 0; memcpy(src+src_len, ffd.cFileName, name_len*sizeof(WCHAR)); src[src_len+name_len] = '\\'; src[src_len+name_len+1] = '*'; src[src_len+name_len+2] = 0; TRACE("copying %s to %s\n", debugstr_w(src), debugstr_w(dst)); if(!CreateDirectoryW(dst, NULL)) { if(overwrite && GetLastError()==ERROR_ALREADY_EXISTS) { tmp = GetFileAttributesW(dst); if(tmp==INVALID_FILE_ATTRIBUTES || !(tmp&FILE_ATTRIBUTE_DIRECTORY)) { FindClose(f); return CTL_E_FILEALREADYEXISTS; } } FindClose(f); return create_error(GetLastError()); } copied = TRUE; hr = copy_file(src, src_len+name_len+2, dst, dst_len+name_len, overwrite); if(FAILED(hr) && hr!=CTL_E_FILENOTFOUND) { FindClose(f); return hr; } hr = copy_folder(src, src_len+name_len+2, dst, dst_len+name_len, overwrite); if(FAILED(hr) && hr!=CTL_E_PATHNOTFOUND) { FindClose(f); return hr; } } while(FindNextFileW(f, &ffd)); FindClose(f); return copied ? S_OK : CTL_E_PATHNOTFOUND; } static HRESULT WINAPI filesys_CopyFolder(IFileSystem3 *iface, BSTR Source, BSTR Destination, VARIANT_BOOL OverWriteFiles) { TRACE("%p %s %s %d\n", iface, debugstr_w(Source), debugstr_w(Destination), OverWriteFiles); if(!Source || !Destination) return E_POINTER; return copy_folder(Source, SysStringLen(Source), Destination, SysStringLen(Destination), OverWriteFiles); } static HRESULT WINAPI filesys_CreateFolder(IFileSystem3 *iface, BSTR path, IFolder **folder) { BOOL ret; TRACE("(%p)->(%s %p)\n", iface, debugstr_w(path), folder); ret = CreateDirectoryW(path, NULL); if (!ret) { *folder = NULL; if (GetLastError() == ERROR_ALREADY_EXISTS) return CTL_E_FILEALREADYEXISTS; return HRESULT_FROM_WIN32(GetLastError()); } return create_folder(path, folder); } static HRESULT WINAPI filesys_CreateTextFile(IFileSystem3 *iface, BSTR filename, VARIANT_BOOL overwrite, VARIANT_BOOL unicode, ITextStream **stream) { DWORD disposition; TRACE("%p %s %d %d %p\n", iface, debugstr_w(filename), overwrite, unicode, stream); disposition = overwrite == VARIANT_TRUE ? CREATE_ALWAYS : CREATE_NEW; return create_textstream(filename, disposition, ForWriting, unicode ? TristateTrue : TristateFalse, stream); } static HRESULT WINAPI filesys_OpenTextFile(IFileSystem3 *iface, BSTR filename, IOMode mode, VARIANT_BOOL create, Tristate format, ITextStream **stream) { DWORD disposition; TRACE("(%p)->(%s %d %d %d %p)\n", iface, debugstr_w(filename), mode, create, format, stream); disposition = create == VARIANT_TRUE ? OPEN_ALWAYS : OPEN_EXISTING; return create_textstream(filename, disposition, mode, format, stream); } static HRESULT WINAPI filesys_GetStandardStream(IFileSystem3 *iface, StandardStreamTypes StandardStreamType, VARIANT_BOOL Unicode, ITextStream **ppts) { FIXME("%p %d %d %p\n", iface, StandardStreamType, Unicode, ppts); return E_NOTIMPL; } static void get_versionstring(VS_FIXEDFILEINFO *info, WCHAR *ver) { static const WCHAR fmtW[] = {'%','d','.','%','d','.','%','d','.','%','d',0}; DWORDLONG version; WORD a, b, c, d; version = (((DWORDLONG)info->dwFileVersionMS) << 32) + info->dwFileVersionLS; a = (WORD)( version >> 48); b = (WORD)((version >> 32) & 0xffff); c = (WORD)((version >> 16) & 0xffff); d = (WORD)( version & 0xffff); swprintf(ver, 30, fmtW, a, b, c, d); } static HRESULT WINAPI filesys_GetFileVersion(IFileSystem3 *iface, BSTR name, BSTR *version) { static const WCHAR rootW[] = {'\\',0}; VS_FIXEDFILEINFO *info; WCHAR ver[30]; void *ptr; DWORD len; BOOL ret; TRACE("%p %s %p\n", iface, debugstr_w(name), version); len = GetFileVersionInfoSizeW(name, NULL); if (!len) return HRESULT_FROM_WIN32(GetLastError()); ptr = heap_alloc(len); if (!GetFileVersionInfoW(name, 0, len, ptr)) { heap_free(ptr); return HRESULT_FROM_WIN32(GetLastError()); } ret = VerQueryValueW(ptr, rootW, (void**)&info, &len); if (!ret) { heap_free(ptr); return HRESULT_FROM_WIN32(GetLastError()); } get_versionstring(info, ver); heap_free(ptr); *version = SysAllocString(ver); TRACE("version=%s\n", debugstr_w(ver)); return S_OK; } static const struct IFileSystem3Vtbl filesys_vtbl = { filesys_QueryInterface, filesys_AddRef, filesys_Release, filesys_GetTypeInfoCount, filesys_GetTypeInfo, filesys_GetIDsOfNames, filesys_Invoke, filesys_get_Drives, filesys_BuildPath, filesys_GetDriveName, filesys_GetParentFolderName, filesys_GetFileName, filesys_GetBaseName, filesys_GetExtensionName, filesys_GetAbsolutePathName, filesys_GetTempName, filesys_DriveExists, filesys_FileExists, filesys_FolderExists, filesys_GetDrive, filesys_GetFile, filesys_GetFolder, filesys_GetSpecialFolder, filesys_DeleteFile, filesys_DeleteFolder, filesys_MoveFile, filesys_MoveFolder, filesys_CopyFile, filesys_CopyFolder, filesys_CreateFolder, filesys_CreateTextFile, filesys_OpenTextFile, filesys_GetStandardStream, filesys_GetFileVersion }; static struct filesystem filesystem; HRESULT WINAPI FileSystem_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv) { TRACE("(%p %s %p)\n", outer, debugstr_guid(riid), ppv); filesystem.IFileSystem3_iface.lpVtbl = &filesys_vtbl; init_classinfo(&CLSID_FileSystemObject, (IUnknown *)&filesystem.IFileSystem3_iface, &filesystem.classinfo); return IFileSystem3_QueryInterface(&filesystem.IFileSystem3_iface, riid, ppv); }