2008-03-05 09:52:00 -05:00
|
|
|
// ==++==
|
|
|
|
//
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
//
|
|
|
|
// ==--==
|
2008-11-17 18:29:00 -05:00
|
|
|
//============================================================
|
|
|
|
//
|
|
|
|
// Class: File
|
|
|
|
//
|
|
|
|
// Purpose: A collection of methods for manipulating Files.
|
|
|
|
//
|
|
|
|
//===========================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
using System.Text;
|
|
|
|
using FileSystem.Utils;
|
|
|
|
using Microsoft.Singularity.Directory;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
namespace System.IO
|
|
|
|
{
|
2008-03-05 09:52:00 -05:00
|
|
|
// Class for creating FileStream objects, and some basic file management
|
|
|
|
// routines such as Delete, etc.
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File"]/*' />
|
|
|
|
public sealed class File
|
|
|
|
{
|
|
|
|
private const int GetFileExInfoStandard = 0;
|
|
|
|
|
|
|
|
private File()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.OpenText"]/*' />
|
|
|
|
public static StreamReader OpenText(String path)
|
|
|
|
{
|
|
|
|
if (path == null)
|
|
|
|
throw new ArgumentNullException("path");
|
|
|
|
return new StreamReader(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.CreateText"]/*' />
|
|
|
|
public static StreamWriter CreateText(String path)
|
|
|
|
{
|
|
|
|
if (path == null)
|
|
|
|
throw new ArgumentNullException("path");
|
|
|
|
return new StreamWriter(path,false);
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.AppendText"]/*' />
|
|
|
|
public static StreamWriter AppendText(String path)
|
|
|
|
{
|
|
|
|
if (path == null)
|
|
|
|
throw new ArgumentNullException("path");
|
|
|
|
return new StreamWriter(path,true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Copies an existing file to a new file. An exception is raised if the
|
|
|
|
// destination file already exists. Use the
|
|
|
|
// Copy(String, String, boolean) method to allow
|
|
|
|
// overwriting an existing file.
|
|
|
|
//
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.Copy"]/*' />
|
|
|
|
public static void Copy(String sourceFileName, String destFileName) {
|
|
|
|
Copy(sourceFileName, destFileName, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copies an existing file to a new file. If overwrite is
|
|
|
|
// false, then an IOException is thrown if the destination file
|
|
|
|
// already exists. If overwrite is true, the file is
|
|
|
|
// overwritten.
|
|
|
|
//
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.Copy1"]/*' />
|
|
|
|
public static void Copy(String sourceFileName, String destFileName, bool overwrite) {
|
|
|
|
InternalCopy(sourceFileName, destFileName,overwrite);
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.InternalCopy"]/*' />
|
|
|
|
/// <devdoc>
|
|
|
|
/// Note: This returns the fully qualified name of the destination file.
|
|
|
|
/// </devdoc>
|
|
|
|
internal static String! InternalCopy(String sourceFileName, String destFileName, bool overwrite) {
|
2008-11-17 18:29:00 -05:00
|
|
|
if (sourceFileName == null || destFileName == null)
|
2008-03-05 09:52:00 -05:00
|
|
|
throw new ArgumentNullException((sourceFileName==null ? "sourceFileName" : "destFileName"), "ArgumentNull_FileName");
|
2008-11-17 18:29:00 -05:00
|
|
|
if (sourceFileName.Length == 0 || destFileName.Length == 0)
|
2008-03-05 09:52:00 -05:00
|
|
|
throw new ArgumentException("Argument_EmptyFileName", (sourceFileName.Length==0 ? "sourceFileName" : "destFileName"));
|
|
|
|
|
|
|
|
String fullSourceFileName = Path.GetFullPathInternal(sourceFileName);
|
|
|
|
String fullDestFileName = Path.GetFullPathInternal(destFileName);
|
|
|
|
|
|
|
|
bool r = Native.CopyFile(fullSourceFileName, fullDestFileName, !overwrite);
|
|
|
|
if (!r) {
|
|
|
|
__Error.WinIOError(0, destFileName);
|
|
|
|
}
|
|
|
|
|
|
|
|
return fullDestFileName;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Creates a file in a particular path. If the file exists, it is replaced.
|
|
|
|
// The file is opened with ReadWrite accessand cannot be opened by another
|
|
|
|
// application until it has been closed. An IOException is thrown if the
|
|
|
|
// directory specified doesn't exist.
|
|
|
|
//
|
|
|
|
// Your application must have Create, Read, and Write permissions to
|
|
|
|
// the file.
|
|
|
|
//
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.Create"]/*' />
|
|
|
|
public static FileStream Create(String path) {
|
|
|
|
return Create(path, FileStream.DefaultBufferSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates a file in a particular path. If the file exists, it is replaced.
|
|
|
|
// The file is opened with ReadWrite access and cannot be opened by another
|
|
|
|
// application until it has been closed. An IOException is thrown if the
|
|
|
|
// directory specified doesn't exist.
|
|
|
|
//
|
|
|
|
// Your application must have Create, Read, and Write permissions to
|
|
|
|
// the file.
|
|
|
|
//
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.Create1"]/*' />
|
|
|
|
public static FileStream Create(String path, int bufferSize) {
|
|
|
|
return new FileStream(path, FileMode.Create, FileAccess.ReadWrite,
|
|
|
|
FileShare.None, bufferSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deletes a file. The file specified by the designated path is deleted.
|
|
|
|
// If the file does not exist, Delete succeeds without throwing
|
|
|
|
// an exception.
|
|
|
|
//
|
|
|
|
// On NT, Delete will fail for a file that is open for normal I/O
|
|
|
|
// or a file that is memory mapped.
|
|
|
|
//
|
|
|
|
// Your application must have Delete permission to the target file.
|
|
|
|
//
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.Delete"]/*' />
|
|
|
|
public static void Delete(String path) {
|
2008-11-17 18:29:00 -05:00
|
|
|
if (path == null)
|
2008-03-05 09:52:00 -05:00
|
|
|
throw new ArgumentNullException("path");
|
|
|
|
|
|
|
|
String fullPath = Path.GetFullPathInternal(path);
|
|
|
|
ErrorCode error;
|
|
|
|
bool r = Native.DeleteFile(fullPath, out error);
|
|
|
|
if (!r) {
|
|
|
|
//int err = __Error.ErrorCodeToWin32Error(error);
|
|
|
|
//__Error.WinIOError(err, path);
|
2008-11-17 18:29:00 -05:00
|
|
|
__Error.SingularityIOError(error,path);
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tests if a file exists. The result is true if the file
|
|
|
|
// given by the specified path exists; otherwise, the result is
|
|
|
|
// false. Note that if path describes a directory,
|
|
|
|
// Exists will return true.
|
|
|
|
//
|
|
|
|
// Your application must have Read permission for the target directory.
|
|
|
|
//
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.Exists"]/*' />
|
2008-11-17 18:29:00 -05:00
|
|
|
public static bool Exists(String! path)
|
2008-03-05 09:52:00 -05:00
|
|
|
{
|
|
|
|
DirectoryServiceContract.Imp dsRoot = DirectoryService.NewClientEndpoint();
|
|
|
|
if (dsRoot == null) throw new Exception("No directory service endpoint.");
|
|
|
|
bool ok = Exists(dsRoot, path);
|
2008-11-17 18:29:00 -05:00
|
|
|
delete dsRoot;
|
|
|
|
return ok;
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
public static bool Exists(DirectoryServiceContract.Imp:Ready! dsRoot, String! path) {
|
2008-11-17 18:29:00 -05:00
|
|
|
try {
|
2008-03-05 09:52:00 -05:00
|
|
|
path = Path.GetFullPathInternal(path);
|
2008-11-17 18:29:00 -05:00
|
|
|
if (path == null)
|
2008-03-05 09:52:00 -05:00
|
|
|
return false;
|
2008-11-17 18:29:00 -05:00
|
|
|
if (path.Length == 0)
|
2008-03-05 09:52:00 -05:00
|
|
|
return false;
|
2008-11-17 18:29:00 -05:00
|
|
|
bool exists = FileUtils.FileExists(dsRoot, (!)path);
|
|
|
|
|
|
|
|
#if false // uncomment to debug file access.
|
|
|
|
Console.WriteLine("FileExists({0}) = {1}", path, exists);
|
|
|
|
#endif
|
|
|
|
return exists;
|
|
|
|
}
|
|
|
|
catch (ArgumentException) {
|
|
|
|
|
|
|
|
}
|
|
|
|
catch (NotSupportedException) {} // To deal with the fact that security can now throw this on :
|
|
|
|
catch (IOException) {
|
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
internal static bool InternalExists(String path) {
|
2008-03-05 09:52:00 -05:00
|
|
|
Native.FILE_ATTRIBUTE_DATA data = new Native.FILE_ATTRIBUTE_DATA();
|
|
|
|
int dataInitialised = FillAttributeInfo(path,ref data);
|
|
|
|
if (dataInitialised != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return (data.fileAttributes & Native.FILE_ATTRIBUTE_DIRECTORY) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.Open"]/*' />
|
|
|
|
public static FileStream Open(String path,FileMode mode) {
|
|
|
|
return Open(path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.None);
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.Open1"]/*' />
|
|
|
|
public static FileStream Open(String path,FileMode mode, FileAccess access) {
|
|
|
|
return Open(path,mode, access, FileShare.None);
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.Open2"]/*' />
|
|
|
|
public static FileStream Open(String path, FileMode mode, FileAccess access, FileShare share) {
|
|
|
|
return new FileStream(path, mode, access, share);
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.SetCreationTime"]/*' />
|
|
|
|
public static void SetCreationTime(String path, DateTime creationTime)
|
|
|
|
{
|
|
|
|
SetCreationTimeUtc(path, creationTime.ToUniversalTime());
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.SetCreationTimeUtc"]/*' />
|
|
|
|
public static void SetCreationTimeUtc(String path, DateTime creationTimeUtc)
|
|
|
|
{
|
|
|
|
IntPtr handle = IntPtr.Zero;
|
|
|
|
FileStream fs = OpenFile(path, FileAccess.Write, ref handle);
|
|
|
|
|
|
|
|
bool r = Native.SetFileTime(handle, new long[] {creationTimeUtc.ToFileTimeUtc()}, null, null);
|
2008-11-17 18:29:00 -05:00
|
|
|
if (!r) {
|
2008-03-05 09:52:00 -05:00
|
|
|
fs.Close();
|
|
|
|
__Error.WinIOError(0, path);
|
|
|
|
}
|
|
|
|
fs.Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.GetCreationTimeUtc"]/*' />
|
|
|
|
public static DateTime GetCreationTimeUtc(String! path)
|
|
|
|
{
|
|
|
|
String fullPath = Path.GetFullPathInternal(path);
|
|
|
|
|
|
|
|
Native.FILE_ATTRIBUTE_DATA data = new Native.FILE_ATTRIBUTE_DATA();
|
|
|
|
int dataInitialised = FillAttributeInfo(fullPath,ref data);
|
|
|
|
if (dataInitialised != 0)
|
|
|
|
__Error.WinIOError(dataInitialised, path);
|
|
|
|
|
|
|
|
if (data.fileAttributes == -1)
|
|
|
|
throw new IOException(String.Format("IO.PathNotFound_Path", path));
|
|
|
|
|
|
|
|
return DateTime.FromFileTimeUtc(data.ftCreationTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.SetLastAccessTimeUtc"]/*' />
|
|
|
|
public static void SetLastAccessTimeUtc(String path, DateTime lastAccessTimeUtc)
|
|
|
|
{
|
|
|
|
IntPtr handle = IntPtr.Zero;
|
|
|
|
FileStream fs = OpenFile(path, FileAccess.Write, ref handle);
|
|
|
|
|
|
|
|
bool r = Native.SetFileTime(handle, null, new long[] {lastAccessTimeUtc.ToFileTimeUtc()}, null);
|
2008-11-17 18:29:00 -05:00
|
|
|
if (!r) {
|
2008-03-05 09:52:00 -05:00
|
|
|
fs.Close();
|
|
|
|
__Error.WinIOError(0, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
fs.Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.GetLastAccessTimeUtc"]/*' />
|
|
|
|
public static DateTime GetLastAccessTimeUtc(String! path)
|
|
|
|
{
|
|
|
|
String fullPath = Path.GetFullPathInternal(path);
|
|
|
|
|
|
|
|
Native.FILE_ATTRIBUTE_DATA data = new Native.FILE_ATTRIBUTE_DATA();
|
|
|
|
int dataInitialised = FillAttributeInfo(fullPath,ref data);
|
|
|
|
if (dataInitialised != 0)
|
|
|
|
__Error.WinIOError(dataInitialised, path);
|
|
|
|
|
|
|
|
if (data.fileAttributes == -1)
|
|
|
|
throw new IOException(String.Format("IO.PathNotFound_Path", path));
|
|
|
|
|
|
|
|
return DateTime.FromFileTimeUtc(data.ftLastAccessTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.SetLastWriteTimeUtc"]/*' />
|
|
|
|
public static void SetLastWriteTimeUtc(String path, DateTime lastWriteTimeUtc)
|
|
|
|
{
|
|
|
|
IntPtr handle = IntPtr.Zero;
|
|
|
|
FileStream fs = OpenFile(path, FileAccess.Write, ref handle);
|
|
|
|
|
|
|
|
bool r = Native.SetFileTime(handle, null, null, new long[] {lastWriteTimeUtc.ToFileTimeUtc()});
|
2008-11-17 18:29:00 -05:00
|
|
|
if (!r) {
|
2008-03-05 09:52:00 -05:00
|
|
|
fs.Close();
|
|
|
|
__Error.WinIOError(0, path);
|
|
|
|
}
|
|
|
|
fs.Close();
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
public static DateTime GetLastWriteTime(String! path)
|
|
|
|
{
|
|
|
|
//TODO: FIXFIX need to convert to local time some day
|
|
|
|
return GetLastWriteTimeUtc(path);
|
|
|
|
}
|
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.GetLastWriteTimeUtc"]/*' />
|
|
|
|
public static DateTime GetLastWriteTimeUtc(String! path)
|
|
|
|
{
|
|
|
|
String fullPath = Path.GetFullPathInternal(path);
|
|
|
|
|
|
|
|
Native.FILE_ATTRIBUTE_DATA data = new Native.FILE_ATTRIBUTE_DATA();
|
|
|
|
int dataInitialised = FillAttributeInfo(fullPath,ref data);
|
|
|
|
if (dataInitialised != 0)
|
|
|
|
__Error.WinIOError(dataInitialised, path);
|
|
|
|
|
|
|
|
if (data.fileAttributes == -1)
|
|
|
|
throw new IOException(String.Format("IO.PathNotFound_Path", path));
|
|
|
|
|
|
|
|
return DateTime.FromFileTimeUtc(data.ftLastWriteTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.GetAttributes"]/*' />
|
|
|
|
public static FileAttributes GetAttributes(String! path)
|
|
|
|
{
|
|
|
|
String fullPath = Path.GetFullPathInternal(path);
|
|
|
|
|
|
|
|
Native.FILE_ATTRIBUTE_DATA data = new Native.FILE_ATTRIBUTE_DATA();
|
|
|
|
int dataInitialised = FillAttributeInfo(fullPath,ref data);
|
|
|
|
if (dataInitialised != 0)
|
|
|
|
__Error.WinIOError(dataInitialised, path);
|
|
|
|
|
|
|
|
if (data.fileAttributes == -1)
|
|
|
|
__Error.WinIOError(Native.ERROR_FILE_NOT_FOUND, path);
|
|
|
|
|
|
|
|
return (FileAttributes) data.fileAttributes;
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.SetAttributes"]/*' />
|
|
|
|
public static void SetAttributes(String! path,FileAttributes fileAttributes)
|
|
|
|
{
|
|
|
|
String fullPath = Path.GetFullPathInternal(path);
|
|
|
|
bool r = Native.SetFileAttributes(fullPath, (int) fileAttributes);
|
|
|
|
if (!r) {
|
|
|
|
__Error.WinIOError(0, path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.OpenRead"]/*' />
|
|
|
|
public static FileStream OpenRead(String path) {
|
|
|
|
return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.OpenWrite"]/*' />
|
|
|
|
public static FileStream OpenWrite(String path) {
|
|
|
|
return new FileStream(path, FileMode.OpenOrCreate,
|
|
|
|
FileAccess.Write, FileShare.None);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Moves a specified file to a new location and potentially a new file name.
|
|
|
|
// This method does work across volumes.
|
|
|
|
//
|
|
|
|
//| <include file='doc\File.uex' path='docs/doc[@for="File.Move"]/*' />
|
|
|
|
public static void Move(String sourceFileName, String destFileName) {
|
2008-11-17 18:29:00 -05:00
|
|
|
if (sourceFileName == null || destFileName == null)
|
2008-03-05 09:52:00 -05:00
|
|
|
throw new ArgumentNullException((sourceFileName==null ? "sourceFileName" : "destFileName"), "ArgumentNull_FileName");
|
2008-11-17 18:29:00 -05:00
|
|
|
if (sourceFileName.Length == 0 || destFileName.Length == 0)
|
2008-03-05 09:52:00 -05:00
|
|
|
throw new ArgumentException("Argument_EmptyFileName", (sourceFileName.Length==0 ? "sourceFileName" : "destFileName"));
|
|
|
|
|
|
|
|
String fullSourceFileName = Path.GetFullPathInternal(sourceFileName);
|
|
|
|
String fullDestFileName = Path.GetFullPathInternal(destFileName);
|
|
|
|
|
|
|
|
if (!InternalExists(fullSourceFileName))
|
|
|
|
__Error.WinIOError(Native.ERROR_FILE_NOT_FOUND,sourceFileName);
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
if (!Native.MoveFile(fullSourceFileName, fullDestFileName)) {
|
2008-03-05 09:52:00 -05:00
|
|
|
__Error.WinIOError(0, destFileName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal static int FillAttributeInfo(String path, ref Native.FILE_ATTRIBUTE_DATA data)
|
|
|
|
{
|
|
|
|
int dataInitialised = 0;
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
bool success = Native.GetFileAttributesEx(path, GetFileExInfoStandard, ref data);
|
|
|
|
|
|
|
|
if (!success) {
|
|
|
|
data.fileAttributes = -1;
|
|
|
|
dataInitialised = 0;
|
|
|
|
}
|
|
|
|
return dataInitialised;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static FileStream! OpenFile(String path, FileAccess access, ref IntPtr handle)
|
|
|
|
{
|
|
|
|
return new FileStream(path, FileMode.Open, access, FileShare.ReadWrite, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Defined in WinError.h
|
|
|
|
private const int ERROR_INVALID_PARAMETER = 87;
|
|
|
|
private const int ERROR_ACCESS_DENIED = 0x5;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|