1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using Microsoft.Win32; 6 using Microsoft.Win32.SafeHandles; 7 using System.Runtime.InteropServices; 8 9 namespace System.IO 10 { 11 internal static partial class InternalFile 12 { InternalExists(String path)13 internal static bool InternalExists(String path) 14 { 15 Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data = new Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA(); 16 int errorCode = FillAttributeInfo(path, ref data, false, true); 17 18 return (errorCode == 0) && (data.fileAttributes != -1) 19 && ((data.fileAttributes & Interop.Kernel32.FileAttributes.FILE_ATTRIBUTE_DIRECTORY) == 0); 20 } 21 22 // Returns 0 on success, otherwise a Win32 error code. Note that 23 // classes should use -1 as the uninitialized state for dataInitialized. FillAttributeInfo(String path, ref Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data, bool tryagain, bool returnErrorOnNotFound)24 internal static int FillAttributeInfo(String path, ref Interop.Kernel32.WIN32_FILE_ATTRIBUTE_DATA data, bool tryagain, bool returnErrorOnNotFound) 25 { 26 int errorCode = 0; 27 if (tryagain) // someone has a handle to the file open, or other error 28 { 29 Interop.Kernel32.WIN32_FIND_DATA findData; 30 findData = new Interop.Kernel32.WIN32_FIND_DATA(); 31 32 // Remove trailing slash since this can cause grief to FindFirstFile. You will get an invalid argument error 33 String tempPath = path.TrimEnd(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); 34 35 // For floppy drives, normally the OS will pop up a dialog saying 36 // there is no disk in drive A:, please insert one. We don't want that. 37 // SetThreadErrorMode will let us disable this, but we should set the error 38 // mode back, since this may have wide-ranging effects. 39 uint oldMode; 40 bool setThreadErrorModeSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode); 41 try 42 { 43 bool error = false; 44 SafeFindHandle handle = Interop.Kernel32.FindFirstFile(tempPath, ref findData); 45 try 46 { 47 if (handle.IsInvalid) 48 { 49 error = true; 50 errorCode = Marshal.GetLastWin32Error(); 51 52 if (errorCode == Interop.Errors.ERROR_FILE_NOT_FOUND || 53 errorCode == Interop.Errors.ERROR_PATH_NOT_FOUND || 54 errorCode == Interop.Errors.ERROR_NOT_READY) // floppy device not ready 55 { 56 if (!returnErrorOnNotFound) 57 { 58 // Return default value for backward compatibility 59 errorCode = 0; 60 data.fileAttributes = -1; 61 } 62 } 63 return errorCode; 64 } 65 } 66 finally 67 { 68 // Close the Win32 handle 69 try 70 { 71 handle.Dispose(); 72 } 73 catch 74 { 75 // if we're already returning an error, don't throw another one. 76 if (!error) 77 { 78 throw Win32Marshal.GetExceptionForLastWin32Error(); 79 } 80 } 81 } 82 } 83 finally 84 { 85 if (setThreadErrorModeSuccess) 86 Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode); 87 } 88 89 // Copy the information to data 90 data.PopulateFrom(ref findData); 91 } 92 else 93 { 94 // For floppy drives, normally the OS will pop up a dialog saying 95 // there is no disk in drive A:, please insert one. We don't want that. 96 // SetThreadErrorMode will let us disable this, but we should set the error 97 // mode back, since this may have wide-ranging effects. 98 bool success = false; 99 uint oldMode; 100 bool setThreadErrorModeSuccess = Interop.Kernel32.SetThreadErrorMode(Interop.Kernel32.SEM_FAILCRITICALERRORS, out oldMode); 101 try 102 { 103 success = Interop.Kernel32.GetFileAttributesEx(path, Interop.Kernel32.GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, ref data); 104 } 105 finally 106 { 107 if (setThreadErrorModeSuccess) 108 Interop.Kernel32.SetThreadErrorMode(oldMode, out oldMode); 109 } 110 111 if (!success) 112 { 113 errorCode = Marshal.GetLastWin32Error(); 114 if (errorCode != Interop.Errors.ERROR_FILE_NOT_FOUND && 115 errorCode != Interop.Errors.ERROR_PATH_NOT_FOUND && 116 errorCode != Interop.Errors.ERROR_NOT_READY) // floppy device not ready 117 { 118 // In case someone latched onto the file. Take the perf hit only for failure 119 return FillAttributeInfo(path, ref data, true, returnErrorOnNotFound); 120 } 121 else 122 { 123 if (!returnErrorOnNotFound) 124 { 125 // Return default value for backward compatibility 126 errorCode = 0; 127 data.fileAttributes = -1; 128 } 129 } 130 } 131 } 132 133 return errorCode; 134 } 135 } 136 } 137