/// <summary> /// Ensure we have the data. /// Does not throw for nonexistence. /// </summary> private void EnsurePopulated() { if (_dataIsGood == null) { _dataIsGood = false; _filename = FileUtilities.AttemptToShortenPath(_filename); // This is no-op unless the path actually is too long _data = new NativeMethodsShared.WIN32_FILE_ATTRIBUTE_DATA(); // THIS COPIED FROM THE BCL: // // For floppy drives, normally the OS will pop up a dialog saying // there is no disk in drive A:, please insert one. We don't want that. // SetErrorMode will let us disable this, but we should set the error // mode back, since this may have wide-ranging effects. int oldMode = NativeMethodsShared.SetErrorMode(1 /* ErrorModes.SEM_FAILCRITICALERRORS */); bool success = false; _fileOrDirectoryExists = true; try { success = NativeMethodsShared.GetFileAttributesEx(_filename, 0, ref _data); if (!success) { int error = Marshal.GetLastWin32Error(); // File not found is the most common case, for example we're copying // somewhere without a file yet. Don't do something like FileInfo.Exists to // get a nice error, or we're doing IO again! Don't even format our own string: // that turns out to be unacceptably expensive here as well. Set a flag for this particular case. // // Also, when not under debugger (!) it will give error == 3 for path too long. Make that consistently throw instead. if ((error == 2 /* ERROR_FILE_NOT_FOUND */ || error == 3 /* ERROR_PATH_NOT_FOUND */) && _filename.Length <= NativeMethodsShared.MAX_PATH) { _fileOrDirectoryExists = false; return; } // Throw nice message as far as we can. At this point IO is OK. var length = new FileInfo(_filename).Length; // Otherwise this will give at least something NativeMethodsShared.ThrowExceptionForErrorCode(error); ErrorUtilities.ThrowInternalErrorUnreachable(); } } finally { NativeMethodsShared.SetErrorMode(oldMode); } _dataIsGood = true; } }
/// <summary> /// Constructor gets the data for the filename. /// On Win32 it uses native means. Otherwise, /// uses standard .NET FileInfo/DirInfo /// </summary> public FileDirInfo(string filename) { Exists = false; // If file/directory does not exist, return 12 midnight 1/1/1601. LastWriteTimeUtc = new DateTime(1601, 1, 1); _filename = FileUtilities.AttemptToShortenPath(filename); // This is no-op unless the path actually is too long int oldMode = 0; if (NativeMethodsShared.IsWindows) { // THIS COPIED FROM THE BCL: // // For floppy drives, normally the OS will pop up a dialog saying // there is no disk in drive A:, please insert one. We don't want that. // SetErrorMode will let us disable this, but we should set the error // mode back, since this may have wide-ranging effects. oldMode = NativeMethodsShared.SetErrorMode(1 /* ErrorModes.SEM_FAILCRITICALERRORS */); } try { if (NativeMethodsShared.IsWindows) { var data = new NativeMethodsShared.WIN32_FILE_ATTRIBUTE_DATA(); bool success = NativeMethodsShared.GetFileAttributesEx(_filename, 0, ref data); if (!success) { int error = Marshal.GetLastWin32Error(); // File not found is the most common case, for example we're copying // somewhere without a file yet. Don't do something like FileInfo.Exists to // get a nice error, or we're doing IO again! Don't even format our own string: // that turns out to be unacceptably expensive here as well. Set a flag for this particular case. // // Also, when not under debugger (!) it will give error == 3 for path too long. Make that consistently throw instead. if ((error == 2 /* ERROR_FILE_NOT_FOUND */ || error == 3 /* ERROR_PATH_NOT_FOUND */) && _filename.Length <= NativeMethodsShared.MAX_PATH) { Exists = false; return; } // Throw nice message as far as we can. At this point IO is OK. Length = new FileInfo(_filename).Length; // Otherwise this will give at least something NativeMethodsShared.ThrowExceptionForErrorCode(error); ErrorUtilities.ThrowInternalErrorUnreachable(); } Exists = true; IsDirectory = (data.fileAttributes & NativeMethodsShared.FILE_ATTRIBUTE_DIRECTORY) != 0; IsReadOnly = !IsDirectory && (data.fileAttributes & NativeMethodsShared.FILE_ATTRIBUTE_READONLY) != 0; LastWriteTimeUtc = DateTime.FromFileTimeUtc(((long)data.ftLastWriteTimeHigh << 0x20) | data.ftLastWriteTimeLow); Length = IsDirectory ? 0 : (((long)data.fileSizeHigh << 0x20) | data.fileSizeLow); } else { // Check if we have a directory IsDirectory = Directory.Exists(_filename); Exists = IsDirectory; // If not exists, see if this is a file if (!Exists) { Exists = File.Exists(_filename); } if (IsDirectory) { // Use DirectoryInfo to get the last write date var directoryInfo = new DirectoryInfo(_filename); IsReadOnly = false; LastWriteTimeUtc = directoryInfo.LastWriteTimeUtc; } else if (Exists) { // Use FileInfo to get readonly and last write date var fileInfo = new FileInfo(_filename); IsReadOnly = fileInfo.IsReadOnly; LastWriteTimeUtc = fileInfo.LastWriteTimeUtc; Length = fileInfo.Length; } } } catch (Exception ex) { // Save the exception thrown and assume the file does not exist _exceptionThrown = ex; Exists = false; } finally { // Reset the error mode on Windows if (NativeMethodsShared.IsWindows) { NativeMethodsShared.SetErrorMode(oldMode); } } }