เหตุการณ์ FileSystemWatcher Changed ถูกยกสองครั้ง


336

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

public void Initialize()
{
   FileSystemWatcher _fileWatcher = new FileSystemWatcher();
  _fileWatcher.Path = "C:\\Folder";
  _fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
  _fileWatcher.Filter = "Version.txt";
  _fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
  _fileWatcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
   .......
}

ในกรณีของฉันOnChangedถูกเรียกสองครั้งเมื่อฉันเปลี่ยนไฟล์ข้อความversion.txtและบันทึก


2
@BrettRigby: ไม่น่าแปลกใจ คำตอบที่อาจเป็นไปได้เหล่านี้ไม่สามารถแก้ปัญหาได้ มันเป็นวิธีแก้ปัญหาทั้งหมดสำหรับปัญหาเฉพาะ ในความเป็นจริงไม่มีพวกเขาแก้ปัญหาเฉพาะของฉัน (ฉันต้องยอมรับว่าฉันไม่ได้ทดสอบพวกเขาทั้งหมด)

มันเป็นวิธีแก้ปัญหา แต่ควรตัดสินโดยคุณภาพของวิธีแก้ปัญหา การติดตามการเปลี่ยนแปลงนั้นทำงานได้อย่างสมบูรณ์และเรียบง่าย OP ขอวิธีการระงับเหตุการณ์ที่ซ้ำกันและนั่นคือสิ่งที่การตอบสนองด้านล่างให้ msdn.microsoft.com/en-us/library/… อธิบายว่าเหตุการณ์หลายเหตุการณ์อาจเกิดจากการป้องกันไวรัสหรือ "สิ่งที่ซับซ้อนของระบบไฟล์" (ซึ่งฟังดูเหมือนข้ออ้าง)
Tyler Montney

2
ฉันเพิ่งเปิดตัวปัญหานี้github.com/Microsoft/dotnet/issues/347
Stephan Ahlf

2
ฉันได้สร้างคลาสที่ช่วยให้คุณได้รับเหตุการณ์เดียวเท่านั้น คุณสามารถรับรหัสได้จากgithub.com/melenaos/FileSystemSafeWatcher
Menelaos Vergis

คำตอบ:


277

ฉันกลัวว่านี่เป็นข้อผิดพลาดที่รู้จักกันดีของFileSystemWatcherชั้นเรียน นี่คือจากเอกสารของคลาส:

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

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

เมื่อไม่นานมานี้ฉันได้ทำเครื่องหมายหน้าเพจด้วยเคล็ดลับ FileSystemWatcherสองสามข้อ คุณอาจต้องการตรวจสอบ



151

ฉันได้ "แก้ไขปัญหา" โดยใช้กลยุทธ์ต่อไปนี้ในตัวแทนของฉัน

// fsw_ is the FileSystemWatcher instance used by my application.

private void OnDirectoryChanged(...)
{
   try
   {
      fsw_.EnableRaisingEvents = false;

      /* do my stuff once asynchronously */
   }

   finally
   {
      fsw_.EnableRaisingEvents = true;
   }
}

14
ฉันลองมันและมันใช้งานได้หากฉันแก้ไขไฟล์ทีละไฟล์ แต่ถ้าฉันแก้ไขสองไฟล์ในเวลาเดียวกัน (เช่นคัดลอก 1.txt และ 2.txt เพื่อคัดลอก 1.txt และสำเนาของ 2.txt) มันจะเพิ่มเฉพาะ เหตุการณ์หนึ่งไม่สองอย่างที่คาดไว้
Christopher Painter

2
เป็นเวลาสองเดือนแล้ว แต่ฉันคิดว่าสิ่งที่ฉันทำคือการให้ event call เป็นวิธีการที่ทำให้ตรรกะทางธุรกิจภายในข้อความสั่งการล็อก ด้วยวิธีนี้ถ้าฉันได้รับกิจกรรมเพิ่มเติมพวกเขาเข้าคิวจนกว่าจะถึงตาของพวกเขาและไม่มีอะไรให้พวกเขาทำซ้ำตั้งแต่ก่อนหน้านี้ดูแลทุกอย่าง
Christopher Painter

15
นี่ดูเหมือนจะแก้ไขปัญหา แต่ไม่ได้ หากกระบวนการอื่นกำลังทำการเปลี่ยนแปลงที่คุณอาจสูญเสียไปสาเหตุที่ดูเหมือนว่าจะใช้งานได้ก็เพราะ IO ของกระบวนการอื่นเป็นแบบอะซิงโครนัสและคุณปิดใช้งานการตรวจสอบจนกว่าคุณจะทำการประมวลผลเสร็จสิ้น ที่น่าสนใจ นั่นคือเหตุผลที่ @ChristopherPainter สังเกตปัญหาของเขา
Jf Beaulac

14
-1: จะเกิดอะไรขึ้นถ้ามีการเปลี่ยนแปลงอีกอย่างที่คุณสนใจเกิดขึ้นในขณะที่ปิดการใช้งาน
G. Stoynev

2
@cYounes: เว้นแต่ว่าคุณจะทำสิ่งต่าง ๆแบบอะซิงโครนัส
David Brabant

107

OnChangedเหตุการณ์ที่ซ้ำกันจากFileSystemWatcherสามารถตรวจจับและทิ้งได้โดยการตรวจสอบการFile.GetLastWriteTimeประทับเวลาของไฟล์ที่เป็นปัญหา ชอบมาก

DateTime lastRead = DateTime.MinValue;

void OnChanged(object source, FileSystemEventArgs a)
{
    DateTime lastWriteTime = File.GetLastWriteTime(uri);
    if (lastWriteTime != lastRead)
    {
        doStuff();
        lastRead = lastWriteTime;
    }
    // else discard the (duplicated) OnChanged event
}

13
ฉันชอบโซลูชันนั้น แต่ฉันใช้ Rx เพื่อทำสิ่งที่ "ถูกต้อง" (เปลี่ยน"Rename"ชื่อของกิจกรรมที่คุณสนใจ):Observable.FromEventPattern<FileSystemEventArgs>(fileSystemWatcher, "Renamed") .Select(e => e.EventArgs) .Distinct(e => e.FullPath) .Subscribe(onNext);
Kjellski

4
ฉันพลาดอะไรไปรึเปล่า? ฉันไม่เข้าใจว่ามันจะทำงานอย่างไร จากสิ่งที่ฉันได้เห็นเหตุการณ์เกิดขึ้นพร้อมกันดังนั้นหากพวกเขาทั้งคู่เข้าสู่เหตุการณ์ข้างต้นในเวลาเดียวกันพวกเขาทั้งคู่จะเริ่มทำงานก่อนที่จะอ่านการตั้งค่าล่าสุด
Peter Jamsmenson

ในฐานะที่เป็นDateTimeเพียง แต่มีความละเอียดมิลลิวินาทีวิธีนี้ทำงานแม้ว่าคุณจะแทนที่ด้วยFile.GetLastWriteTime DateTime.Nowขึ้นอยู่กับสถานการณ์ของคุณคุณอาจใช้a.FullNameตัวแปรส่วนกลางเพื่อตรวจสอบเหตุการณ์ที่ซ้ำกัน
Roland

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

10
ไม่สามารถใช้งานได้เนื่องจากเหตุการณ์ที่ถูกไล่ออกจะถูกแยกออกจากกัน: เวลาเขียนล่าสุด: 636076274162565607 เวลาเขียนล่าสุด: 636076274162655722
Asheh

23

นี่คือทางออกของฉันซึ่งช่วยให้ฉันหยุดเหตุการณ์ที่ถูกยกขึ้นสองครั้ง:

watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;

ที่นี่ฉันได้ตั้งค่าNotifyFilterคุณสมบัติด้วยชื่อไฟล์และขนาดเท่านั้น
watcherเป็นวัตถุของฉันของ FileSystemWatcher หวังว่าจะช่วยได้


9
นอกจากนี้ใน Notepad ฉันสร้างไฟล์ที่มีตัวอักษรสี่ตัวคือ abcd จากนั้นฉันก็เปิดตัวอย่างใหม่ของ Notepad และป้อนอักขระสี่ตัวที่เหมือนกัน ฉันเลือกไฟล์ | บันทึกเป็นและเลือกไฟล์เดียวกัน ไฟล์มีขนาดเท่ากันและขนาดและชื่อไฟล์จะไม่เปลี่ยนแปลงเนื่องจากไฟล์มีตัวอักษรสี่ตัวเหมือนกันดังนั้นจึงไม่ทำงาน
Rhyous

30
อาจเป็นไปได้ว่าการเปลี่ยนแปลงของแท้นั้นไม่สามารถเปลี่ยนแปลงขนาดของไฟล์ได้ดังนั้นเทคนิคนี้จะล้มเหลวในสถานการณ์นั้น
ลีกริสซัม

3
ฉันเดาว่าเป็นกรณีทั่วไปที่คุณรู้ว่าการเปลี่ยนแปลงใด ๆ ที่มีความหมายจะปรับเปลี่ยนขนาดไฟล์ (ตัวอย่างเช่นกรณีของฉันถูกผนวกเข้ากับไฟล์บันทึก) ในขณะที่ทุกคนที่ใช้โซลูชันนี้ควรระวัง (และเอกสาร) ที่สันนิษฐานว่านี่เป็นสิ่งที่ฉันต้องการ
GrandOpener

1
@GrandOpener: นี่ไม่จริงเสมอไป ในกรณีของฉันฉันกำลังดูไฟล์ที่เนื้อหาประกอบด้วยเพียงหนึ่งตัวอักษรซึ่งเป็น 0 หรือ 1

8

สถานการณ์ของฉันคือฉันมีเครื่องเสมือนที่มีเซิร์ฟเวอร์ Linux อยู่ ฉันกำลังพัฒนาไฟล์บนโฮสต์ Windows เมื่อฉันเปลี่ยนบางสิ่งในโฟลเดอร์บนโฮสต์ฉันต้องการอัปโหลดการเปลี่ยนแปลงทั้งหมดซิงค์กับเซิร์ฟเวอร์เสมือนผ่าน Ftp นี่คือวิธีที่ฉันจะกำจัดเหตุการณ์การเปลี่ยนแปลงที่ซ้ำกันเมื่อฉันเขียนไปยังไฟล์ (ซึ่งแฟล็กโฟลเดอร์ที่มีไฟล์ที่จะแก้ไขด้วย):

private Hashtable fileWriteTime = new Hashtable();

private void fsw_sync_Changed(object source, FileSystemEventArgs e)
{
    string path = e.FullPath.ToString();
    string currentLastWriteTime = File.GetLastWriteTime( e.FullPath ).ToString();

    // if there is no path info stored yet
    // or stored path has different time of write then the one now is inspected
    if ( !fileWriteTime.ContainsKey(path) ||
         fileWriteTime[path].ToString() != currentLastWriteTime
    )
    {
        //then we do the main thing
        log( "A CHANGE has occured with " + path );

        //lastly we update the last write time in the hashtable
        fileWriteTime[path] = currentLastWriteTime;
    }
}

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


ฉันถือว่าคุณว่างเปล่า hashtable เป็นระยะ
ThunderGr

นี่จะแม่นยำกับวินาที แต่ถ้าช่วงเวลาระหว่างการเปลี่ยนแปลงทั้งสองนั้นนานพอที่จะผ่านวินาทีมันจะล้มเหลว ยิ่งไปกว่านั้นถ้าคุณต้องการความแม่นยำมากขึ้นคุณสามารถใช้ToString("o")แต่เตรียมพร้อมสำหรับความล้มเหลวเพิ่มเติม
Pragmateek

5
อย่าเปรียบเทียบสตริงใช้ DateTime.Equals ()
Phillip Kamikaze

ไม่ทำ พวกเขาไม่เท่ากัน ในกรณีของโครงการปัจจุบันของฉันพวกเขาแยกกันเป็นมิลลิวินาที ฉันใช้ (newtime-oldtime). TotalMilliseconds <(ขีด จำกัด โดยพลการปกติคือ 5ms)
Flynn1179

8

ลองด้วยรหัสนี้:

class WatchPlotDirectory
{
    bool let = false;
    FileSystemWatcher watcher;
    string path = "C:/Users/jamie/OneDrive/Pictures/Screenshots";

    public WatchPlotDirectory()
    {
        watcher = new FileSystemWatcher();
        watcher.Path = path;
        watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                               | NotifyFilters.FileName | NotifyFilters.DirectoryName;
        watcher.Filter = "*.*";
        watcher.Changed += new FileSystemEventHandler(OnChanged);
        watcher.Renamed += new RenamedEventHandler(OnRenamed);
        watcher.EnableRaisingEvents = true;
    }



    void OnChanged(object sender, FileSystemEventArgs e)
    {
        if (let==false) {
            string mgs = string.Format("File {0} | {1}",
                                       e.FullPath, e.ChangeType);
            Console.WriteLine("onchange: " + mgs);
            let = true;
        }

        else
        {
            let = false;
        }


    }

    void OnRenamed(object sender, RenamedEventArgs e)
    {
        string log = string.Format("{0} | Renamed from {1}",
                                   e.FullPath, e.OldName);
        Console.WriteLine("onrenamed: " + log);

    }

    public void setPath(string path)
    {
        this.path = path;
    }
}

1
นี่คือทางออกที่ดีที่สุดโดยใช้สัญญาณแทนการจับเวลา
Aaron Blenkush

1
สัญญาณอะไร? ฉันเห็นเพียงตัวแปรบูลีนที่นี่ นอกจากนี้ปัญหาหลักยังไม่ได้รับการแก้ไข: FileSystemEventHandler ยังคงยิงหลายเหตุการณ์ รหัสนี้มีประสิทธิภาพอย่างไร if (let==false) { ... } else { let = false; }? ไม่น่าเชื่อว่าการ upvotes นี้จะต้องเป็นเรื่องของตราสัญลักษณ์ StackOverflow เท่านั้น
sɐunıɔןɐqɐp

8

นี่คือแนวทางของฉัน:

// Consider having a List<String> named _changedFiles

private void OnChanged(object source, FileSystemEventArgs e)
{
    lock (_changedFiles)
    {
        if (_changedFiles.Contains(e.FullPath))
        {
            return;
        }
        _changedFiles.Add(e.FullPath);
    }

    // do your stuff

    System.Timers.Timer timer = new Timer(1000) { AutoReset = false };
    timer.Elapsed += (timerElapsedSender, timerElapsedArgs) =>
    {
        lock (_changedFiles)
        {
            _changedFiles.Remove(e.FullPath);
        }
    };
   timer.Start();
}

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

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


โซลูชันของคุณใช้งานได้ดีสำหรับฉัน คุณลืมเพิ่มไฟล์ในรายการ _changedFiles ส่วนแรกของรหัสควรมีลักษณะเช่นนี้:lock (_changedFiles) { if (_changedFiles.Contains(e.FullPath)) { return; } _changedFiles.Add(e.FullPath); // add this! } // do your stuff
davidthegrey

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

โซลูชันของคุณไม่ปลอดภัยสำหรับเธรด _changedFilesจะเข้าถึงได้จากหลายหัวข้อ วิธีการหนึ่งที่จะแก้ไขได้คือการใช้แทนConcurrentDictionary Listอีกวิธีหนึ่งคือการกำหนดปัจจุบันFormให้กับTimer.SynchronizingObjectทรัพย์สินเช่นเดียวกับFileSystemWatcher.SynchronizingObjectทรัพย์สิน
Theodor Zoulias

5

ฉันได้สร้าง repo Git โดยมีคลาสที่ขยายFileSystemWatcherเพื่อกระตุ้นเหตุการณ์เมื่อคัดลอกเสร็จแล้ว มันจะยกเลิกกิจกรรมที่เปลี่ยนแปลงทั้งหมดที่ผ่านมาและจะเพิ่มเฉพาะเมื่อไฟล์พร้อมให้อ่าน

ดาวน์โหลดFileSystemSafeWatcherและเพิ่มลงในโครงการของคุณ

จากนั้นใช้เป็นปกติFileSystemWatcherและตรวจสอบเมื่อมีการเรียกใช้เหตุการณ์

var fsw = new FileSystemSafeWatcher(file);
fsw.EnableRaisingEvents = true;
// Add event handlers here
fsw.Created += fsw_Created;

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

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

_consolidationInterval ดูเหมือนว่าจะทำงานได้ดีสำหรับฉัน ฉันต้องการใครสักคนที่จะแยกสิ่งนี้และทำให้มันเป็นแพ็คเกจ NuGet
zumalifeguard

1
ขอบคุณ :) มันแก้ไขปัญหาของฉัน .. หวังว่ากิจกรรมที่สร้างและคัดลอกจะทำงานได้อย่างถูกต้องกับผู้เฝ้าดูคนเดียวเพื่อแก้ปัญหานี้ได้ดี stackoverflow.com/questions/55015132/…
เทคโน

1
มันยอดเยี่ยมมาก ฉันได้นำไปใช้กับโครงการของฉันและมันก็ถูกตีทุกครั้งที่ฉันพยายามทำลายมัน ขอบคุณ.
Christh

4

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

    Dictionary<string, DateTime> dateTimeDictionary = new Dictionary<string, DateTime>(); 

        private void OnChanged(object source, FileSystemEventArgs e)
            {
                if (!dateTimeDictionary.ContainsKey(e.FullPath) || (dateTimeDictionary.ContainsKey(e.FullPath) && System.IO.File.GetLastWriteTime(e.FullPath) != dateTimeDictionary[e.FullPath]))
                {
                    dateTimeDictionary[e.FullPath] = System.IO.File.GetLastWriteTime(e.FullPath);

                    //your code here
                }
            }

นี่เป็นโซลูชันที่มั่นคง แต่ไม่มีรหัสเลย ในyour code hereส่วนคุณควรเพิ่มหรืออัพเดท dateTimeDictionary dateTimeDictionary[e.FullPath] = System.IO.File.GetLastWriteTime(e.FullPath);
DiamondDrake

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

3

หนึ่งที่เป็นไปได้ 'แฮ็ค' อาจเป็นการเร่งเหตุการณ์โดยใช้ส่วนขยายที่ทำปฏิกิริยาเช่น:

var watcher = new FileSystemWatcher("./");

Observable.FromEventPattern<FileSystemEventArgs>(watcher, "Changed")
            .Throttle(new TimeSpan(500000))
            .Subscribe(HandleChangeEvent);

watcher.EnableRaisingEvents = true;

ในกรณีนี้ฉันใช้ปริมาณน้อยถึง 50ms ในระบบของฉันที่เพียงพอ แต่ค่าที่สูงกว่าควรปลอดภัยกว่า (และอย่างที่ฉันบอกไปมันยังคงเป็น 'แฮ็ค')


ฉันใช้วิธีการ.Distinct(e => e.FullPath)ที่ฉันจัดการได้ง่ายขึ้น และคุณได้รับพฤติกรรมที่ถูกเรียกคืนจาก API
Kjellski

3

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

private int fireCount = 0;
private void inputFileWatcher_Changed(object sender, FileSystemEventArgs e)
    {
       fireCount++;
       if (fireCount == 1)
        {
            MessageBox.Show("Fired only once!!");
            dowork();
        }
        else
        {
            fireCount = 0;
        }
    }
}

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

ลองแยกแยะเหตุการณ์ประเภทต่างๆและจัดการกับมันแยกกันฉันแค่เสนอวิธีแก้ปัญหาที่เป็นไปได้ โชคดี.
Xiaoyuvax

แม้ว่าจะไม่ได้ทดสอบ แต่ฉันไม่แน่ใจว่าสิ่งนี้จะไม่ได้ผลสำหรับการสร้างและการลบ มันควรจะใช้ในทางทฤษฎีเช่นกันเนื่องจากคำสั่ง fireCount ++ และ if () เป็นทั้งอะตอมและจะไม่ถูกรอ แม้จะมีเหตุการณ์ที่ถูกกระตุ้นสองเหตุการณ์ที่แข่งขันกันเอง ฉันเดาว่าต้องมีอย่างอื่นที่ทำให้เกิดปัญหาของคุณ (? โดยสูญเสียคุณหมายถึงอะไร?)
Xiaoyuvax

3

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

public void fileSystemWatcher1_Changed( object sender, System.IO.FileSystemEventArgs e )
    {            
        fileSystemWatcher1.Changed -= new System.IO.FileSystemEventHandler( fileSystemWatcher1_Changed );
        MessageBox.Show( "File has been uploaded to destination", "Success!" );
        fileSystemWatcher1.Changed += new System.IO.FileSystemEventHandler( fileSystemWatcher1_Changed );
    }

1
คุณไม่จำเป็นต้องเรียกใช้ตัวสร้างประเภทผู้รับมอบสิทธิ์ this.fileSystemWatcher1.Changed -= this.fileSystemWatcher1_Changed;ควรทำสิ่งที่ถูกต้อง
bartonjs

@ Bartonjs ขอบคุณสำหรับสิ่งนั้น ฉันไม่แน่ใจว่าทำไมฉันถึงเรียกตัวสร้างทั้งหมด สุจริตเป็นไปได้มากที่สุดความผิดพลาดมือใหม่ แม้ว่าจะดูเหมือนว่าการแฮกของการแก้ไขของฉันจะทำงานได้ดี
Fancy_Mammoth

2

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

        var lastRead = DateTime.MinValue;

        Watcher = new FileSystemWatcher(...)
        {
            NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite,
            Filter = "*.dll",
            IncludeSubdirectories = false,
        };
        Watcher.Changed += (senderObject, ea) =>
        {
            var now = DateTime.Now;
            var lastWriteTime = File.GetLastWriteTime(ea.FullPath);

            if (now == lastWriteTime)
            {
                return;
            }

            if (lastWriteTime != lastRead)
            {
                // do something...
                lastRead = lastWriteTime;
            }
        };

        Watcher.EnableRaisingEvents = true;

เช่นเดียวกับคำตอบนี้
Jean-Paul

2

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

private void EventCallback(object sender, FileSystemEventArgs e)
{
    var fileName = e.FullPath;

    if (!File.Exists(fileName))
    {
        // We've dealt with the file, this is just supressing further events.
        return;
    }

    // File exists, so move it to a working directory. 
    File.Move(fileName, [working directory]);

    // Kick-off whatever processing is required.
}

2

รหัสนี้ใช้ได้สำหรับฉัน

        private void OnChanged(object source, FileSystemEventArgs e)
    {

        string fullFilePath = e.FullPath.ToString();
        string fullURL = buildTheUrlFromStudyXML(fullFilePath);

        System.Diagnostics.Process.Start("iexplore", fullURL);

        Timer timer = new Timer();
        ((FileSystemWatcher)source).Changed -= new FileSystemEventHandler(OnChanged);
        timer.Interval = 1000;
        timer.Elapsed += new ElapsedEventHandler(t_Elapsed);
        timer.Start();
    }

    private void t_Elapsed(object sender, ElapsedEventArgs e)
    {
        ((Timer)sender).Stop();
        theWatcher.Changed += new FileSystemEventHandler(OnChanged);
    }

2

ส่วนใหญ่สำหรับอนาคตฉัน :)

ฉันเขียน wrapper โดยใช้ Rx:

 public class WatcherWrapper : IDisposable
{
    private readonly FileSystemWatcher _fileWatcher;
    private readonly Subject<FileSystemEventArgs> _infoSubject;
    private Subject<FileSystemEventArgs> _eventSubject;

    public WatcherWrapper(string path, string nameFilter = "*.*", NotifyFilters? notifyFilters = null)
    {
        _fileWatcher = new FileSystemWatcher(path, nameFilter);

        if (notifyFilters != null)
        {
            _fileWatcher.NotifyFilter = notifyFilters.Value;
        }

        _infoSubject = new Subject<FileSystemEventArgs>();
        _eventSubject = new Subject<FileSystemEventArgs>();

        Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Changed").Select(e => e.EventArgs)
            .Subscribe(_infoSubject.OnNext);
        Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Created").Select(e => e.EventArgs)
            .Subscribe(_infoSubject.OnNext);
        Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Deleted").Select(e => e.EventArgs)
            .Subscribe(_infoSubject.OnNext);
        Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Renamed").Select(e => e.EventArgs)
            .Subscribe(_infoSubject.OnNext);

        // this takes care of double events and still works with changing the name of the same file after a while
        _infoSubject.Buffer(TimeSpan.FromMilliseconds(20))
            .Select(x => x.GroupBy(z => z.FullPath).Select(z => z.LastOrDefault()).Subscribe(
                infos =>
                {
                    if (infos != null)
                        foreach (var info in infos)
                        {
                            {
                                _eventSubject.OnNext(info);
                            }
                        }
                });

        _fileWatcher.EnableRaisingEvents = true;
    }

    public IObservable<FileSystemEventArgs> FileEvents => _eventSubject;


    public void Dispose()
    {
        _fileWatcher?.Dispose();
        _eventSubject.Dispose();
        _infoSubject.Dispose();
    }
}

การใช้งาน:

var watcher = new WatcherWrapper(_path, "*.info");
// all more complicated and scenario specific filtering of events can be done here    
watcher.FileEvents.Where(x => x.ChangeType != WatcherChangeTypes.Deleted).Subscribe(x => //do stuff)

1

ฉันเปลี่ยนวิธีตรวจสอบไฟล์ในไดเรกทอรี แทนที่จะใช้ FileSystemWatcher ฉันสำรวจสถานที่บนเธรดอื่นแล้วดู LastWriteTime ของไฟล์

DateTime lastWriteTime = File.GetLastWriteTime(someFilePath);

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


9
เช่นเดียวกับที่คุณต้องเผาวงจรพื้นหลังแทนที่จะได้รับแจ้งจากเหตุการณ์ของระบบ
Matthew Whited

1

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

private void OnChanged(object source, FileSystemEventArgs e)
{
    try
    {
        using (var fs = File.OpenWrite(e.FullPath))
        {
        }
        //do your stuff
    }
    catch (Exception)
    {
        //no write access, other app not done
    }
}

เพียงแค่เปิดเพื่อเขียนดูเหมือนจะไม่ยกเหตุการณ์ที่เปลี่ยนแปลง ดังนั้นควรปลอดภัย


1
FileReadTime = DateTime.Now;

private void File_Changed(object sender, FileSystemEventArgs e)
{            
    var lastWriteTime = File.GetLastWriteTime(e.FullPath);
    if (lastWriteTime.Subtract(FileReadTime).Ticks > 0)
    {
        // code
        FileReadTime = DateTime.Now;
    }
}

1
แม้ว่านี่อาจเป็นทางออกที่ดีที่สุดสำหรับคำถามที่ถาม แต่ก็เป็นเรื่องดีเสมอที่จะเพิ่มความคิดเห็นว่าทำไมคุณถึงเลือกวิธีนี้และทำไมคุณถึงคิดว่ามันใช้ได้ :)
waka

1

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

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

using System.Threading; // used for backgroundworker
using System.Diagnostics; // used for file information
private static IDictionary<string, string> fileModifiedTable = new Dictionary<string, string>(); // used to keep track of our changed events

private void fswFileWatch_Changed( object sender, FileSystemEventArgs e )
    {
        try
        {
           //check if we already have this value in our dictionary.
            if ( fileModifiedTable.TryGetValue( e.FullPath, out sEmpty ) )
            {              
                //compare timestamps      
                if ( fileModifiedTable[ e.FullPath ] != File.GetLastWriteTime( e.FullPath ).ToString() )
                {        
                    //lock the table                
                    lock ( fileModifiedTable )
                    {
                        //make sure our file is still valid
                        if ( File.Exists( e.FullPath ) )
                        {                               
                            // create a new background worker to do our task while the main thread stays awake. Also give it do work and work completed handlers
                            BackgroundWorker newThreadWork = new BackgroundWorker();
                            newThreadWork.DoWork += new DoWorkEventHandler( bgwNewThread_DoWork );
                            newThreadWork.RunWorkerCompleted += new RunWorkerCompletedEventHandler( bgwNewThread_RunWorkerCompleted );

                            // capture the path
                            string eventFilePath = e.FullPath;
                            List<object> arguments = new List<object>();

                            // add arguments to pass to the background worker
                            arguments.Add( eventFilePath );
                            arguments.Add( newEvent.File_Modified );

                            // start the new thread with the arguments
                            newThreadWork.RunWorkerAsync( arguments );

                            fileModifiedTable[ e.FullPath ] = File.GetLastWriteTime( e.FullPath ).ToString(); //update the modified table with the new timestamp of the file.
                            FILE_MODIFIED_FLAG.WaitOne(); // wait for the modified thread to complete before firing the next thread in the event multiple threads are being worked on.
                        }
                    }
                }
            }
        }
        catch ( IOException IOExcept )
        {
            //catch any errors
            postError( IOExcept, "fswFileWatch_Changed" );
        }
    }

ใช้สิ่งนี้ในหนึ่งในโครงการของฉัน ใช้งานได้ดี!
Tyler Montney

ไม่สามารถใช้งานได้เนื่องจากเหตุการณ์ที่ถูกไล่ออกจะถูกแยกออกจากกัน: เวลาเขียนล่าสุด: 636076274162565607 เวลาเขียนล่าสุด: 636076274162655722
ศาสตราจารย์ของการเขียนโปรแกรม

1

เหตุการณ์ถ้าไม่ถามก็น่าเสียดายที่ไม่มีตัวอย่างโซลูชันสำเร็จรูปสำหรับ F # เพื่อแก้ไขนี่คือสูตรของฉันเพียงเพราะฉันสามารถและ F # เป็นภาษา. NET ที่ยอดเยี่ยม

เหตุการณ์ที่ซ้ำกันจะถูกกรองออกโดยใช้FSharp.Control.Reactiveแพคเกจซึ่งเป็นเพียง wrapper F # สำหรับส่วนขยายปฏิกิริยา ทั้งหมดที่สามารถกำหนดเป็นกรอบเต็มหรือnetstandard2.0:

let createWatcher path filter () =
    new FileSystemWatcher(
        Path = path,
        Filter = filter,
        EnableRaisingEvents = true,
        SynchronizingObject = null // not needed for console applications
    )

let createSources (fsWatcher: FileSystemWatcher) =
    // use here needed events only. 
    // convert `Error` and `Renamed` events to be merded
    [| fsWatcher.Changed :> IObservable<_>
       fsWatcher.Deleted :> IObservable<_>
       fsWatcher.Created :> IObservable<_>
       //fsWatcher.Renamed |> Observable.map renamedToNeeded
       //fsWatcher.Error   |> Observable.map errorToNeeded
    |] |> Observable.mergeArray

let handle (e: FileSystemEventArgs) =
    printfn "handle %A event '%s' '%s' " e.ChangeType e.Name e.FullPath 

let watch path filter throttleTime =
    // disposes watcher if observer subscription is disposed
    Observable.using (createWatcher path filter) createSources
    // filter out multiple equal events
    |> Observable.distinctUntilChanged
    // filter out multiple Changed
    |> Observable.throttle throttleTime
    |> Observable.subscribe handle

[<EntryPoint>]
let main _args =
    let path = @"C:\Temp\WatchDir"
    let filter = "*.zip"
    let throttleTime = TimeSpan.FromSeconds 10.
    use _subscription = watch path filter throttleTime
    System.Console.ReadKey() |> ignore
    0 // return an integer exit code

1

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

private FileSystemWatcher watcher = new FileSystemWatcher();
...
watcher.Path = "E:\\data";
watcher.NotifyFilter = NotifyFilters.LastWrite ;
watcher.Filter = "data.txt";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;

...

private void OnChanged(object source, FileSystemEventArgs e)
   {
    System.Timers.Timer t = new System.Timers.Timer();
    try
    {
        watcher.Changed -= new FileSystemEventHandler(OnChanged);
        watcher.EnableRaisingEvents = false;

        t.Interval = 500;
        t.Elapsed += (sender, args) => t_Elapsed(sender, e);
        t.Start();
    }
    catch(Exception ex) {
        ;
    }
}

private void t_Elapsed(object sender, FileSystemEventArgs e) 
   {
    ((System.Timers.Timer)sender).Stop();
       //.. Do you stuff HERE ..
     watcher.Changed += new FileSystemEventHandler(OnChanged);
     watcher.EnableRaisingEvents = true;
}

1

ลองสิ่งนี้มันใช้งานได้ดี

  private static readonly FileSystemWatcher Watcher = new FileSystemWatcher();
    static void Main(string[] args)
    {
        Console.WriteLine("Watching....");

        Watcher.Path = @"D:\Temp\Watcher";
        Watcher.Changed += OnChanged;
        Watcher.EnableRaisingEvents = true;
        Console.ReadKey();
    }

    static void OnChanged(object sender, FileSystemEventArgs e)
    {
        try
        {
            Watcher.Changed -= OnChanged;
            Watcher.EnableRaisingEvents = false;
            Console.WriteLine($"File Changed. Name: {e.Name}");
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception);
        }
        finally
        {
            Watcher.Changed += OnChanged;
            Watcher.EnableRaisingEvents = true;
        }
    }

1

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

ฉันใช้. NET async ที่นี่เพื่อช่วยให้ฉันทำเกลียว

    private static int _fileSystemWatcherCounts;
    private async void OnChanged(object sender, FileSystemEventArgs e)
    {
        // Filter several calls in short period of time
        Interlocked.Increment(ref _fileSystemWatcherCounts);
        await Task.Delay(100);
        if (Interlocked.Decrement(ref _fileSystemWatcherCounts) == 0)
            DoYourWork();
    }

1

ฉันคิดว่าทางออกที่ดีที่สุดในการแก้ปัญหาคือใช้ส่วนขยายปฏิกิริยาเมื่อคุณแปลงเหตุการณ์เป็นที่สังเกตได้จากนั้นคุณสามารถเพิ่ม Throttling (.. ) (เดิมเรียกว่า Debounce (.. ))

โค้ดตัวอย่างที่นี่

        var templatesWatcher = new FileSystemWatcher(settingsSnapshot.Value.TemplatesDirectory)
        {
            NotifyFilter = NotifyFilters.LastWrite,
            IncludeSubdirectories = true
        };

        templatesWatcher.EnableRaisingEvents = true;

        Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
                addHandler => templatesWatcher.Changed += addHandler,
                removeHandler => templatesWatcher.Changed -= removeHandler)
            .Throttle(TimeSpan.FromSeconds(5))
            .Subscribe(args =>
            {
                _logger.LogInformation($"Template file {args.EventArgs.Name} has changed");
                //TODO do something
            });

0

ฉันสามารถทำได้โดยเพิ่มฟังก์ชั่นที่จะตรวจสอบรายการที่ซ้ำกันในอาร์เรย์บัฟเฟอร์

จากนั้นดำเนินการหลังจากที่อาร์เรย์ไม่ได้ถูกแก้ไขเป็นเวลา X โดยใช้ตัวจับเวลา: - รีเซ็ตตัวจับเวลาทุกครั้งที่บางสิ่งถูกเขียนลงในบัฟเฟอร์ - ดำเนินการกับเห็บ

นี่จะเป็นการจับคู่การทำซ้ำอีกประเภทหนึ่ง หากคุณแก้ไขไฟล์ภายในโฟลเดอร์โฟลเดอร์จะส่งเหตุการณ์ Change

Function is_duplicate(str1 As String) As Boolean
    If lb_actions_list.Items.Count = 0 Then
        Return False
    Else
        Dim compStr As String = lb_actions_list.Items(lb_actions_list.Items.Count - 1).ToString
        compStr = compStr.Substring(compStr.IndexOf("-") + 1).Trim

        If compStr <> str1 AndAlso compStr.parentDir <> str1 & "\" Then
            Return False
        Else
            Return True
        End If
    End If
End Function

Public Module extentions
<Extension()>
Public Function parentDir(ByVal aString As String) As String
    Return aString.Substring(0, CInt(InStrRev(aString, "\", aString.Length - 1)))
End Function
End Module

0

วิธีนี้ใช้ได้ผลกับฉันในแอปพลิเคชันการผลิต:

สิ่งแวดล้อม:

VB.Net Framework 4.5.2

ตั้งค่าคุณสมบัติของวัตถุด้วยตนเอง: NotifyFilter = Size

จากนั้นใช้รหัสนี้:

Public Class main
    Dim CalledOnce = False
    Private Sub FileSystemWatcher1_Changed(sender As Object, e As IO.FileSystemEventArgs) Handles FileSystemWatcher1.Changed
            If (CalledOnce = False) Then
                CalledOnce = True
                If (e.ChangeType = 4) Then
                    ' Do task...
                CalledOnce = False
            End If
        End Sub
End Sub

ใช้แนวคิดเดียวกันเช่น @Jamie Krcmar แต่สำหรับ VB.NET
wpcoder

0

ลองนี้สิ!

string temp="";

public void Initialize()
{
   FileSystemWatcher _fileWatcher = new FileSystemWatcher();
  _fileWatcher.Path = "C:\\Folder";
  _fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
  _fileWatcher.Filter = "Version.txt";
  _fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
  _fileWatcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
   .......
if(temp=="")
{
   //do thing you want.
   temp = e.name //name of text file.
}else if(temp !="" && temp != e.name)
{
   //do thing you want.
   temp = e.name //name of text file.
}else
{
  //second fire ignored.
}

}

0

ฉันต้องรวมแนวคิดหลายอย่างจากโพสต์ด้านบนและเพิ่มการตรวจสอบการล็อคไฟล์เพื่อให้มันทำงานได้สำหรับฉัน:

FileSystemWatcher fileSystemWatcher;

private void DirectoryWatcher_Start()
{
    FileSystemWatcher fileSystemWatcher = new FileSystemWatcher
    {
        Path = @"c:\mypath",
        NotifyFilter = NotifyFilters.LastWrite,
        Filter = "*.*",
        EnableRaisingEvents = true
    };

    fileSystemWatcher.Changed += new FileSystemEventHandler(DirectoryWatcher_OnChanged);
}

private static void WaitUntilFileIsUnlocked(String fullPath, Action<String> callback, FileAccess fileAccess = FileAccess.Read, Int32 timeoutMS = 10000)
{
    Int32 waitMS = 250;
    Int32 currentMS = 0;
    FileInfo file = new FileInfo(fullPath);
    FileStream stream = null;
    do
    {
        try
        {
            stream = file.Open(FileMode.Open, fileAccess, FileShare.None);
            stream.Close();
            callback(fullPath);
            return;
        }
        catch (IOException)
        {
        }
        finally
        {
            if (stream != null)
                stream.Dispose();
        }
        Thread.Sleep(waitMS);
        currentMS += waitMS;
    } while (currentMS < timeoutMS);
}    

private static Dictionary<String, DateTime> DirectoryWatcher_fileLastWriteTimeCache = new Dictionary<String, DateTime>();

private void DirectoryWatcher_OnChanged(Object source, FileSystemEventArgs ev)
{
    try
    {
        lock (DirectoryWatcher_fileLastWriteTimeCache)
        {
            DateTime lastWriteTime = File.GetLastWriteTime(ev.FullPath);
            if (DirectoryWatcher_fileLastWriteTimeCache.ContainsKey(ev.FullPath))
            {
                if (DirectoryWatcher_fileLastWriteTimeCache[ev.FullPath].AddMilliseconds(500) >= lastWriteTime)
                    return;     // file was already handled
            }

            DirectoryWatcher_fileLastWriteTimeCache[ev.FullPath] = lastWriteTime;
        }

        Task.Run(() => WaitUntilFileIsUnlocked(ev.FullPath, fullPath =>
        {
            // do the job with fullPath...
        }));

    }
    catch (Exception e)
    {
        // handle exception
    }
}

0

ฉันเข้าหาสองครั้งสร้างปัญหาเช่นนี้ซึ่งไม่สนใจเหตุการณ์แรก:

Private WithEvents fsw As New System.IO.FileSystemWatcher
Private complete As New List(Of String)

Private Sub fsw_Created(ByVal sender As Object, _
    ByVal e As System.IO.FileSystemEventArgs) Handles fsw.Created

    If Not complete.Contains(e.FullPath) Then
        complete.Add(e.FullPath)

    Else
        complete.Remove(e.FullPath)
        Dim th As New Threading.Thread(AddressOf hprocess)
        th.Start(e)

    End If

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