"Fossies" - the Fresh Open Source Software Archive

Member "UXP-2019.06.08/other-licenses/7zstub/src/CPP/Windows/FileFind.cpp" (8 Jun 2019, 19034 Bytes) of package /linux/www/UXP-2019.06.08.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    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 
   58 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 
   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 
   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 
  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 
  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 
  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 {
  215   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 
  222 bool CStreamInfo::IsMainStream() const throw()
  223 {
  224   return StringsAreEqualNoCase_Ascii(Name, "::$DATA");
  225 };
  226 
  227 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 
  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 
  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 
  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 
  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 
  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 
  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 
  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 += ":$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 += '*'; // CHAR_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 
  602 bool DoesFileExist(CFSTR name)
  603 {
  604   CFileInfo fi;
  605   return fi.Find(name) && !fi.IsDir();
  606 }
  607 
  608 bool DoesDirExist(CFSTR name)
  609 {
  610   CFileInfo fi;
  611   return fi.Find(name) && fi.IsDir();
  612 }
  613 
  614 bool DoesFileOrDirExist(CFSTR name)
  615 {
  616   CFileInfo fi;
  617   return fi.Find(name);
  618 }
  619 
  620 
  621 void CEnumerator::SetDirPrefix(const FString &dirPrefix)
  622 {
  623   _wildcard = dirPrefix;
  624   _wildcard += '*';
  625 }
  626 
  627 bool CEnumerator::NextAny(CFileInfo &fi)
  628 {
  629   if (_findFile.IsHandleAllocated())
  630     return _findFile.FindNext(fi);
  631   else
  632     return _findFile.FindFirst(_wildcard, fi);
  633 }
  634 
  635 bool CEnumerator::Next(CFileInfo &fi)
  636 {
  637   for (;;)
  638   {
  639     if (!NextAny(fi))
  640       return false;
  641     if (!fi.IsDots())
  642       return true;
  643   }
  644 }
  645 
  646 bool CEnumerator::Next(CFileInfo &fi, bool &found)
  647 {
  648   if (Next(fi))
  649   {
  650     found = true;
  651     return true;
  652   }
  653   found = false;
  654   return (::GetLastError() == ERROR_NO_MORE_FILES);
  655 }
  656 
  657 ////////////////////////////////
  658 // CFindChangeNotification
  659 // FindFirstChangeNotification can return 0. MSDN doesn't tell about it.
  660 
  661 bool CFindChangeNotification::Close() throw()
  662 {
  663   if (!IsHandleAllocated())
  664     return true;
  665   if (!::FindCloseChangeNotification(_handle))
  666     return false;
  667   _handle = INVALID_HANDLE_VALUE;
  668   return true;
  669 }
  670            
  671 HANDLE CFindChangeNotification::FindFirst(CFSTR path, bool watchSubtree, DWORD notifyFilter)
  672 {
  673   #ifndef _UNICODE
  674   if (!g_IsNT)
  675     _handle = ::FindFirstChangeNotification(fs2fas(path), BoolToBOOL(watchSubtree), notifyFilter);
  676   else
  677   #endif
  678   {
  679     IF_USE_MAIN_PATH
  680     _handle = ::FindFirstChangeNotificationW(fs2us(path), BoolToBOOL(watchSubtree), notifyFilter);
  681     #ifdef WIN_LONG_PATH
  682     if (!IsHandleAllocated())
  683     {
  684       UString superPath;
  685       if (GetSuperPath(path, superPath, USE_MAIN_PATH))
  686         _handle = ::FindFirstChangeNotificationW(superPath, BoolToBOOL(watchSubtree), notifyFilter);
  687     }
  688     #endif
  689   }
  690   return _handle;
  691 }
  692 
  693 #ifndef UNDER_CE
  694 
  695 bool MyGetLogicalDriveStrings(CObjectVector<FString> &driveStrings)
  696 {
  697   driveStrings.Clear();
  698   #ifndef _UNICODE
  699   if (!g_IsNT)
  700   {
  701     driveStrings.Clear();
  702     UINT32 size = GetLogicalDriveStrings(0, NULL);
  703     if (size == 0)
  704       return false;
  705     CObjArray<char> buf(size);
  706     UINT32 newSize = GetLogicalDriveStrings(size, buf);
  707     if (newSize == 0 || newSize > size)
  708       return false;
  709     AString s;
  710     UINT32 prev = 0;
  711     for (UINT32 i = 0; i < newSize; i++)
  712     {
  713       if (buf[i] == 0)
  714       {
  715         s = buf + prev;
  716         prev = i + 1;
  717         driveStrings.Add(fas2fs(s));
  718       }
  719     }
  720     return prev == newSize;
  721   }
  722   else
  723   #endif
  724   {
  725     UINT32 size = GetLogicalDriveStringsW(0, NULL);
  726     if (size == 0)
  727       return false;
  728     CObjArray<wchar_t> buf(size);
  729     UINT32 newSize = GetLogicalDriveStringsW(size, buf);
  730     if (newSize == 0 || newSize > size)
  731       return false;
  732     UString s;
  733     UINT32 prev = 0;
  734     for (UINT32 i = 0; i < newSize; i++)
  735     {
  736       if (buf[i] == 0)
  737       {
  738         s = buf + prev;
  739         prev = i + 1;
  740         driveStrings.Add(us2fs(s));
  741       }
  742     }
  743     return prev == newSize;
  744   }
  745 }
  746 
  747 #endif
  748 
  749 }}}