1 // Windows/FileFind.cpp
2
3 #include "StdAfx.h"
4
5 #ifndef _UNICODE
6 #include "../Common/StringConvert.h"
7 #endif
8
9 #include "FileFind.h"
10 #include "FileIO.h"
11 #include "FileName.h"
12
13 #ifndef _UNICODE
14 extern bool g_IsNT;
15 #endif
16
17 using namespace NWindows;
18 using namespace NFile;
19 using namespace NName;
20
21 #if defined(_WIN32) && !defined(UNDER_CE)
22
23 EXTERN_C_BEGIN
24
25 typedef enum
26 {
27 My_FindStreamInfoStandard,
28 My_FindStreamInfoMaxInfoLevel
29 } MY_STREAM_INFO_LEVELS;
30
31 typedef struct
32 {
33 LARGE_INTEGER StreamSize;
34 WCHAR cStreamName[MAX_PATH + 36];
35 } MY_WIN32_FIND_STREAM_DATA, *MY_PWIN32_FIND_STREAM_DATA;
36
37 typedef WINBASEAPI HANDLE (WINAPI *FindFirstStreamW_Ptr)(LPCWSTR fileName, MY_STREAM_INFO_LEVELS infoLevel,
38 LPVOID findStreamData, DWORD flags);
39
40 typedef WINBASEAPI BOOL (APIENTRY *FindNextStreamW_Ptr)(HANDLE findStream, LPVOID findStreamData);
41
42 EXTERN_C_END
43
44 #endif
45
46 namespace NWindows {
47 namespace NFile {
48
49 #ifdef SUPPORT_DEVICE_FILE
50 namespace NSystem
51 {
52 bool MyGetDiskFreeSpace(CFSTR rootPath, UInt64 &clusterSize, UInt64 &totalSize, UInt64 &freeSize);
53 }
54 #endif
55
56 namespace NFind {
57
IsDots() const58 bool CFileInfo::IsDots() const throw()
59 {
60 if (!IsDir() || Name.IsEmpty())
61 return false;
62 if (Name[0] != '.')
63 return false;
64 return Name.Len() == 1 || (Name.Len() == 2 && Name[1] == '.');
65 }
66
67 #define WIN_FD_TO_MY_FI(fi, fd) \
68 fi.Attrib = fd.dwFileAttributes; \
69 fi.CTime = fd.ftCreationTime; \
70 fi.ATime = fd.ftLastAccessTime; \
71 fi.MTime = fd.ftLastWriteTime; \
72 fi.Size = (((UInt64)fd.nFileSizeHigh) << 32) + fd.nFileSizeLow; \
73 fi.IsAltStream = false; \
74 fi.IsDevice = false;
75
76 /*
77 #ifdef UNDER_CE
78 fi.ObjectID = fd.dwOID;
79 #else
80 fi.ReparseTag = fd.dwReserved0;
81 #endif
82 */
83
Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATAW & fd,CFileInfo & fi)84 static void Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATAW &fd, CFileInfo &fi)
85 {
86 WIN_FD_TO_MY_FI(fi, fd);
87 fi.Name = us2fs(fd.cFileName);
88 #if defined(_WIN32) && !defined(UNDER_CE)
89 // fi.ShortName = us2fs(fd.cAlternateFileName);
90 #endif
91 }
92
93 #ifndef _UNICODE
94
Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATA & fd,CFileInfo & fi)95 static void Convert_WIN32_FIND_DATA_to_FileInfo(const WIN32_FIND_DATA &fd, CFileInfo &fi)
96 {
97 WIN_FD_TO_MY_FI(fi, fd);
98 fi.Name = fas2fs(fd.cFileName);
99 #if defined(_WIN32) && !defined(UNDER_CE)
100 // fi.ShortName = fas2fs(fd.cAlternateFileName);
101 #endif
102 }
103 #endif
104
105 ////////////////////////////////
106 // CFindFile
107
Close()108 bool CFindFileBase::Close() throw()
109 {
110 if (_handle == INVALID_HANDLE_VALUE)
111 return true;
112 if (!::FindClose(_handle))
113 return false;
114 _handle = INVALID_HANDLE_VALUE;
115 return true;
116 }
117
118 /*
119 WinXP-64 FindFirstFile():
120 "" - ERROR_PATH_NOT_FOUND
121 folder\ - ERROR_FILE_NOT_FOUND
122 \ - ERROR_FILE_NOT_FOUND
123 c:\ - ERROR_FILE_NOT_FOUND
124 c: - ERROR_FILE_NOT_FOUND, if current dir is ROOT ( c:\ )
125 c: - OK, if current dir is NOT ROOT ( c:\folder )
126 folder - OK
127
128 \\ - ERROR_INVALID_NAME
129 \\Server - ERROR_INVALID_NAME
130 \\Server\ - ERROR_INVALID_NAME
131
132 \\Server\Share - ERROR_BAD_NETPATH
133 \\Server\Share - ERROR_BAD_NET_NAME (Win7).
134 !!! There is problem : Win7 makes some requests for "\\Server\Shar" (look in Procmon),
135 when we call it for "\\Server\Share"
136
137 \\Server\Share\ - ERROR_FILE_NOT_FOUND
138
139 \\?\UNC\Server\Share - ERROR_INVALID_NAME
140 \\?\UNC\Server\Share - ERROR_BAD_PATHNAME (Win7)
141 \\?\UNC\Server\Share\ - ERROR_FILE_NOT_FOUND
142
143 \\Server\Share_RootDrive - ERROR_INVALID_NAME
144 \\Server\Share_RootDrive\ - ERROR_INVALID_NAME
145
146 c:\* - ERROR_FILE_NOT_FOUND, if thare are no item in that folder
147 */
148
FindFirst(CFSTR path,CFileInfo & fi)149 bool CFindFile::FindFirst(CFSTR path, CFileInfo &fi)
150 {
151 if (!Close())
152 return false;
153 #ifndef _UNICODE
154 if (!g_IsNT)
155 {
156 WIN32_FIND_DATAA fd;
157 _handle = ::FindFirstFileA(fs2fas(path), &fd);
158 if (_handle == INVALID_HANDLE_VALUE)
159 return false;
160 Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi);
161 }
162 else
163 #endif
164 {
165 WIN32_FIND_DATAW fd;
166
167 IF_USE_MAIN_PATH
168 _handle = ::FindFirstFileW(fs2us(path), &fd);
169 #ifdef WIN_LONG_PATH
170 if (_handle == INVALID_HANDLE_VALUE && USE_SUPER_PATH)
171 {
172 UString superPath;
173 if (GetSuperPath(path, superPath, USE_MAIN_PATH))
174 _handle = ::FindFirstFileW(superPath, &fd);
175 }
176 #endif
177 if (_handle == INVALID_HANDLE_VALUE)
178 return false;
179 Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi);
180 }
181 return true;
182 }
183
FindNext(CFileInfo & fi)184 bool CFindFile::FindNext(CFileInfo &fi)
185 {
186 #ifndef _UNICODE
187 if (!g_IsNT)
188 {
189 WIN32_FIND_DATAA fd;
190 if (!::FindNextFileA(_handle, &fd))
191 return false;
192 Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi);
193 }
194 else
195 #endif
196 {
197 WIN32_FIND_DATAW fd;
198 if (!::FindNextFileW(_handle, &fd))
199 return false;
200 Convert_WIN32_FIND_DATA_to_FileInfo(fd, fi);
201 }
202 return true;
203 }
204
205 #if defined(_WIN32) && !defined(UNDER_CE)
206
207 ////////////////////////////////
208 // AltStreams
209
210 static FindFirstStreamW_Ptr g_FindFirstStreamW;
211 static FindNextStreamW_Ptr g_FindNextStreamW;
212
213 struct CFindStreamLoader
214 {
CFindStreamLoaderNWindows::NFile::NFind::CFindStreamLoader215 CFindStreamLoader()
216 {
217 g_FindFirstStreamW = (FindFirstStreamW_Ptr)::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "FindFirstStreamW");
218 g_FindNextStreamW = (FindNextStreamW_Ptr)::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "FindNextStreamW");
219 }
220 } g_FindStreamLoader;
221
IsMainStream() const222 bool CStreamInfo::IsMainStream() const throw()
223 {
224 return StringsAreEqualNoCase_Ascii(Name, "::$DATA");
225 };
226
GetReducedName() const227 UString CStreamInfo::GetReducedName() const
228 {
229 // remove ":$DATA" postfix, but keep postfix, if Name is "::$DATA"
230 UString s = Name;
231 if (s.Len() > 6 + 1 && StringsAreEqualNoCase_Ascii(s.RightPtr(6), ":$DATA"))
232 s.DeleteFrom(s.Len() - 6);
233 return s;
234 }
235
236 /*
237 UString CStreamInfo::GetReducedName2() const
238 {
239 UString s = GetReducedName();
240 if (!s.IsEmpty() && s[0] == ':')
241 s.Delete(0);
242 return s;
243 }
244 */
245
Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(const MY_WIN32_FIND_STREAM_DATA & sd,CStreamInfo & si)246 static void Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(const MY_WIN32_FIND_STREAM_DATA &sd, CStreamInfo &si)
247 {
248 si.Size = sd.StreamSize.QuadPart;
249 si.Name = sd.cStreamName;
250 }
251
252 /*
253 WinXP-64 FindFirstStream():
254 "" - ERROR_PATH_NOT_FOUND
255 folder\ - OK
256 folder - OK
257 \ - OK
258 c:\ - OK
259 c: - OK, if current dir is ROOT ( c:\ )
260 c: - OK, if current dir is NOT ROOT ( c:\folder )
261 \\Server\Share - OK
262 \\Server\Share\ - OK
263
264 \\ - ERROR_INVALID_NAME
265 \\Server - ERROR_INVALID_NAME
266 \\Server\ - ERROR_INVALID_NAME
267 */
268
FindFirst(CFSTR path,CStreamInfo & si)269 bool CFindStream::FindFirst(CFSTR path, CStreamInfo &si)
270 {
271 if (!Close())
272 return false;
273 if (!g_FindFirstStreamW)
274 {
275 ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
276 return false;
277 }
278 {
279 MY_WIN32_FIND_STREAM_DATA sd;
280 SetLastError(0);
281 IF_USE_MAIN_PATH
282 _handle = g_FindFirstStreamW(fs2us(path), My_FindStreamInfoStandard, &sd, 0);
283 if (_handle == INVALID_HANDLE_VALUE)
284 {
285 if (::GetLastError() == ERROR_HANDLE_EOF)
286 return false;
287 // long name can be tricky for path like ".\dirName".
288 #ifdef WIN_LONG_PATH
289 if (USE_SUPER_PATH)
290 {
291 UString superPath;
292 if (GetSuperPath(path, superPath, USE_MAIN_PATH))
293 _handle = g_FindFirstStreamW(superPath, My_FindStreamInfoStandard, &sd, 0);
294 }
295 #endif
296 }
297 if (_handle == INVALID_HANDLE_VALUE)
298 return false;
299 Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(sd, si);
300 }
301 return true;
302 }
303
FindNext(CStreamInfo & si)304 bool CFindStream::FindNext(CStreamInfo &si)
305 {
306 if (!g_FindNextStreamW)
307 {
308 ::SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
309 return false;
310 }
311 {
312 MY_WIN32_FIND_STREAM_DATA sd;
313 if (!g_FindNextStreamW(_handle, &sd))
314 return false;
315 Convert_WIN32_FIND_STREAM_DATA_to_StreamInfo(sd, si);
316 }
317 return true;
318 }
319
Next(CStreamInfo & si,bool & found)320 bool CStreamEnumerator::Next(CStreamInfo &si, bool &found)
321 {
322 bool res;
323 if (_find.IsHandleAllocated())
324 res = _find.FindNext(si);
325 else
326 res = _find.FindFirst(_filePath, si);
327 if (res)
328 {
329 found = true;
330 return true;
331 }
332 found = false;
333 return (::GetLastError() == ERROR_HANDLE_EOF);
334 }
335
336 #endif
337
338
339 #define MY_CLEAR_FILETIME(ft) ft.dwLowDateTime = ft.dwHighDateTime = 0;
340
ClearBase()341 void CFileInfoBase::ClearBase() throw()
342 {
343 Size = 0;
344 MY_CLEAR_FILETIME(CTime);
345 MY_CLEAR_FILETIME(ATime);
346 MY_CLEAR_FILETIME(MTime);
347 Attrib = 0;
348 IsAltStream = false;
349 IsDevice = false;
350 }
351
352 /*
353 WinXP-64 GetFileAttributes():
354 If the function fails, it returns INVALID_FILE_ATTRIBUTES and use GetLastError() to get error code
355
356 \ - OK
357 C:\ - OK, if there is such drive,
358 D:\ - ERROR_PATH_NOT_FOUND, if there is no such drive,
359
360 C:\folder - OK
361 C:\folder\ - OK
362 C:\folderBad - ERROR_FILE_NOT_FOUND
363
364 \\Server\BadShare - ERROR_BAD_NETPATH
365 \\Server\Share - WORKS OK, but MSDN says:
366 GetFileAttributes for a network share, the function fails, and GetLastError
367 returns ERROR_BAD_NETPATH. You must specify a path to a subfolder on that share.
368 */
369
GetFileAttrib(CFSTR path)370 DWORD GetFileAttrib(CFSTR path)
371 {
372 #ifndef _UNICODE
373 if (!g_IsNT)
374 return ::GetFileAttributes(fs2fas(path));
375 else
376 #endif
377 {
378 IF_USE_MAIN_PATH
379 {
380 DWORD dw = ::GetFileAttributesW(fs2us(path));
381 if (dw != INVALID_FILE_ATTRIBUTES)
382 return dw;
383 }
384 #ifdef WIN_LONG_PATH
385 if (USE_SUPER_PATH)
386 {
387 UString superPath;
388 if (GetSuperPath(path, superPath, USE_MAIN_PATH))
389 return ::GetFileAttributesW(superPath);
390 }
391 #endif
392 return INVALID_FILE_ATTRIBUTES;
393 }
394 }
395
396 /* if path is "c:" or "c::" then CFileInfo::Find() returns name of current folder for that disk
397 so instead of absolute path we have relative path in Name. That is not good in some calls */
398
399 /* In CFileInfo::Find() we want to support same names for alt streams as in CreateFile(). */
400
401 /* CFileInfo::Find()
402 We alow the following paths (as FindFirstFile):
403 C:\folder
404 c: - if current dir is NOT ROOT ( c:\folder )
405
406 also we support paths that are not supported by FindFirstFile:
407 \
408 \\.\c:
409 c:\ - Name will be without tail slash ( c: )
410 \\?\c:\ - Name will be without tail slash ( c: )
411 \\Server\Share
412 \\?\UNC\Server\Share
413
414 c:\folder:stream - Name = folder:stream
415 c:\:stream - Name = :stream
416 c::stream - Name = c::stream
417 */
418
Find(CFSTR path)419 bool CFileInfo::Find(CFSTR path)
420 {
421 #ifdef SUPPORT_DEVICE_FILE
422 if (IsDevicePath(path))
423 {
424 ClearBase();
425 Name = path + 4;
426 IsDevice = true;
427
428 if (NName::IsDrivePath2(path + 4) && path[6] == 0)
429 {
430 FChar drive[4] = { path[4], ':', '\\', 0 };
431 UInt64 clusterSize, totalSize, freeSize;
432 if (NSystem::MyGetDiskFreeSpace(drive, clusterSize, totalSize, freeSize))
433 {
434 Size = totalSize;
435 return true;
436 }
437 }
438
439 NIO::CInFile inFile;
440 // ::OutputDebugStringW(path);
441 if (!inFile.Open(path))
442 return false;
443 // ::OutputDebugStringW(L"---");
444 if (inFile.SizeDefined)
445 Size = inFile.Size;
446 return true;
447 }
448 #endif
449
450 #if defined(_WIN32) && !defined(UNDER_CE)
451
452 int colonPos = FindAltStreamColon(path);
453 if (colonPos >= 0 && path[(unsigned)colonPos + 1] != 0)
454 {
455 UString streamName = fs2us(path + (unsigned)colonPos);
456 FString filePath = path;
457 filePath.DeleteFrom(colonPos);
458 /* we allow both cases:
459 name:stream
460 name:stream:$DATA
461 */
462 const unsigned kPostfixSize = 6;
463 if (streamName.Len() <= kPostfixSize
464 || !StringsAreEqualNoCase_Ascii(streamName.RightPtr(kPostfixSize), ":$DATA"))
465 streamName += L":$DATA";
466
467 bool isOk = true;
468
469 if (IsDrivePath2(filePath) &&
470 (colonPos == 2 || colonPos == 3 && filePath[2] == '\\'))
471 {
472 // FindFirstFile doesn't work for "c:\" and for "c:" (if current dir is ROOT)
473 ClearBase();
474 Name.Empty();
475 if (colonPos == 2)
476 Name = filePath;
477 }
478 else
479 isOk = Find(filePath);
480
481 if (isOk)
482 {
483 Attrib &= ~(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT);
484 Size = 0;
485 CStreamEnumerator enumerator(filePath);
486 for (;;)
487 {
488 CStreamInfo si;
489 bool found;
490 if (!enumerator.Next(si, found))
491 return false;
492 if (!found)
493 {
494 ::SetLastError(ERROR_FILE_NOT_FOUND);
495 return false;
496 }
497 if (si.Name.IsEqualTo_NoCase(streamName))
498 {
499 // we delete postfix, if alt stream name is not "::$DATA"
500 if (si.Name.Len() > kPostfixSize + 1)
501 si.Name.DeleteFrom(si.Name.Len() - kPostfixSize);
502 Name += us2fs(si.Name);
503 Size = si.Size;
504 IsAltStream = true;
505 return true;
506 }
507 }
508 }
509 }
510
511 #endif
512
513 CFindFile finder;
514
515 #if defined(_WIN32) && !defined(UNDER_CE)
516 {
517 /*
518 DWORD lastError = GetLastError();
519 if (lastError == ERROR_FILE_NOT_FOUND
520 || lastError == ERROR_BAD_NETPATH // XP64: "\\Server\Share"
521 || lastError == ERROR_BAD_NET_NAME // Win7: "\\Server\Share"
522 || lastError == ERROR_INVALID_NAME // XP64: "\\?\UNC\Server\Share"
523 || lastError == ERROR_BAD_PATHNAME // Win7: "\\?\UNC\Server\Share"
524 )
525 */
526
527 unsigned rootSize = 0;
528 if (IsSuperPath(path))
529 rootSize = kSuperPathPrefixSize;
530
531 if (NName::IsDrivePath(path + rootSize) && path[rootSize + 3] == 0)
532 {
533 DWORD attrib = GetFileAttrib(path);
534 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0)
535 {
536 ClearBase();
537 Attrib = attrib;
538 Name = path + rootSize;
539 Name.DeleteFrom(2); // we don't need backslash (C:)
540 return true;
541 }
542 }
543 else if (IS_PATH_SEPAR(path[0]))
544 if (path[1] == 0)
545 {
546 DWORD attrib = GetFileAttrib(path);
547 if (attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0)
548 {
549 ClearBase();
550 Name.Empty();
551 Attrib = attrib;
552 return true;
553 }
554 }
555 else
556 {
557 const unsigned prefixSize = GetNetworkServerPrefixSize(path);
558 if (prefixSize > 0 && path[prefixSize] != 0)
559 {
560 if (NName::FindSepar(path + prefixSize) < 0)
561 {
562 FString s = path;
563 s.Add_PathSepar();
564 s += FCHAR_ANY_MASK;
565
566 bool isOK = false;
567 if (finder.FindFirst(s, *this))
568 {
569 if (Name == FTEXT("."))
570 {
571 Name = path + prefixSize;
572 return true;
573 }
574 isOK = true;
575 /* if "\\server\share" maps to root folder "d:\", there is no "." item.
576 But it's possible that there are another items */
577 }
578 {
579 DWORD attrib = GetFileAttrib(path);
580 if (isOK || attrib != INVALID_FILE_ATTRIBUTES && (attrib & FILE_ATTRIBUTE_DIRECTORY) != 0)
581 {
582 ClearBase();
583 if (attrib != INVALID_FILE_ATTRIBUTES)
584 Attrib = attrib;
585 else
586 SetAsDir();
587 Name = path + prefixSize;
588 return true;
589 }
590 }
591 // ::SetLastError(lastError);
592 }
593 }
594 }
595 }
596 #endif
597
598 return finder.FindFirst(path, *this);
599 }
600
601
DoesFileExist(CFSTR name)602 bool DoesFileExist(CFSTR name)
603 {
604 CFileInfo fi;
605 return fi.Find(name) && !fi.IsDir();
606 }
607
DoesDirExist(CFSTR name)608 bool DoesDirExist(CFSTR name)
609 {
610 CFileInfo fi;
611 return fi.Find(name) && fi.IsDir();
612 }
613
DoesFileOrDirExist(CFSTR name)614 bool DoesFileOrDirExist(CFSTR name)
615 {
616 CFileInfo fi;
617 return fi.Find(name);
618 }
619
620
NextAny(CFileInfo & fi)621 bool CEnumerator::NextAny(CFileInfo &fi)
622 {
623 if (_findFile.IsHandleAllocated())
624 return _findFile.FindNext(fi);
625 else
626 return _findFile.FindFirst(_wildcard, fi);
627 }
628
Next(CFileInfo & fi)629 bool CEnumerator::Next(CFileInfo &fi)
630 {
631 for (;;)
632 {
633 if (!NextAny(fi))
634 return false;
635 if (!fi.IsDots())
636 return true;
637 }
638 }
639
Next(CFileInfo & fi,bool & found)640 bool CEnumerator::Next(CFileInfo &fi, bool &found)
641 {
642 if (Next(fi))
643 {
644 found = true;
645 return true;
646 }
647 found = false;
648 return (::GetLastError() == ERROR_NO_MORE_FILES);
649 }
650
651 ////////////////////////////////
652 // CFindChangeNotification
653 // FindFirstChangeNotification can return 0. MSDN doesn't tell about it.
654
Close()655 bool CFindChangeNotification::Close() throw()
656 {
657 if (!IsHandleAllocated())
658 return true;
659 if (!::FindCloseChangeNotification(_handle))
660 return false;
661 _handle = INVALID_HANDLE_VALUE;
662 return true;
663 }
664
FindFirst(CFSTR path,bool watchSubtree,DWORD notifyFilter)665 HANDLE CFindChangeNotification::FindFirst(CFSTR path, bool watchSubtree, DWORD notifyFilter)
666 {
667 #ifndef _UNICODE
668 if (!g_IsNT)
669 _handle = ::FindFirstChangeNotification(fs2fas(path), BoolToBOOL(watchSubtree), notifyFilter);
670 else
671 #endif
672 {
673 IF_USE_MAIN_PATH
674 _handle = ::FindFirstChangeNotificationW(fs2us(path), BoolToBOOL(watchSubtree), notifyFilter);
675 #ifdef WIN_LONG_PATH
676 if (!IsHandleAllocated())
677 {
678 UString superPath;
679 if (GetSuperPath(path, superPath, USE_MAIN_PATH))
680 _handle = ::FindFirstChangeNotificationW(superPath, BoolToBOOL(watchSubtree), notifyFilter);
681 }
682 #endif
683 }
684 return _handle;
685 }
686
687 #ifndef UNDER_CE
688
MyGetLogicalDriveStrings(CObjectVector<FString> & driveStrings)689 bool MyGetLogicalDriveStrings(CObjectVector<FString> &driveStrings)
690 {
691 driveStrings.Clear();
692 #ifndef _UNICODE
693 if (!g_IsNT)
694 {
695 driveStrings.Clear();
696 UINT32 size = GetLogicalDriveStrings(0, NULL);
697 if (size == 0)
698 return false;
699 CObjArray<char> buf(size);
700 UINT32 newSize = GetLogicalDriveStrings(size, buf);
701 if (newSize == 0 || newSize > size)
702 return false;
703 AString s;
704 UINT32 prev = 0;
705 for (UINT32 i = 0; i < newSize; i++)
706 {
707 if (buf[i] == 0)
708 {
709 s = buf + prev;
710 prev = i + 1;
711 driveStrings.Add(fas2fs(s));
712 }
713 }
714 return prev == newSize;
715 }
716 else
717 #endif
718 {
719 UINT32 size = GetLogicalDriveStringsW(0, NULL);
720 if (size == 0)
721 return false;
722 CObjArray<wchar_t> buf(size);
723 UINT32 newSize = GetLogicalDriveStringsW(size, buf);
724 if (newSize == 0 || newSize > size)
725 return false;
726 UString s;
727 UINT32 prev = 0;
728 for (UINT32 i = 0; i < newSize; i++)
729 {
730 if (buf[i] == 0)
731 {
732 s = buf + prev;
733 prev = i + 1;
734 driveStrings.Add(us2fs(s));
735 }
736 }
737 return prev == newSize;
738 }
739 }
740
741 #endif
742
743 }}}
744