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