รอจนกว่าไฟล์จะถูกปลดล็อคใน. NET


103

อะไรคือวิธีที่ง่ายที่สุดในการบล็อกเธรดจนกว่าไฟล์จะถูกปลดล็อกและสามารถเข้าถึงได้เพื่ออ่านและเปลี่ยนชื่อ ตัวอย่างเช่นมี WaitOnFile () อยู่ที่ไหนสักแห่งใน. NET Framework หรือไม่

ฉันมีบริการที่ใช้ FileSystemWatcher เพื่อค้นหาไฟล์ที่จะส่งไปยังไซต์ FTP แต่เหตุการณ์ไฟล์ที่สร้างขึ้นจะเริ่มทำงานก่อนที่กระบวนการอื่นจะเขียนไฟล์เสร็จสิ้น

ทางออกที่ดีที่สุดจะมีช่วงหมดเวลาเพื่อไม่ให้เธรดค้างตลอดไปก่อนที่จะยอมแพ้

แก้ไข: หลังจากลองใช้วิธีแก้ปัญหาด้านล่างแล้วฉันได้เปลี่ยนระบบเพื่อให้ไฟล์ทั้งหมดถูกเขียนถึงPath.GetTempFileName()จากนั้นดำเนินการFile.Move()ไปยังตำแหน่งสุดท้าย ทันทีที่FileSystemWatcherเหตุการณ์เริ่มขึ้นไฟล์ก็เสร็จสมบูรณ์แล้ว


4
ตั้งแต่ออก. NET 4.0 มีวิธีที่ดีกว่าในการแก้ปัญหานี้หรือไม่?
สัน

คำตอบ:


40

นี่คือคำตอบที่ฉันให้กับคำถามที่เกี่ยวข้อง :

    /// <summary>
    /// Blocks until the file is not locked any more.
    /// </summary>
    /// <param name="fullPath"></param>
    bool WaitForFile(string fullPath)
    {
        int numTries = 0;
        while (true)
        {
            ++numTries;
            try
            {
                // Attempt to open the file exclusively.
                using (FileStream fs = new FileStream(fullPath,
                    FileMode.Open, FileAccess.ReadWrite, 
                    FileShare.None, 100))
                {
                    fs.ReadByte();

                    // If we got this far the file is ready
                    break;
                }
            }
            catch (Exception ex)
            {
                Log.LogWarning(
                   "WaitForFile {0} failed to get an exclusive lock: {1}", 
                    fullPath, ex.ToString());

                if (numTries > 10)
                {
                    Log.LogWarning(
                        "WaitForFile {0} giving up after 10 tries", 
                        fullPath);
                    return false;
                }

                // Wait for the lock to be released
                System.Threading.Thread.Sleep(500);
            }
        }

        Log.LogTrace("WaitForFile {0} returning true after {1} tries",
            fullPath, numTries);
        return true;
    }

8
ฉันพบว่าสิ่งนี้น่าเกลียด แต่เป็นทางออกเดียวที่เป็นไปได้
knoopx

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

75
คิดไม่ดี! แม้ว่าแนวคิดจะถูกต้อง แต่ทางออกที่ดีกว่าคือส่งคืน FileStream แทนบูล หากไฟล์ถูกล็อกอีกครั้งก่อนที่ผู้ใช้จะมีโอกาสได้รับการล็อกไฟล์ - เขาจะได้รับข้อยกเว้นแม้ว่าฟังก์ชันจะส่งคืน "เท็จ" ก็ตาม
Nissim

2
วิธีการของ Fero อยู่ที่ไหน?
Vbp

1
ความคิดเห็นของ Nissim ก็เป็นสิ่งที่ฉันคิดเช่นกัน แต่ถ้าคุณจะใช้การค้นหานั้นอย่าลืมรีเซ็ตเป็น 0 หลังจากอ่านไบต์ fs.Seek (0, SeekOriginBegin);
58

73

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

FileStream WaitForFile (string fullPath, FileMode mode, FileAccess access, FileShare share)
{
    for (int numTries = 0; numTries < 10; numTries++) {
        FileStream fs = null;
        try {
            fs = new FileStream (fullPath, mode, access, share);
            return fs;
        }
        catch (IOException) {
            if (fs != null) {
                fs.Dispose ();
            }
            Thread.Sleep (50);
        }
    }

    return null;
}

16
ฉันมาจากอนาคตเพื่อบอกว่ารหัสนี้ยังคงใช้งานได้เหมือนมีเสน่ห์ ขอบคุณ.
OnoSendai

6
@PabloCosta เป๊ะ! มันไม่สามารถปิดมันได้เพราะถ้าเป็นเช่นนั้นเธรดอื่นอาจเข้ามาและเปิดมันขึ้นมา การใช้งานนี้ถูกต้องเพราะยังคงเปิดอยู่! ปล่อยให้ผู้โทรกังวลเกี่ยวกับเรื่องนี้มันปลอดภัยที่จะusingเป็นโมฆะเพียงตรวจสอบค่าว่างในusingบล็อก
doug65536

2
"FileStream fs = null;" ควรประกาศนอกการลอง แต่ภายในสำหรับ จากนั้นกำหนดและใช้ fs ในการลอง บล็อกจับควรทำ "if (fs! = null) fs.Dispose ();" (หรือแค่ fs? .Dispose () ใน C # 6) เพื่อให้แน่ใจว่า FileStream ที่ไม่ถูกส่งคืนได้รับการทำความสะอาดอย่างถูกต้อง
Bill Menees

1
การอ่านไบต์จำเป็นจริงหรือ? จากประสบการณ์ของฉันถ้าคุณเปิดไฟล์สำหรับการเข้าถึงแบบอ่านคุณมีมันไม่ต้องทดสอบมัน แม้ว่าด้วยการออกแบบที่นี่คุณไม่ได้บังคับให้เข้าถึงแบบเอกสิทธิ์เฉพาะบุคคลดังนั้นจึงเป็นไปได้ที่คุณอาจจะอ่านไบต์แรกได้ แต่ไม่มีตัวอื่น (การล็อกระดับไบต์) จากคำถามเดิมคุณน่าจะเปิดด้วยระดับการแชร์แบบอ่านอย่างเดียวดังนั้นกระบวนการอื่น ๆ จะล็อกหรือแก้ไขไฟล์ไม่ได้ ไม่ว่าในกรณีใดฉันรู้สึกว่า fs. ReadByte () เป็นของเสียทั้งหมดหรือไม่เพียงพอขึ้นอยู่กับการใช้งาน
eselk

8
ผู้ใช้กรณีใดที่ไม่สามารถfsเป็นโมฆะในcatchบล็อกได้? หากตัวFileStreamสร้างพ่นตัวแปรจะไม่ถูกกำหนดค่าและไม่มีสิ่งอื่นใดอยู่ในตัวสร้างtryที่สามารถโยนIOExceptionไฟล์. return new FileStream(...)สำหรับผมแล้วมันดูเหมือนว่ามันควรจะตกลงที่จะทำ
Matti Virkkunen

18

นี่คือรหัสทั่วไปสำหรับทำสิ่งนี้โดยไม่ขึ้นกับการทำงานของไฟล์เอง นี่คือตัวอย่างวิธีการใช้งาน:

WrapSharingViolations(() => File.Delete(myFile));

หรือ

WrapSharingViolations(() => File.Copy(mySourceFile, myDestFile));

คุณยังสามารถกำหนดจำนวนการลองใหม่และเวลารอระหว่างการลองใหม่

หมายเหตุ: ขออภัยข้อผิดพลาด Win32 พื้นฐาน (ERROR_SHARING_VIOLATION) ไม่ถูกเปิดเผยกับ. NET ดังนั้นฉันจึงได้เพิ่มฟังก์ชันแฮ็คขนาดเล็ก ( IsSharingViolation) โดยอาศัยกลไกการสะท้อนเพื่อตรวจสอบสิ่งนี้

    /// <summary>
    /// Wraps sharing violations that could occur on a file IO operation.
    /// </summary>
    /// <param name="action">The action to execute. May not be null.</param>
    public static void WrapSharingViolations(WrapSharingViolationsCallback action)
    {
        WrapSharingViolations(action, null, 10, 100);
    }

    /// <summary>
    /// Wraps sharing violations that could occur on a file IO operation.
    /// </summary>
    /// <param name="action">The action to execute. May not be null.</param>
    /// <param name="exceptionsCallback">The exceptions callback. May be null.</param>
    /// <param name="retryCount">The retry count.</param>
    /// <param name="waitTime">The wait time in milliseconds.</param>
    public static void WrapSharingViolations(WrapSharingViolationsCallback action, WrapSharingViolationsExceptionsCallback exceptionsCallback, int retryCount, int waitTime)
    {
        if (action == null)
            throw new ArgumentNullException("action");

        for (int i = 0; i < retryCount; i++)
        {
            try
            {
                action();
                return;
            }
            catch (IOException ioe)
            {
                if ((IsSharingViolation(ioe)) && (i < (retryCount - 1)))
                {
                    bool wait = true;
                    if (exceptionsCallback != null)
                    {
                        wait = exceptionsCallback(ioe, i, retryCount, waitTime);
                    }
                    if (wait)
                    {
                        System.Threading.Thread.Sleep(waitTime);
                    }
                }
                else
                {
                    throw;
                }
            }
        }
    }

    /// <summary>
    /// Defines a sharing violation wrapper delegate.
    /// </summary>
    public delegate void WrapSharingViolationsCallback();

    /// <summary>
    /// Defines a sharing violation wrapper delegate for handling exception.
    /// </summary>
    public delegate bool WrapSharingViolationsExceptionsCallback(IOException ioe, int retry, int retryCount, int waitTime);

    /// <summary>
    /// Determines whether the specified exception is a sharing violation exception.
    /// </summary>
    /// <param name="exception">The exception. May not be null.</param>
    /// <returns>
    ///     <c>true</c> if the specified exception is a sharing violation exception; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsSharingViolation(IOException exception)
    {
        if (exception == null)
            throw new ArgumentNullException("exception");

        int hr = GetHResult(exception, 0);
        return (hr == -2147024864); // 0x80070020 ERROR_SHARING_VIOLATION

    }

    /// <summary>
    /// Gets the HRESULT of the specified exception.
    /// </summary>
    /// <param name="exception">The exception to test. May not be null.</param>
    /// <param name="defaultValue">The default value in case of an error.</param>
    /// <returns>The HRESULT value.</returns>
    public static int GetHResult(IOException exception, int defaultValue)
    {
        if (exception == null)
            throw new ArgumentNullException("exception");

        try
        {
            const string name = "HResult";
            PropertyInfo pi = exception.GetType().GetProperty(name, BindingFlags.NonPublic | BindingFlags.Instance); // CLR2
            if (pi == null)
            {
                pi = exception.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance); // CLR4
            }
            if (pi != null)
                return (int)pi.GetValue(exception, null);
        }
        catch
        {
        }
        return defaultValue;
    }

5
พวกเขาสามารถให้ไฟล์SharingViolationException. IOExceptionในความเป็นจริงพวกเขายังคงสามารถย้อนกลับเข้ากันได้ตราบใดที่มันลงมาจาก และพวกเขาก็ควรจริงๆ
Roman Starkov


9
ใน. NET Framework 4.5, .NET Standard และ. NET Core นั้น HResult เป็นคุณสมบัติสาธารณะบนคลาส Exception การสะท้อนกลับไม่จำเป็นสำหรับสิ่งนี้อีกต่อไป จาก MSDN:Starting with the .NET Framework 4.5, the HResult property's setter is protected, whereas its getter is public. In previous versions of the .NET Framework, both getter and setter are protected.
NightOwl888

13

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

using System;
using System.IO;
using System.Threading;

/// <summary>
/// This is a wrapper aroung a FileStream.  While it is not a Stream itself, it can be cast to
/// one (keep in mind that this might throw an exception).
/// </summary>
public class SafeFileStream: IDisposable
{
    #region Private Members
    private Mutex m_mutex;
    private Stream m_stream;
    private string m_path;
    private FileMode m_fileMode;
    private FileAccess m_fileAccess;
    private FileShare m_fileShare;
    #endregion//Private Members

    #region Constructors
    public SafeFileStream(string path, FileMode mode, FileAccess access, FileShare share)
    {
        m_mutex = new Mutex(false, String.Format("Global\\{0}", path.Replace('\\', '/')));
        m_path = path;
        m_fileMode = mode;
        m_fileAccess = access;
        m_fileShare = share;
    }
    #endregion//Constructors

    #region Properties
    public Stream UnderlyingStream
    {
        get
        {
            if (!IsOpen)
                throw new InvalidOperationException("The underlying stream does not exist - try opening this stream.");
            return m_stream;
        }
    }

    public bool IsOpen
    {
        get { return m_stream != null; }
    }
    #endregion//Properties

    #region Functions
    /// <summary>
    /// Opens the stream when it is not locked.  If the file is locked, then
    /// </summary>
    public void Open()
    {
        if (m_stream != null)
            throw new InvalidOperationException(SafeFileResources.FileOpenExceptionMessage);
        m_mutex.WaitOne();
        m_stream = File.Open(m_path, m_fileMode, m_fileAccess, m_fileShare);
    }

    public bool TryOpen(TimeSpan span)
    {
        if (m_stream != null)
            throw new InvalidOperationException(SafeFileResources.FileOpenExceptionMessage);
        if (m_mutex.WaitOne(span))
        {
            m_stream = File.Open(m_path, m_fileMode, m_fileAccess, m_fileShare);
            return true;
        }
        else
            return false;
    }

    public void Close()
    {
        if (m_stream != null)
        {
            m_stream.Close();
            m_stream = null;
            m_mutex.ReleaseMutex();
        }
    }

    public void Dispose()
    {
        Close();
        GC.SuppressFinalize(this);
    }

    public static explicit operator Stream(SafeFileStream sfs)
    {
        return sfs.UnderlyingStream;
    }
    #endregion//Functions
}

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

ฉันได้ทำการทดสอบอย่างรวดเร็วด้วย ~ 20 สิ่งในการอ่าน / เขียนไฟล์ต่างๆและไม่เห็นความเสียหาย เห็นได้ชัดว่ามันไม่ได้ก้าวหน้ามากนัก แต่มันควรจะใช้ได้กับเคสง่ายๆส่วนใหญ่


5

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

  • Ftp สองไฟล์ แต่ดูได้เพียงไฟล์เดียว ตัวอย่างเช่นส่งไฟล์ important.txt และ important.finish ดูเฉพาะไฟล์เสร็จสิ้น แต่ประมวลผล txt
  • FTP ไฟล์เดียว แต่เปลี่ยนชื่อเมื่อเสร็จสิ้น ตัวอย่างเช่น send important.wait และให้ผู้ส่งเปลี่ยนชื่อเป็น important.txt เมื่อเสร็จสิ้น

โชคดี!


นั่นคือสิ่งที่ตรงกันข้ามกับอัตโนมัติ นั่นเหมือนกับการรับไฟล์ด้วยตนเองโดยมีขั้นตอนมากขึ้น
HackSlash

4

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


3

จากMSDN :

เหตุการณ์ OnCreated จะถูกยกขึ้นทันทีที่สร้างไฟล์ หากไฟล์กำลังถูกคัดลอกหรือโอนไปยังไดเร็กทอรีที่เฝ้าดูเหตุการณ์ OnCreated จะถูกยกขึ้นทันทีตามด้วยเหตุการณ์ OnChanged อย่างน้อยหนึ่งเหตุการณ์

FileSystemWatcher ของคุณสามารถแก้ไขได้เพื่อที่จะไม่ทำการอ่าน / เปลี่ยนชื่อในระหว่างเหตุการณ์ "OnCreated" แต่ให้ทำดังนี้

  1. ขยายเธรดที่สำรวจสถานะไฟล์จนกว่าจะไม่ถูกล็อก (โดยใช้อ็อบเจ็กต์ FileInfo)
  2. โทรกลับเข้าสู่บริการเพื่อดำเนินการกับไฟล์ทันทีที่ระบุว่าไฟล์ไม่ถูกล็อกอีกต่อไปและพร้อมที่จะใช้งาน

1
การวางไข่เธรดของตัวตรวจสอบระบบไฟล์สามารถทำให้บัฟเฟอร์ที่อยู่ภายใต้การโอเวอร์โฟลว์ทำให้ไฟล์ที่เปลี่ยนแปลงหายไปจำนวนมากหายไป แนวทางที่ดีกว่าคือการสร้างคิวผู้บริโภค / ผู้ผลิต
Nissim

2

ในกรณีส่วนใหญ่วิธีง่ายๆเช่น @harpo แนะนำจะใช้ได้ผล คุณสามารถพัฒนาโค้ดที่ซับซ้อนขึ้นได้โดยใช้แนวทางนี้:

  • ค้นหาที่จับที่เปิดทั้งหมดสำหรับไฟล์ที่เลือกโดยใช้ SystemHandleInformation \ SystemProcessInformation
  • คลาสย่อย WaitHandle เพื่อเข้าถึงหมายเลขอ้างอิงภายใน
  • ส่งแฮนเดิลที่พบซึ่งห่อด้วยเมธอด WaitHandle ที่เป็นคลาสย่อยไปยัง WaitHandle.WaitAny

2

โฆษณาเพื่อโอนไฟล์ทริกเกอร์กระบวนการ SameNameASTrasferedFile.trg ที่สร้างขึ้นหลังจากการส่งไฟล์เสร็จสิ้น

จากนั้นตั้งค่า FileSystemWatcher ที่จะเริ่มเหตุการณ์เฉพาะในไฟล์ * .trg


1

ฉันไม่รู้ว่าคุณใช้อะไรในการกำหนดสถานะการล็อกของไฟล์ แต่สิ่งนี้ควรทำ

ในขณะที่ (จริง)
{
    ลอง {
        กระแส = File.Open (fileName, fileMode);
        หยุดพัก;
    }
    จับ (FileIOException) {

        // ตรวจสอบว่าเป็นปัญหาการล็อกหรือไม่

        Thread.Sleep (100);
    }
}

1
สายไปหน่อย แต่เมื่อไฟล์ถูกล็อคคุณจะไม่มีวันออกจากลูปของคุณ คุณควรเพิ่มตัวนับ (ดูคำตอบที่ 1)
ปีเตอร์

0

วิธีแก้ปัญหาที่เป็นไปได้คือการรวมระบบไฟล์เข้ากับการสำรวจความคิดเห็น

รับการแจ้งเตือนสำหรับทุกการเปลี่ยนแปลงในไฟล์และเมื่อได้รับการแจ้งเตือนให้ตรวจสอบว่าถูกล็อกตามที่ระบุไว้ในคำตอบที่ยอมรับในปัจจุบัน: https://stackoverflow.com/a/50800/6754146 รหัสสำหรับเปิดไฟล์สตรีมจะถูกคัดลอกจากคำตอบ และแก้ไขเล็กน้อย:

public static void CheckFileLock(string directory, string filename, Func<Task> callBack)
{
    var watcher = new FileSystemWatcher(directory, filename);
    FileSystemEventHandler check = 
        async (sender, eArgs) =>
    {
        string fullPath = Path.Combine(directory, filename);
        try
        {
            // Attempt to open the file exclusively.
            using (FileStream fs = new FileStream(fullPath,
                    FileMode.Open, FileAccess.ReadWrite,
                    FileShare.None, 100))
            {
                fs.ReadByte();
                watcher.EnableRaisingEvents = false;
                // If we got this far the file is ready
            }
            watcher.Dispose();
            await callBack();
        }
        catch (IOException) { }
    };
    watcher.NotifyFilter = NotifyFilters.LastWrite;
    watcher.IncludeSubdirectories = false;
    watcher.EnableRaisingEvents = true;
    //Attach the checking to the changed method, 
    //on every change it gets checked once
    watcher.Changed += check;
    //Initially do a check for the case it is already released
    check(null, null);
}

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


-1

ฉันทำแบบเดียวกับ Gulzar แค่พยายามวนไปเรื่อย ๆ

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


2
อาจถูก แต่นาทีละครั้งนานเกินไปสำหรับการใช้งานจำนวนมาก การตรวจสอบตามเวลาจริงเป็นสิ่งสำคัญในบางครั้ง แทนที่จะต้องใช้สิ่งที่จะรับฟังข้อความระบบไฟล์ใน C # (ไม่ใช่ภาษาที่สะดวกที่สุดสำหรับสิ่งเหล่านี้) คุณใช้ FSW
ThunderGr

-1

เพียงใช้เหตุการณ์ที่เปลี่ยนแปลงกับ NotifyFilter NotifyFilters.LastWrite :

var watcher = new FileSystemWatcher {
      Path = @"c:\temp\test",
      Filter = "*.xml",
      NotifyFilter = NotifyFilters.LastWrite
};
watcher.Changed += watcher_Changed; 
watcher.EnableRaisingEvents = true;

1
FileSystemWatcher ไม่เพียง แต่แจ้งเตือนเมื่อเขียนไฟล์เสร็จแล้ว มันมักจะแจ้งให้คุณทราบหลายครั้งสำหรับการเขียนเชิงตรรกะ "เดี่ยว" และหากคุณพยายามเปิดไฟล์หลังจากได้รับการแจ้งเตือนครั้งแรกคุณจะได้รับข้อยกเว้น
รอส

-1

ฉันพบปัญหาที่คล้ายกันเมื่อเพิ่มไฟล์แนบ Outlook "ใช้" บันทึกวัน

string fileName = MessagingBLL.BuildPropertyAttachmentFileName(currProp);

                //create a temporary file to send as the attachment
                string pathString = Path.Combine(Path.GetTempPath(), fileName);

                //dirty trick to make sure locks are released on the file.
                using (System.IO.File.Create(pathString)) { }

                mailItem.Subject = MessagingBLL.PropertyAttachmentSubject;
                mailItem.Attachments.Add(pathString, Outlook.OlAttachmentType.olByValue, Type.Missing, Type.Missing);

-3

วิธีนี้เป็นตัวเลือก:

private void WaitOnFile(string fileName)
{
    FileInfo fileInfo = new FileInfo(fileName);
    for (long size = -1; size != fileInfo.Length; fileInfo.Refresh())
    {
        size = fileInfo.Length;
        System.Threading.Thread.Sleep(1000);
    }
}

แน่นอนว่าหากมีการจัดสรรขนาดไฟล์ไว้ล่วงหน้าในการสร้างคุณจะได้รับผลบวกปลอม


1
หากกระบวนการเขียนไปยังไฟล์หยุดชั่วคราวนานกว่าหนึ่งวินาทีหรือบัฟเฟอร์ในหน่วยความจำนานกว่าหนึ่งวินาทีคุณจะได้รับผลบวกเท็จอีกครั้ง ฉันไม่คิดว่านี่เป็นทางออกที่ดีภายใต้สถานการณ์ใด ๆ
Chris Wenham
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.