파일이 사용 중인지 확인할 수 있는 방법이 있습니까?
1개의 이미지 파일에 반복적으로 접근해야 하는 프로그램을 C#로 작성하고 있습니다.대부분 작동하지만 컴퓨터가 빠르게 실행되는 경우 파일이 파일 시스템에 다시 저장되기 전에 파일에 액세스하려고 시도하고 다음 오류가 발생합니다.
"다른 프로세스에서 사용 중인 파일"
저는 이 문제를 해결할 방법을 찾고 싶지만, 저의 모든 구글링은 예외 처리를 사용하여 체크를 생성하는 것만 산출했습니다.이것은 저의 종교에 반하는 것입니다, 그래서 저는 그것을 하는 더 나은 방법을 가진 사람이 있나요?
이 솔루션에 대한 참고 업데이트:확인 중FileAccess.ReadWrite
파일에 이 " " " "로 확인하도록 되었습니다.FileAccess.Read
.
원본:저는 지난 몇 년 동안 이 코드를 사용했고, 문제가 없었습니다.
예외 사용에 대한 망설임을 이해하지만 항상 예외를 피할 수는 없습니다.
protected virtual bool IsFileLocked(FileInfo file)
{
try
{
using(FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
{
stream.Close();
}
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
//file is not locked
return false;
}
이 경우 스레드 경합 상태가 발생할 수 있으며, 보안 취약성으로 사용되는 문서화된 예가 있습니다.파일을 사용할 수 있는지 확인한 후 사용하려고 하면 해당 시점에 던질 수 있으며 악의적인 사용자가 이를 사용하여 코드를 강제로 사용하거나 악용할 수 있습니다.
가장 좋은 방법은 파일 처리를 시도하는 시도 캐치입니다.
try
{
using (Stream stream = new FileStream("MyFilename.txt", FileMode.Open))
{
// File/Stream manipulating code here
}
} catch {
//check here why it failed and ask user to retry if the file is in use.
}
이를 통해 파일이 잠겨 있는지 확인합니다.
using System.IO;
using System.Runtime.InteropServices;
internal static class Helper
{
const int ERROR_SHARING_VIOLATION = 32;
const int ERROR_LOCK_VIOLATION = 33;
private static bool IsFileLocked(Exception exception)
{
int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1);
return errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION;
}
internal static bool CanReadFile(string filePath)
{
//Try-Catch so we dont crash the program and can check the exception
try {
//The "using" is important because FileStream implements IDisposable and
//"using" will avoid a heap exhaustion situation when too many handles
//are left undisposed.
using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) {
if (fileStream != null) fileStream.Close(); //This line is me being overly cautious, fileStream will never be null unless an exception occurs... and I know the "using" does it but its helpful to be explicit - especially when we encounter errors - at least for me anyway!
}
}
catch (IOException ex) {
//THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
if (IsFileLocked(ex)) {
// do something, eg File.Copy or present the user with a MsgBox - I do not recommend Killing the process that is locking the file
return false;
}
}
finally
{ }
return true;
}
}
성능상의 이유로 동일한 작업에서 파일 내용을 읽는 것이 좋습니다.다음은 몇 가지 예입니다.
public static byte[] ReadFileBytes(string filePath)
{
byte[] buffer = null;
try
{
using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
int length = (int)fileStream.Length; // get file length
buffer = new byte[length]; // create buffer
int count; // actual number of bytes read
int sum = 0; // total number of bytes read
// read until Read method returns 0 (end of the stream has been reached)
while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
sum += count; // sum is a buffer offset for next reading
fileStream.Close(); //This is not needed, just me being paranoid and explicitly releasing resources ASAP
}
}
catch (IOException ex)
{
//THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
if (IsFileLocked(ex))
{
// do something?
}
}
catch (Exception ex)
{
}
finally
{
}
return buffer;
}
public static string ReadFileTextWithEncoding(string filePath)
{
string fileContents = string.Empty;
byte[] buffer;
try
{
using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
int length = (int)fileStream.Length; // get file length
buffer = new byte[length]; // create buffer
int count; // actual number of bytes read
int sum = 0; // total number of bytes read
// read until Read method returns 0 (end of the stream has been reached)
while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
{
sum += count; // sum is a buffer offset for next reading
}
fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP
//Depending on the encoding you wish to use - I'll leave that up to you
fileContents = System.Text.Encoding.Default.GetString(buffer);
}
}
catch (IOException ex)
{
//THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
if (IsFileLocked(ex))
{
// do something?
}
}
catch (Exception ex)
{
}
finally
{ }
return fileContents;
}
public static string ReadFileTextNoEncoding(string filePath)
{
string fileContents = string.Empty;
byte[] buffer;
try
{
using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
int length = (int)fileStream.Length; // get file length
buffer = new byte[length]; // create buffer
int count; // actual number of bytes read
int sum = 0; // total number of bytes read
// read until Read method returns 0 (end of the stream has been reached)
while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
{
sum += count; // sum is a buffer offset for next reading
}
fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP
char[] chars = new char[buffer.Length / sizeof(char) + 1];
System.Buffer.BlockCopy(buffer, 0, chars, 0, buffer.Length);
fileContents = new string(chars);
}
}
catch (IOException ex)
{
//THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
if (IsFileLocked(ex))
{
// do something?
}
}
catch (Exception ex)
{
}
finally
{
}
return fileContents;
}
직접 사용해 보십시오.
byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt");
저는 최근에 이 문제를 접했고 이것을 발견했습니다: https://learn.microsoft.com/en-us/dotnet/standard/io/handling-io-errors .
는 마이크로소프트의 "Microsoft"인지 에 대해 합니다.IOException
잠긴 파일 때문에 발생했습니다.
catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32 ) {
Console.WriteLine("There is a sharing violation.");
}
의도한 대로 예외를 사용합니다.파일이 사용 중임을 승인하고 작업이 완료될 때까지 반복하여 다시 시도합니다.또한 작업 전에 상태를 확인하는 데 시간을 낭비하지 않기 때문에 가장 효율적입니다.
예를 들어 아래 기능을 사용합니다.
TimeoutFileAction(() => { System.IO.File.etc...; return null; } );
2초 후에 시간이 초과되는 재사용 가능한 방법
private T TimeoutFileAction<T>(Func<T> func)
{
var started = DateTime.UtcNow;
while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
{
try
{
return func();
}
catch (System.IO.IOException exception)
{
//ignore, or log somewhere if you want to
}
}
return default(T);
}
파일 시스템 감시기를 사용하여 변경된 이벤트를 볼 수 있습니다.
저도 사용해 본 적은 없지만, 시도해 볼 만한 가치가 있을지도 모릅니다.이 경우에 파일 시스템 감시기가 조금 무겁다면, 저는 시도/캐치/슬립 루프를 선택할 것입니다.
위에서 수락한 답변은 파일 공유를 사용하여 쓰기 위해 파일이 열려 있는 경우 문제가 발생합니다.읽기 모드이거나 파일에 읽기 전용 특성이 있는 경우 코드가 작동하지 않습니다.이 수정된 솔루션은 다음 두 가지 사항을 염두에 두고 가장 안정적으로 작동합니다(승인된 솔루션에 대해서도 마찬가지임).
- 쓰기 공유 모드로 열린 파일에서는 작동하지 않습니다.
- 이는 스레드화 문제를 고려하지 않으므로 스레드화 문제를 잠그거나 별도로 처리해야 합니다.
위의 내용을 고려하여 파일이 쓰기 위해 잠겨 있는지 또는 읽기를 방지하기 위해 잠겨 있는지 확인합니다.
public static bool FileLocked(string FileName)
{
FileStream fs = null;
try
{
// NOTE: This doesn't handle situations where file is opened for writing by another process but put into write shared mode, it will not throw an exception and won't show it as write locked
fs = File.Open(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None); // If we can't open file for reading and writing then it's locked by another process for writing
}
catch (UnauthorizedAccessException) // https://msdn.microsoft.com/en-us/library/y973b725(v=vs.110).aspx
{
// This is because the file is Read-Only and we tried to open in ReadWrite mode, now try to open in Read only mode
try
{
fs = File.Open(FileName, FileMode.Open, FileAccess.Read, FileShare.None);
}
catch (Exception)
{
return true; // This file has been locked, we can't even open it to read
}
}
catch (Exception)
{
return true; // This file has been locked
}
finally
{
if (fs != null)
fs.Close();
}
return false;
}
스트림을 사용할 수 있게 되는 즉시 스트림을 제공하는 태스크를 반환할 수 있습니다.단순화된 솔루션이지만 좋은 출발점입니다.실이 안전합니다.
private async Task<Stream> GetStreamAsync()
{
try
{
return new FileStream("sample.mp3", FileMode.Open, FileAccess.Write);
}
catch (IOException)
{
await Task.Delay(TimeSpan.FromSeconds(1));
return await GetStreamAsync();
}
}
이 스트림은 일반적으로 사용할 수 있습니다.
using (var stream = await FileStreamGetter.GetStreamAsync())
{
Console.WriteLine(stream.Length);
}
3-라인을 사용하는 것 외에 참고용으로:전체 정보를 원하는 경우 Microsoft Dev Center에 대한 작은 프로젝트가 있습니다.
https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4
현재 위치: https://github.com/TacticalHorse/LockFinder/blob/master/LockFinder.cs
서론에서:
.NET Framework 4.0에서 개발된 C# 샘플 코드는 파일을 잠그는 프로세스를 찾는 데 도움이 됩니다.rstrtmgr.dll에 포함된 RmStartSession 함수를 사용하여 다시 시작 관리자 세션을 생성하고 반환 결과에 따라 Win32Exception 개체의 새 인스턴스가 생성됩니다.RmRegisterResources 함수를 통해 리소스를 재시작 관리자 세션에 등록한 후 RmGetList 함수를 호출하여 RM_PROCESS_INFO 배열을 열거하여 특정 파일을 사용하는 애플리케이션이 무엇인지 확인합니다.
재시작 관리자 세션에 연결하여 작동합니다.
재시작 관리자는 세션에 등록된 리소스 목록을 사용하여 종료 및 재시작해야 하는 애플리케이션 및 서비스를 결정합니다.리소스는 파일 이름, 서비스 짧은 이름 또는 실행 중인 애플리케이션을 설명하는 RM_UNIKE_PROCESS 구조로 식별할 수 있습니다.
당신의 특별한 요구에 좀 과하게 설계된 것일 수도 있습니다.하지만 원하는 것이 있다면 vs-project를 사용하십시오.
static bool FileInUse(string path) {
try {
using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate)) {
return !fs.CanWrite;
}
//return false;
}
catch (IOException ex) {
return true;
}
}
string filePath = "C:\\Documents And Settings\\yourfilename";
bool isFileInUse = FileInUse(filePath);
// Then you can do some checking
if (isFileInUse)
Console.WriteLine("File is in use");
else
Console.WriteLine("File is not in use");
이것이 도움이 되길 바랍니다!
제가 아는 유일한 방법은 너무 빠르지 않은 Win32 전용 잠금 API를 사용하는 것이지만, 예제가 있습니다.
대부분의 사람들은, 이것에 대한 간단한 해결책을 위해, 단순히 루프를 잡거나 잡거나 잠을 자려고 합니다.
다음 코드는 제가 가장 잘 아는 한 승인된 답변과 동일하지만 코드는 더 적습니다.
public static bool IsFileLocked(string file)
{
try
{
using (var stream = File.OpenRead(file))
return false;
}
catch (IOException)
{
return true;
}
}
그러나 다음과 같은 방식으로 하는 것이 더 강력하다고 생각합니다.
public static void TryToDoWithFileStream(string file, Action<FileStream> action,
int count, int msecTimeOut)
{
FileStream stream = null;
for (var i = 0; i < count; ++i)
{
try
{
stream = File.OpenRead(file);
break;
}
catch (IOException)
{
Thread.Sleep(msecTimeOut);
}
}
action(stream);
}
제 경험에 따르면, 여러분은 보통 이렇게 하고 싶을 것입니다. 그런 다음 파일을 '보호'하여 멋진 작업을 한 다음 '보호'된 파일을 사용합니다.이렇게 사용하고 싶은 파일이 하나만 있다면 제레미 톰슨의 답변에 설명된 트릭을 사용할 수 있습니다.그러나 설치 프로그램을 작성할 때와 같이 많은 파일에서 이 작업을 수행하려고 하면 상당한 부상을 입을 수 있습니다.
이 문제를 해결할 수 있는 가장 쉬운 방법은 파일 시스템에서 폴더 이름 중 하나를 사용하는 경우 폴더 이름을 변경할 수 없다는 점입니다.폴더를 동일한 파일 시스템에 보관하면 매력적으로 작동합니다.
이것이 악용될 수 있는 명백한 방법을 알고 있어야 합니다.결국 파일이 잠기지 않습니다.또한, 당신의 결과를 초래할 수 있는 다른 이유들이 있다는 것을 알아두세요.Move
실패하는 작업.분명히 적절한 오류 처리(MSDN)가 여기에 도움이 될 수 있습니다.
var originalFolder = @"c:\myHugeCollectionOfFiles"; // your folder name here
var someFolder = Path.Combine(originalFolder, "..", Guid.NewGuid().ToString("N"));
try
{
Directory.Move(originalFolder, someFolder);
// Use files
}
catch // TODO: proper exception handling
{
// Inform user, take action
}
finally
{
Directory.Move(someFolder, originalFolder);
}
개별 파일에 대해서는 Jeremy Thompson이 게시한 잠금 제안을 고수하겠습니다.
한때 PDF를 온라인 백업 보관소에 업로드해야 했습니다.그러나 사용자가 다른 프로그램(예: PDF 판독기)에서 파일을 열면 백업이 실패합니다.서둘러서, 저는 이 스레드에서 몇 가지 주요 답변을 시도했지만 그것들을 작동시킬 수 없었습니다.제게 효과가 있었던 것은 PDF 파일을 자신의 디렉터리로 옮기려고 시도한 것입니다.파일이 다른 프로그램에서 열려 있으면 이 작업이 실패하고 이동에 성공하면 별도의 디렉터리로 이동할 때처럼 복원 작업이 필요하지 않습니다.다른 사람들의 특정 사용 사례에 도움이 될 수도 있는 기본 솔루션을 게시하고 싶습니다.
string str_path_and_name = str_path + '\\' + str_filename;
FileInfo fInfo = new FileInfo(str_path_and_name);
bool open_elsewhere = false;
try
{
fInfo.MoveTo(str_path_and_name);
}
catch (Exception ex)
{
open_elsewhere = true;
}
if (open_elsewhere)
{
//handle case
}
내 라이브러리를 사용하여 여러 앱에서 파일에 액세스할 수 있습니다.
nuget에서 설치할 수 있습니다.설치-패키지 Xabe.파일락
자세한 내용은 https://github.com/tomaszzmuda/Xabe.FileLock 을 참조하십시오.
ILock fileLock = new FileLock(file);
if(fileLock.Acquire(TimeSpan.FromSeconds(15), true))
{
using(fileLock)
{
// file operations here
}
}
fileLock.Acquire 메서드는 이 개체에 대해 배타적인 파일을 잠글 수 있는 경우에만 true를 반환합니다.하지만 파일을 업로드하는 앱은 파일 잠금에서도 해야 합니다.개체에 액세스할 수 없는 경우 메서드가 false를 반환합니다.
파일을 임시 디렉토리로 이동/복사합니다.가능하면 잠금장치가 없고 잠금장치가 없어도 안전하게 임시 작업을 할 수 있습니다.그렇지 않으면 x초 안에 다시 이동합니다.
이런 것이 도움이 될까요?
var fileWasWrittenSuccessfully = false;
while (fileWasWrittenSuccessfully == false)
{
try
{
lock (new Object())
{
using (StreamWriter streamWriter = new StreamWriter("filepath.txt"), true))
{
streamWriter.WriteLine("text");
}
}
fileWasWrittenSuccessfully = true;
}
catch (Exception)
{
}
}
이 해결 방법을 사용하지만 IsFileLocked 기능으로 파일 잠금을 확인할 때와 파일을 열 때 사이에 시간이 걸립니다.이 기간 동안 다른 스레드가 파일을 열 수 있으므로 IO Exception을 받을 것입니다.
그래서, 저는 이것을 위해 코드를 추가했습니다.제 경우 XDocument를 로드합니다.
XDocument xDoc = null;
while (xDoc == null)
{
while (IsFileBeingUsed(_interactionXMLPath))
{
Logger.WriteMessage(Logger.LogPrioritet.Warning, "Deserialize can not open XML file. is being used by another process. wait...");
Thread.Sleep(100);
}
try
{
xDoc = XDocument.Load(_interactionXMLPath);
}
catch
{
Logger.WriteMessage(Logger.LogPrioritet.Error, "Load working!!!!!");
}
}
당신은 어떻게 생각하나요?제가 뭔가 바꿀 수 있을까요?IsFileBeingUsed 기능을 전혀 사용할 필요가 없었던 것은 아닐까요?
감사해요.
이것이 WTF 반사를 유발하는지 알고 싶습니다.콘솔 앱에서 PDF 문서를 만든 후 실행하는 프로세스가 있습니다.그러나 사용자가 프로세스를 여러 번 실행할 경우 이전에 생성된 파일을 먼저 닫지 않고 동일한 파일을 생성하면 앱이 예외를 던져 사망하는 취약성을 처리했습니다.파일 이름이 판매 견적 번호를 기반으로 하기 때문에 이러한 현상이 자주 발생했습니다.
이러한 무례한 방식으로 실패하기보다는 자동으로 파일 버전이 증가하는 것에 의존하기로 결정했습니다.
private static string WriteFileToDisk(byte[] data, string fileName, int version = 0)
{
try
{
var versionExtension = version > 0 ? $"_{version:000}" : string.Empty;
var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{fileName}{versionExtension}.pdf");
using (var writer = new FileStream(filePath, FileMode.Create))
{
writer.Write(data, 0, data.Length);
}
return filePath;
}
catch (IOException)
{
return WriteFileToDisk(data, fileName, ++version);
}
}
아마도 좀 더 주의를 기울일 수 있을 것입니다.catch
올바른 IO 예외를 파악할 수 있도록 차단합니다.이 파일들은 어차피 임시 파일이기 때문에 시작할 때 앱 저장소도 지울 것입니다.
저는 이것이 단순히 파일이 사용 중인지 확인하는 OP의 질문 범위를 넘어선다는 것을 알고 있습니다. 하지만 이것은 실제로 제가 이곳에 도착했을 때 해결하고자 했던 문제였기 때문에 아마도 다른 사람에게 유용할 것입니다.
retry_possibility:
//somecode here
try
{
using(FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
{
stream.Close();
}
//write or open your file here
}
catch (IOException)
{
DialogResult dialogResult = MessageBox.Show("This file is opened by you or another user. Please close it and press retry.\n"+ expFilePath, "File Locked", MessageBoxButtons.RetryCancel);
if (dialogResult == DialogResult.Retry)
{
goto retry_possibility;
}
else if (dialogResult == DialogResult.Cancel)
{
//do nothing
}
}
언급URL : https://stackoverflow.com/questions/876473/is-there-a-way-to-check-if-a-file-is-in-use
'programing' 카테고리의 다른 글
이클립스에서 닫힘 탭을 실행 취소하시겠습니까? (0) | 2023.05.31 |
---|---|
프로그램 설치를 위해 "예" 응답을 스크립팅하려면 어떻게 해야 합니까? (0) | 2023.05.31 |
SQL SELECT 속도 대 varchar (0) | 2023.05.31 |
아이패드와 아이폰의 애플터치 아이콘.png은 어떤 크기여야 합니까? (0) | 2023.05.31 |
신속한 선택적 이스케이프 클로저 매개변수 (0) | 2023.05.31 |