파일작업은 흔하게 하는 작업이며, 몇몇 경우에 특정 에러를 발생한다. 이미 사용중인 파일에 대해 살펴보고, 에러를 피할 수 있는 방법을 살펴보자.
C# 파일작업
일반적으로 파일작업은 읽기, 쓰기, 파일 조작들의 동작이 관련되어 있다.
파일작업을 할 때, 여러 작업 또는 앱에서 동시에 같은 파일에 접근을 시도할 수 있다는 것을 알아야한다. 이러한 동시 접근은 충돌과 에러를 발생한다.
다음과 같은 여러 이유로 발생한다.
- 다른 앱에서 동시에 파일을 읽거나 쓰고 있을 때
- 파일을 열었다가 적절히 닫지 않았을 때
- 돌고있는 시스템이나 바이러스 프로그램에 의해 파일이 잡혀 있을 때
어떻게 이러한 오류를 줄이고 피할 수 있는지 알아보자.
Project 세팅
.NET CLI를 사용하여 console app을 생성하자.
에러재현을 위해 파일 생성 후, 제대로 닫지 않고 다시 접근해보자.
public static void ReadFile(string fileName)
{
File.Create(fileName);
try
{
var fileContents = File.ReadAllText(fileName);
Console.WriteLine("File contents: " + fileContents);
}
catch (IOException ex)
{
Console.WriteLine("Error reading the file: " + ex.Message);
}
Console.ReadLine();
}
ReadFile 함수 안에서 parameter로 받은 fileName으로 파일을 생성한다. using statement를 사용하지 않았기 때문에, new stream을 만들고 제대로 닫지 않는다.
파일이 준비되었으면, File.ReadAllText()로 파일 내용을 읽는다. 마지막으로 작업 후에도 output window 유지하도록 Console.ReadLine();을 실행한다.
앱을 실행하면 다음과 같은 에러가 나온다.
FileInUse.FileInUse.ReadFile($"{Directory.GetParent(Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory))?.Parent?.Parent?.Parent?.FullName}/FileInUse.txt");
//output :
//Error reading the file: The process cannot access the file 'D:\myproject\FileInUse.txt' because it is being used by another process.
File.Create() 함수 호출 후, 닫지 않은 open file stream에 접근하려 했기 때문에 이런 에러가 발생한다.
파일 사용여부 확인방법
exception 없이 한번에 파일 사용여부를 확인하고 파일작업을 할 수 있는 방법은 현재 없다.
IOException을 사용하여 파일 사용여부 확인
IsFileInUseGeneric()이라는 작은 helper method를 만든다.
public static bool IsFileInUseGeneric(FileInfo file)
{
try
{
using var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
catch (IOException)
{
return true;
}
return false;
}
위 함수를 이용해서, 파일을 읽기 전에 체크를 한다.
public static void ReadFile(string fileName)
{
File.Create(fileName);
if (IsFileInUseGeneric(new FileInfo(fileName)))
{
Console.WriteLine("File is in use by another process.");
}
else
{
try
{
var fileContents = File.ReadAllText(fileName);
Console.WriteLine("File contents: " + fileContents);
}
catch (IOException ex)
{
Console.WriteLine("Error reading the file: " + ex.Message);
}
}
Console.ReadLine();
}
HResult를 사용하여 파일 사용여부 확인
위 방법은 catch block에서 IOException을 잡는다. 동작을 하긴 하지만, file 사용 이외의 다른 이유로 IOException이 발생할 수 있다.
IOException의 HResult property를 사용하는 이 exception을 좀 더 정확하게 할 수 있다.
일반적으로, HResult는 32-bit value이며 3가지 영역으로 나뉜다.
- severity code : return value가 정보 또는 경고 또는 에러를 나타내는지 알려준다.
- lfacility code : error의 시스템적인 책임을 지는 구역
- error code : exception을 나타내는 unique number
이번 경우에는, error code를 확인한다.
public static bool IsFileInUse(FileInfo file)
{
try
{
using var stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32)
{
return true;
}
return false;
}
catch 에서 when 키워드를 사용해서 특정 exception 처리를 할 수 있다.
catch (ExceptionType [e]) when (expr)
expr이 true이면 catch statement가 실행되고 false이면 실행되지 않는다.
파일 공유 규칙 위반 시, 이 값은 32가 된다.
어떤 방법을 사용해야 하는가?
첫번째 방법은 에러 원인보다는 파일 접근을 할 수 있는지 여부를 확인할 때 적합하다.
두번째 방법은 파일접근할 수 없는 정확한 이유를 확인할때 적합하다.
'C#' 카테고리의 다른 글
C#] Virtual Method (0) | 2024.01.23 |
---|---|
C#] const vs readonly 차이점 (1) | 2024.01.19 |
C#] 식별자/변수명 네이밍 규칙, convention (0) | 2024.01.12 |
C#] Deprecated Method 표시, 미사용 함수 표시 (0) | 2024.01.09 |
C#] PeriodicTimer class (1) | 2024.01.09 |
댓글