มีวิธีการตรวจสอบว่าไฟล์มีการใช้งานหรือไม่?


846

ฉันกำลังเขียนโปรแกรมใน C # ที่จำเป็นต้องเข้าถึงไฟล์ภาพ 1 ไฟล์ซ้ำ ๆ เวลาส่วนใหญ่ของการทำงาน แต่หากคอมพิวเตอร์ของฉันทำงานได้อย่างรวดเร็วก็จะพยายามที่จะเข้าถึงไฟล์ก่อนที่มันจะได้รับการบันทึกกลับไปที่ระบบแฟ้มและโยนข้อผิดพลาด: "แฟ้มในการใช้งานโดยกระบวนการอื่น"

ฉันต้องการหาวิธีแก้ไขปัญหานี้ แต่ Googling ทั้งหมดของฉันให้ผลการตรวจสอบโดยใช้ข้อยกเว้นเท่านั้น นี่เป็นสิ่งที่ขัดกับศาสนาของฉันดังนั้นฉันจึงสงสัยว่าใครมีวิธีที่ดีกว่าในการทำมัน?


30
เอาล่ะคุณสามารถทดสอบได้โดยตรวจสอบจุดจับเปิดทั้งหมดในระบบ อย่างไรก็ตามเนื่องจาก Windows เป็นระบบปฏิบัติการหลายภารกิจจึงมีโอกาสที่จะเกิดขึ้นทันทีหลังจากที่คุณเรียกใช้รหัสเพื่อตรวจสอบว่าไฟล์เปิดอยู่หรือไม่และคุณเห็นว่าไม่ใช่รหัสกระบวนการจะเริ่มต้นใช้ไฟล์นั้นจากนั้นตามเวลาที่คุณพยายาม ใช้มันคุณจะได้รับข้อผิดพลาด แต่ไม่มีอะไรผิดปกติในการตรวจสอบก่อน อย่าเพิ่งคิดว่ามันไม่ได้ใช้งานเมื่อคุณต้องการมันจริงๆ
BobbyShaftoe

3
แต่สำหรับปัญหาเฉพาะนี้เท่านั้น ฉันขอแนะนำไม่ให้ตรวจสอบการจัดการไฟล์และลองใช้จำนวนครั้งที่กำหนดไว้ล่วงหน้าโดยบอกว่า 3-5 ก่อนที่จะล้มเหลว
BobbyShaftoe

ไฟล์รูปภาพนี้สร้างขึ้นได้อย่างไร? คุณสามารถหยุด / พัก / หยุดโปรแกรมชั่วคราวจนกว่าการสร้างจะเสร็จสมบูรณ์หรือไม่ นั่นคือวิธีที่เหนือกว่าในการจัดการกับสถานการณ์ ถ้าไม่เช่นนั้นฉันไม่คิดว่าคุณสามารถหลีกเลี่ยงการใช้การจัดการข้อยกเว้นได้
Catchwa

การใช้ข้อยกเว้นทั้งหมดไม่ใช่การตรวจสอบสมมติฐานบางอย่างด้วยการทำสิ่งที่อาจเป็นอันตรายในขณะที่จงใจไม่พิจารณาความเป็นไปได้ของความล้มเหลวหรือไม่?
jwg

26
ปรัชญาของคุณมีความเข้าใจที่ดีเกี่ยวกับข้อยกเว้น คนส่วนใหญ่คิดว่าข้อยกเว้นหมายถึงศักดิ์สิทธิ์อึออกจากการลงโทษบางสิ่งผิดปกติตายตาย เมื่อข้อยกเว้นหมายถึง .... ข้อยกเว้น มันหมายถึงบางสิ่งที่พิเศษที่คุณต้อง "จัดการ" (หรือบัญชี) บางทีคุณอาจต้องการลองเข้าถึงข้อมูลอีกครั้งบางทีผู้ใช้อาจต้องรู้ว่าคุณไม่สามารถเชื่อมต่อได้ คุณทำอะไร? คุณจัดการ ConnectionFailedException และแจ้งผู้ใช้ดังนั้นบางทีพวกเขาจะหยุดพยายามหลังจากผ่านไปหนึ่งชั่วโมงและสังเกตว่าปลดสายเคเบิลแล้ว
Lee Louviere

คำตอบ:


542

Updated หมายเหตุในการแก้ปัญหานี้ : การตรวจสอบด้วยจะล้มเหลวสำหรับไฟล์อ่านอย่างเดียวเพื่อแก้ปัญหาได้รับการแก้ไขตรวจสอบกับFileAccess.ReadWrite FileAccess.Readในขณะที่วิธีนี้ใช้งานได้เนื่องจากการพยายามตรวจสอบด้วยFileAccess.Readจะล้มเหลวหากไฟล์มีล็อคการเขียนหรืออ่านอย่างไรก็ตามวิธีนี้จะไม่ทำงานหากไฟล์ไม่มีล็อคการเขียนหรืออ่านอยู่เช่นเปิดแล้ว (สำหรับการอ่านหรือการเขียน) ด้วยการเข้าถึง FileShare อ่านหรือ FileShare.Write

ORIGINAL: ฉันใช้รหัสนี้มาหลายปีแล้วและฉันก็ไม่มีปัญหาอะไรกับมัน

เข้าใจความลังเลของคุณเกี่ยวกับการใช้ข้อยกเว้น แต่คุณไม่สามารถหลีกเลี่ยงได้ตลอดเวลา:

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;
}

60
นี่เป็นทางออกที่ดี แต่ฉันมีหนึ่งความคิดเห็น - คุณไม่สามารถเปิดไฟล์ด้วยโหมดการเข้าถึง FileAccess ได้อ่านเนื่องจาก ReadWrite จะล้มเหลวเสมอหากไฟล์เกิดขึ้นเป็นแบบอ่านอย่างเดียว
adeel825

220
-1 นี่เป็นคำตอบที่ไม่ดีเพราะไฟล์อาจถูกล็อคโดยเธรด / กระบวนการอื่นหลังจากที่มันถูกปิดใน IsFileLocked และก่อนที่เธรดของคุณจะมีโอกาสเปิดมัน
Polyfun

16
ฉันคิดว่านี่เป็นคำตอบที่ดี ฉันใช้นี้เป็นวิธีขยาย a public static bool IsLocked(this FileInfo file) {/*...*/}la
Manuzor

54
@ChrisW: คุณอาจสงสัยว่าเกิดอะไรขึ้น อย่าตื่นตระหนก คุณเป็นเพียงความโกรธเคืองของชุมชน WTF รายวัน: thedailywtf.com/Comments/…
Pierre Lebeaupin

16
@ ChrisW ทำไมเป็นสิ่งที่ไม่ดี ชุมชนนี้อยู่ที่นี่เพื่อชี้ให้เห็นคำตอบที่ดีและไม่ดี หากผู้เชี่ยวชาญหลายคนสังเกตเห็นว่านี่เป็นสิ่งที่ไม่ดีและเข้าร่วมกับ downvote แสดงว่าไซต์นั้นเป็น WAI และก่อนที่คุณจะลบถ้าคุณอ่านบทความพวกเขาพูดกับ "upvote คำตอบที่ถูกต้อง" ไม่ใช่ downvote ผิด คุณต้องการให้พวกเขาอธิบาย upvotes ของพวกเขาในความคิดเห็นเช่นกัน ขอบคุณสำหรับการแนะนำฉันไปยังเว็บไซต์ที่ดีอีก!
Lee Louviere

569

คุณสามารถทนทุกข์ทรมานจากสภาพการแข่งขันของเธรดซึ่งมีตัวอย่างเอกสารเกี่ยวกับสิ่งนี้ที่ใช้เป็นช่องโหว่ด้านความปลอดภัย หากคุณตรวจสอบว่ามีไฟล์อยู่ให้ลองใช้แล้วคุณสามารถโยนไฟล์นั้นไปยังจุดที่ผู้ใช้ที่ประสงค์ร้ายสามารถใช้เพื่อบังคับและใช้ประโยชน์จากโค้ดของคุณ

ทางออกที่ดีที่สุดของคุณคือลองจับ / ในที่สุดซึ่งพยายามที่จะได้รับการจัดการไฟล์

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.
}

124
+1 ไม่มีวิธีที่ปลอดภัย 100% ที่จะ "เรียนรู้ว่าไฟล์นั้นถูกใช้งาน" เพราะมิลลิวินาทีหลังจากที่คุณทำการตรวจสอบไฟล์อาจไม่ได้ใช้งานอีกต่อไปหรือในทางกลับกัน คุณเพียงแค่เปิดไฟล์และใช้งานหากไม่มีข้อยกเว้น
Sedat Kapanoglu

8
. NET ที่แย่เกินไปไม่รองรับ CAS บางอย่างเช่น TryOpenFile (Ref FileHandle) ที่ส่งคืนความสำเร็จ / ล้มเหลว ควรมีการแก้ไขโดยไม่พึ่งพาการจัดการยกเว้นเพียงอย่างเดียว ฉันสงสัยว่า Microsoft Office ทำงานอย่างไร
TamusJRoyce

2
สิ่งสำคัญที่ต้องทำความเข้าใจที่นี่คือ API นี้ใช้เพียงแค่ windows API เพื่อรับการจัดการไฟล์ เช่นนี้พวกเขาจำเป็นต้องแปลรหัสข้อผิดพลาดที่ได้รับจาก C API และตัดเป็นข้อยกเว้นที่จะโยน เรามีข้อยกเว้นในการจัดการ. Net ดังนั้นทำไมไม่ใช้มัน ด้วยวิธีนี้คุณสามารถเขียนพา ธ ไปข้างหน้าใหม่ในรหัสของคุณและปล่อยให้การจัดการข้อผิดพลาดในเส้นทางรหัสแยก
Spence

36
การใช้คำสั่งคือเพื่อให้แน่ใจว่ากระแสถูกปิดหลังจากที่ฉันทำ ฉันคิดว่าคุณจะพบว่าการใช้ () {} มีอักขระน้อยกว่าลอง {} ในที่สุด {obj.Dispose ()} คุณจะพบว่าตอนนี้คุณต้องประกาศการอ้างอิงวัตถุของคุณนอกคำสั่งการใช้ซึ่งเป็นการพิมพ์ที่มากกว่า หากคุณมีอินเทอร์เฟซที่ชัดเจนคุณจะต้องส่ง ในที่สุดคุณต้องการกำจัด ASAP และตรรกะในที่สุดอาจมี UI หรือการดำเนินการที่ใช้เวลานานอื่น ๆ ที่มีน้อยจะทำอย่างไรกับการโทร IDispose </rant>
Spence

2
ที่ไม่ได้ลบล้างความจริงที่ว่าคุณต้องประกาศวัตถุของคุณนอกความพยายามและต้องเรียกการกำจัดอย่างชัดเจนซึ่งการใช้ทำเพื่อคุณและหมายถึงสิ่งเดียวกัน
Spence

92

ใช้สิ่งนี้เพื่อตรวจสอบว่าไฟล์ถูกล็อคหรือไม่:

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");

8
ฉันอยากโหวตถ้าไม่มี "หมายเลขเวทมนตร์" ในนั้นen.wikipedia.org/wiki/Magic_number_(programming)
Kris

3
ฉันอ้างถึงการเปรียบเทียบ errorCode ไม่ใช่บิตกะ แม้ว่าตอนนี้คุณพูดถึงมัน ...
คริส

1
Your Catch ควรเป็น On IOExceptionแทนที่จะเป็นแบบทั่วไปExceptionแล้วทดสอบตามประเภท
Askolein

3
@ JeremyThompson เศร้าที่คุณใส่เฉพาะIOExceptionหลังจากที่คนทั่วไป คนทั่วไปจะจับทุกอย่างที่ผ่านไปและที่เฉพาะเจาะจงIOExceptionจะเหงาเสมอ เพียงแค่สลับทั้งสอง
Askolein

ฉันชอบวิธีนี้ อีกหนึ่งข้อเสนอแนะ: ภายในการจับเป็นอย่างอื่นหาก (IsFileLocked (อดีต)) ฉันจะโยนอดีต นี่จะจัดการกับกรณีที่ไม่มีไฟล์ (หรือ IOException อื่น ๆ ) โดยการโยนข้อยกเว้น
shindigo

7

เพียงใช้ข้อยกเว้นตามที่ตั้งใจไว้ ยอมรับว่าไฟล์ดังกล่าวถูกใช้งานอยู่และลองอีกครั้งซ้ำ ๆ จนกว่าการกระทำของคุณจะเสร็จสิ้น นอกจากนี้ยังมีประสิทธิภาพมากที่สุดเพราะคุณไม่ต้องเสียเวลาในการตรวจสอบสถานะก่อนที่จะทำอะไร

ใช้ฟังก์ชั่นด้านล่างเช่น

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);
}

6

บางทีคุณสามารถใช้FileSystemWatcherและเฝ้าดูการเปลี่ยนแปลงของเหตุการณ์

ฉันไม่ได้ใช้สิ่งนี้ด้วยตัวเอง แต่มันอาจจะคุ้มค่ากับการยิง หาก filesystemwatcher กลายเป็นบิตหนักสำหรับกรณีนี้ฉันจะไปลอง / catch / sleep loop


1
การใช้ FileSystemWatcher ไม่ได้ช่วยเพราะเหตุการณ์ที่สร้างและเปลี่ยนแปลงเกิดขึ้นที่จุดเริ่มต้นของการสร้าง / เปลี่ยนแปลงไฟล์ แม้แต่ไฟล์ขนาดเล็กก็ต้องการเวลาในการเขียนและปิดระบบปฏิบัติการมากกว่าแอปพลิเคชั่น. NET ที่ต้องใช้งานผ่านทาง FileSystemEventHandler มันช่างเศร้าเหลือเกิน แต่ไม่มีตัวเลือกอื่นนอกจากการประมาณเวลารอก่อนที่จะเข้าถึงไฟล์หรือทำงานเป็นห่วงลูป ...

FileSystemWatcher ไม่จัดการกับการเปลี่ยนแปลงจำนวนมากในเวลาเดียวกันได้เป็นอย่างดีดังนั้นโปรดระมัดระวังด้วย
เบ็น F

1
BTW มีพวกคุณสังเกตเห็นในขณะที่การแก้จุดบกพร่องและดูหัวข้อที่ MS เรียก FSW "FileSystemWather" FSW ของตัวเอง? อะไรกันแน่?
devlord

แม่นยำฉันมีปัญหานี้เพราะบริการ windows กับ FileSystemWatcher พยายามอ่านไฟล์ก่อนที่กระบวนการจะปิด
พัฒนาฟรี

6

คุณสามารถส่งคืนงานที่ให้กระแสข้อมูลแก่คุณทันทีที่พร้อมใช้งาน มันเป็นทางออกที่ง่าย แต่มันก็เป็นจุดเริ่มต้นที่ดี มันด้ายปลอดภัย

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
กี่วินาทีจนกระทั่งสแต็คล้นจากการเรียกซ้ำGetStreamAsync()?
CAD bloke

@CADbloke คุณได้จุดดีมาก อันที่จริงตัวอย่างของฉันอาจมีข้อยกเว้นสแตกล้นในกรณีที่ไฟล์ไม่พร้อมใช้งานเป็นเวลานาน เกี่ยวข้องกับคำตอบนี้stackoverflow.com/questions/4513438/…อาจทำให้เกิดข้อยกเว้นใน 5 ชั่วโมง
Ivan Branets

เกี่ยวข้องกับกรณีการใช้งานของคุณจะดีกว่าที่จะโยนข้อยกเว้น I / O หากสมมุติว่า 10 ความพยายามในการอ่านไฟล์ล้มเหลว กลยุทธ์อื่นอาจเพิ่มเวลาการรอคอยเป็นครั้งที่สองเมื่อความพยายาม 10 ครั้งล้มเหลว คุณยังสามารถใช้ทั้งสองอย่างผสมผสานกันได้
Ivan Branets

ฉันจะ (และทำ) เพียงแค่แจ้งเตือนผู้ใช้ว่าไฟล์ถูกล็อค พวกเขามักจะล็อคตัวเองดังนั้นพวกเขาอาจจะทำอะไรกับมัน หรือไม่.
CAD bloke

ในบางกรณีคุณต้องใช้นโยบายการลองใหม่เนื่องจากไฟล์อาจยังไม่พร้อม ลองนึกภาพแอพพลิเคชั่นบนเดสก์ท็อปเพื่อดาวน์โหลดภาพในบางโฟลเดอร์ชั่วคราว แอปพลิเคชันเริ่มดาวน์โหลดและในเวลาเดียวกันคุณเปิดโฟลเดอร์นี้ในตัวสำรวจไฟล์ Windows ต้องการสร้างรูปย่อทันทีและล็อคไฟล์ ในเวลาเดียวกันแอพของคุณจะพยายามแทนที่ภาพที่ถูกล็อคเป็นที่อื่น คุณจะได้รับข้อยกเว้นหากคุณไม่ได้ใช้นโยบายการลองใหม่
Ivan Branets

4

วิธีเดียวที่ฉันรู้คือใช้ Win32 exclusive lock API ซึ่งไม่รวดเร็วเกินไป แต่มีตัวอย่างอยู่

คนส่วนใหญ่สำหรับวิธีง่ายๆในการนี้เพียงลอง / จับ / นอนหลับวน


1
คุณไม่สามารถใช้ API นี้ได้หากไม่เปิดไฟล์ครั้งแรก ณ จุดนี้คุณไม่จำเป็นต้องใช้อีกต่อไป
Harry Johnston

1
ไฟล์ของ Schrodinger
TheLastGIS

4
static bool FileInUse(string path)
    {
        try
        {
            using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate))
            {
                fs.CanWrite
            }
            return false;
        }
        catch (IOException ex)
        {
            return true;
        }
    }

string filePath = "C:\\Documents And Settings\\yourfilename";
bool isFileInUse;

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");

หวังว่านี่จะช่วยได้!


10
การตรวจสอบที่แท้จริงที่คุณดำเนินการเป็นไปด้วยดี วางไว้ในฟังก์ชั่นที่ทำให้เข้าใจผิด คุณไม่ต้องการใช้ฟังก์ชั่นเช่นนี้ก่อนที่จะเปิดไฟล์ ภายในฟังก์ชั่นไฟล์จะถูกเปิดตรวจสอบและปิด จากนั้นโปรแกรมเมอร์จะสันนิษฐานว่าไฟล์นั้นยังคงใช้งานได้และพยายามเปิดให้ใช้ สิ่งนี้ไม่ดีเนื่องจากสามารถใช้และล็อคโดยกระบวนการอื่นที่เข้าคิวเพื่อเปิดไฟล์นี้ ระหว่างครั้งที่ 1 มันถูกเปิด (สำหรับการตรวจสอบ) และครั้งที่สองที่มันถูกเปิด (สำหรับการใช้งาน) ระบบปฏิบัติการอาจยกเลิกกำหนดการของคุณและสามารถเรียกใช้กระบวนการอื่นได้
Lakey

3

คำตอบที่ได้รับการยอมรับด้านบนประสบปัญหาเมื่อไฟล์ถูกเปิดเพื่อการเขียนด้วยโหมด FileShare.Read หรือหากไฟล์นั้นมีคุณสมบัติอ่านอย่างเดียวรหัสจะไม่ทำงาน โซลูชันที่ปรับเปลี่ยนนี้ทำงานได้อย่างน่าเชื่อถือที่สุดโดยคำนึงถึงสองสิ่งที่ต้องคำนึงถึง (เป็นจริงสำหรับโซลูชันที่ยอมรับเช่นกัน):

  1. มันจะไม่ทำงานสำหรับไฟล์ที่ถูกเปิดด้วยโหมดแชร์การเขียน
  2. สิ่งนี้ไม่คำนึงถึงปัญหาการทำเกลียวดังนั้นคุณจะต้องล็อคมันลงหรือจัดการกับปัญหาการทำเกลียวแยกกัน

เมื่อคำนึงถึงข้างต้นแล้วสิ่งนี้จะตรวจสอบว่าไฟล์ถูกล็อคเพื่อการเขียนหรือล็อคเพื่อป้องกันการอ่านหรือไม่ :

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;
}

ยังคงมีปัญหาเช่นเดียวกับคำตอบที่ยอมรับ - มันจะบอกคุณว่าไฟล์ถูกล็อคโดยกระบวนการอื่นในช่วงเวลาใดช่วงเวลาหนึ่งซึ่งไม่ใช่ข้อมูลที่มีประโยชน์ เมื่อถึงเวลาที่ฟังก์ชันส่งคืนผลลัพธ์อาจล้าสมัยไปแล้ว!
Harry Johnston

1
เป็นความจริงหนึ่งสามารถตรวจสอบได้ในช่วงเวลาที่กำหนด (หรือสมัครสมาชิกเหตุการณ์) ข้อดีของวิธีการนี้เหนือโซลูชันที่ยอมรับคือสามารถตรวจสอบแอตทริบิวต์อ่านอย่างเดียวและล็อคการเขียนและไม่ส่งคืนค่าบวกปลอม
rboy

3

นอกเหนือจากการทำงาน 3-liners และเพียงเพื่อการอ้างอิง: หากคุณต้องการข้อมูลที่สมบูรณ์แบบ - มีโครงการเล็ก ๆ ใน Microsoft Dev Center:

https://code.msdn.microsoft.com/windowsapps/How-to-know-the-process-704839f4

จากการแนะนำ:

โค้ดตัวอย่าง C # ที่พัฒนาใน. NET Framework 4.0 จะช่วยในการค้นหาว่าเป็นกระบวนการที่มีการล็อกไฟล์หรือไม่ ฟังก์ชันRmStartSessionซึ่งรวมอยู่ใน rstrtmgr.dll ถูกใช้เพื่อสร้างเซสชันตัวจัดการการรีสตาร์ทและตามผลลัพธ์ที่ส่งคืนอินสแตนซ์ใหม่ของวัตถุ Win32Exception จะถูกสร้างขึ้น หลังจากลงทะเบียนทรัพยากรไปยังเซสชันเริ่มต้นตัวจัดการใหม่ผ่าน ฟังก์ชันRmRegisterRescourcesฟังก์ชันRmGetListจะถูกเรียกใช้เพื่อตรวจสอบว่าแอปพลิเคชันใดกำลังใช้ไฟล์ใดไฟล์หนึ่งโดยการระบุอาเรย์ RM_PROCESS_INFO

มันทำงานได้โดยเชื่อมต่อกับ "รีสตาร์ทเซสชันผู้จัดการ"

ตัวจัดการการรีสตาร์ทใช้รายการทรัพยากรที่ลงทะเบียนกับเซสชันเพื่อพิจารณาว่าแอปพลิเคชันและบริการใดที่จะต้องปิดและรีสตาร์ท ทรัพยากรสามารถระบุได้โดยชื่อไฟล์ชื่อบริการแบบย่อหรือโครงสร้าง RM_UNIQUE_PROCESSที่อธิบายแอปพลิเคชันที่กำลังรัน

มันอาจจะเล็ก ๆ น้อย ๆoverengineeredสำหรับความต้องการเฉพาะของคุณ ... แต่ถ้านั่นคือสิ่งที่คุณต้องการไปข้างหน้าและคว้า VS-โครงการ


2

จากประสบการณ์ของฉันคุณมักจะต้องการทำสิ่งนี้แล้ว 'ป้องกัน' ไฟล์ของคุณเพื่อทำสิ่งที่แปลกใหม่แล้วใช้ไฟล์ 'ที่ป้องกันไว้' หากคุณมีไฟล์เดียวที่คุณต้องการใช้เช่นนี้คุณสามารถใช้เคล็ดลับที่อธิบายในคำตอบโดย Jeremy Thompson อย่างไรก็ตามหากคุณพยายามทำสิ่งนี้กับไฟล์จำนวนมาก (เช่นเมื่อคุณกำลังเขียนโปรแกรมติดตั้ง) คุณจะได้รับความเสียหายเล็กน้อย

วิธีที่ยอดเยี่ยมที่สามารถแก้ไขได้คือการใช้ความจริงที่ว่าระบบไฟล์ของคุณจะไม่อนุญาตให้คุณเปลี่ยนชื่อโฟลเดอร์หากไฟล์ใดไฟล์หนึ่งที่มีการใช้งานอยู่ เก็บโฟลเดอร์ไว้ในระบบไฟล์เดียวกันและมันจะทำงานเหมือนมีเสน่ห์

โปรดทราบว่าคุณควรทราบถึงวิธีการที่ชัดเจนว่าสามารถถูกโจมตีได้ ท้ายที่สุดไฟล์จะไม่ถูกล็อค นอกจากนี้โปรดทราบว่ามีสาเหตุอื่นที่ทำให้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


สวัสดีเนื่องจากลำดับของคำตอบมีการเปลี่ยนแปลงคุณสามารถชี้แจงว่าโพสต์ใดที่คุณหมายถึงสำหรับผู้อ่านของ QA ที่เป็นที่นิยม ขอบคุณ
Jeremy Thompson

1
@JeremyThompson ขอบคุณนะฉันจะแก้ไขโพสต์ ฉันจะใช้วิธีแก้ปัญหาจากคุณส่วนใหญ่เป็นเพราะการใช้งานที่ถูกต้องของคุณFileShareและการตรวจสอบล็อค
atlaste

2

นี่คือโค้ดบางส่วนที่ดีที่สุดที่ฉันสามารถบอกได้ว่าทำแบบเดียวกับคำตอบที่ยอมรับ แต่ใช้รหัสน้อย:

    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);
    }

2

คุณสามารถใช้ห้องสมุดของฉันเพื่อเข้าถึงไฟล์จากหลายแอพ

คุณสามารถติดตั้งได้จาก nuget: Install-Package Xabe.FileLock

หากคุณต้องการข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้โปรดตรวจสอบ 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 จะคืนค่าเป็นจริงก็ต่อเมื่อสามารถล็อกไฟล์แบบเอกสิทธิ์สำหรับวัตถุนี้ แต่แอพพลิเคชั่นที่อัพโหลดไฟล์จะต้องทำในล็อกไฟล์ด้วย หากวัตถุไม่สามารถเข้าถึง metod จะส่งคืนค่าเท็จ


1
โปรดอย่าเพิ่งโพสต์เครื่องมือหรือห้องสมุดเพื่อเป็นคำตอบ อย่างน้อยก็แสดงให้เห็นว่ามันแก้ปัญหาในคำตอบของตัวเอง
paper1111

เพิ่มการสาธิต :) ขออภัย @ paper1111
Tomasz Żmuda

1
ต้องใช้กระบวนการทั้งหมดที่ใช้ไฟล์เพื่อให้ความร่วมมือ ไม่น่าจะใช้กับปัญหาดั้งเดิมของ OPs ได้
Harry Johnston

2

ฉันต้องอัปโหลดไฟล์ 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
}

0

ฉันสนใจที่จะดูว่าสิ่งนี้ก่อให้เกิดปฏิกิริยาตอบสนอง 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บล็อกเพื่อให้แน่ใจว่าฉันได้รับ IOException ที่ถูกต้อง ฉันอาจจะล้างที่เก็บแอปเมื่อเริ่มต้นด้วยเนื่องจากไฟล์เหล่านี้มีวัตถุประสงค์เพื่อเป็นการชั่วคราวเท่านั้น

ฉันรู้ว่าสิ่งนี้เกินขอบเขตของคำถามของ OP เพียงแค่ตรวจสอบว่าไฟล์มีการใช้งานหรือไม่ แต่นี่เป็นปัญหาที่ฉันต้องการแก้ไขเมื่อฉันมาถึงที่นี่ดังนั้นอาจเป็นประโยชน์กับคนอื่น


0

ต้องการความช่วยเหลือนี้ไหม?

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)
    {

    }
}

-2

ลองและย้าย / คัดลอกไฟล์ไปยัง temp dir หากคุณสามารถทำได้จะไม่มีการล็อคและคุณสามารถทำงานใน temp dir ได้อย่างปลอดภัยโดยไม่ต้องล็อค อื่นลองย้ายอีกครั้งในอีก x วินาที


@ jcolebrand ล็อคอะไร สิ่งที่คุณคัดลอก? หรือคนที่คุณใส่ใน dir ชั่วคราว?
Cullub

5
หากคุณคัดลอกไฟล์โดยคาดว่าจะไม่มีใครทำงานได้และคุณจะใช้ไฟล์ชั่วคราวจากนั้นมีคนล็อกไฟล์ทันทีหลังจากที่คุณคัดลอกคุณอาจสูญเสียข้อมูล
jcolebrand

-3

ฉันใช้วิธีแก้ปัญหานี้ แต่ฉันมีช่วงเวลาระหว่างเมื่อฉันตรวจสอบการล็อคไฟล์ด้วยฟังก์ชั่น IsFileLocked และเมื่อฉันเปิดไฟล์ ในช่วงเวลานี้บางเธรดอื่นสามารถเปิดไฟล์ได้ดังนั้นฉันจะได้รับ IOException

ดังนั้นฉันจึงเพิ่มรหัสพิเศษสำหรับสิ่งนี้ ในกรณีของฉันฉันต้องการโหลด 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 เลย?

ขอบคุณ


4
IsFileBeingUsed คืออะไร รหัสที่มาเกี่ยวกับ IsFileBeingUsed?
Kiquenet
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.