VB.NET FileSystemWatcher 多个更改事件

发布于 2024-07-12 03:07:23 字数 555 浏览 5 评论 0原文

我有以下代码:


Imports System.IO

Public Class Blah
    Public Sub New()
        InitializeComponent()

        Dim watcher As New FileSystemWatcher("C:\")
        watcher.EnableRaisingEvents = True

        AddHandler watcher.Changed, AddressOf watcher_Changed
    End Sub

    Private Sub watcher_Changed(ByVal sender As Object, ByVal e As FileSystemEventArgs)
        MsgBox(e.FullPath)
    End Sub
End Class

当我运行它并将更改保存到 C 驱动器上的文件时,代码运行良好,只是它执行了 watcher_Changed() 方法四次。 知道为什么吗? 每次的changeType都是“4”。

谢谢。

I have the following code:


Imports System.IO

Public Class Blah
    Public Sub New()
        InitializeComponent()

        Dim watcher As New FileSystemWatcher("C:\")
        watcher.EnableRaisingEvents = True

        AddHandler watcher.Changed, AddressOf watcher_Changed
    End Sub

    Private Sub watcher_Changed(ByVal sender As Object, ByVal e As FileSystemEventArgs)
        MsgBox(e.FullPath)
    End Sub
End Class

When I run it and save changes to a file on my C drive, the code works great, except it executes the watcher_Changed() method four times. Any idea why? The changeType is "4" every time.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

轻拂→两袖风尘 2024-07-19 03:07:23

来自 VS.NET 文档的“FileSystemWatcher 组件故障排除”部分...

为单个操作生成多个创建的事件

您可能会注意到,在某些情况下,单个创建事件会生成多个由您的组件处理的 Created 事件。 例如,如果您使用 FileSystemWatcher 组件监视目录中新文件的创建,然后使用记事本创建文件进行测试,则即使只创建了一个文件,您也可能会看到生成了两个 Created 事件。 这是因为记事本在写入过程中执行多个文件系统操作。 记事本批量写入磁盘,创建文件内容,然后创建文件属性。 其他应用程序可以以相同的方式执行。 由于 FileSystemWatcher 监视操作系统活动,因此将拾取这些应用程序触发的所有事件。

注意:记事本还可能导致其他有趣的事件生成。 例如,如果您使用 ChangeEventFilter 指定您只想监视属性更改,然后使用记事本写入正在监视的目录中的文件,则会引发事件 。 这是因为记事本在此操作期间更新了文件的已存档属性。

From the "Troubleshooting FileSystemWatcher Components" section of the VS.NET documentation...

Multiple Created Events Generated for a Single Action

You may notice in certain situations that a single creation event generates multiple Created events that are handled by your component. For example, if you use a FileSystemWatcher component to monitor the creation of new files in a directory, and then test it by using Notepad to create a file, you may see two Created events generated even though only a single file was created. This is because Notepad performs multiple file system actions during the writing process. Notepad writes to the disk in batches that create the content of the file and then the file attributes. Other applications may perform in the same manner. Because FileSystemWatcher monitors the operating system activities, all events that these applications fire will be picked up.

Note: Notepad may also cause other interesting event generations. For example, if you use the ChangeEventFilter to specify that you want to watch only for attribute changes, and then you write to a file in the directory you are watching using Notepad, you will raise an event . This is because Notepad updates the Archived attribute for the file during this operation.

在风中等你 2024-07-19 03:07:23

不久前,我也遇到了同样的问题。

经过一番网络搜索后,看来我并不是唯一一个遇到这个问题的人。 :)
所以,也许这是 FileSystemWatcher 中的一个缺陷......

我已经通过跟踪上次引发事件处理程序的时间来解决它。 如果它是在 xxx 毫秒前引发的,我会从事件处理程序中返回。
如果有人知道更优雅的修复方法; 请告诉我。 :)

这就是我解决这个问题的方法:

if( e.ChangeType == WatcherChangeTypes.Changed )
{

    // There is a nasty bug in the FileSystemWatch which causes the 
    // events of the FileSystemWatcher to be called twice.
    // There are a lot of resources about this to be found on the Internet,
    // but there are no real solutions.
    // Therefore, this workaround is necessary: 
    // If the last time that the event has been raised is only a few msec away, 
    // we ignore it.
    if( DateTime.Now.Subtract (_lastTimeFileWatcherEventRaised).TotalMilliseconds < 500 )
    {
        return;
    }


    _lastTimeFileWatcherEventRaised = DateTime.Now;


    .. handle event

A while ago, I've experience the same problem.

After some searching thtrough the web, it appeared that I was not the only one having this issue. :)
So, perhaps it is a flaw in the FileSystemWatcher ...

I've solved it by keeping track of the last time the eventhandler has been raised. If it has been raised less then xxx msec ago, I return from my eventhandler.
If anyone knows a fix that is more elegant; plz let me know. :)

This is how I've worked around it:

if( e.ChangeType == WatcherChangeTypes.Changed )
{

    // There is a nasty bug in the FileSystemWatch which causes the 
    // events of the FileSystemWatcher to be called twice.
    // There are a lot of resources about this to be found on the Internet,
    // but there are no real solutions.
    // Therefore, this workaround is necessary: 
    // If the last time that the event has been raised is only a few msec away, 
    // we ignore it.
    if( DateTime.Now.Subtract (_lastTimeFileWatcherEventRaised).TotalMilliseconds < 500 )
    {
        return;
    }


    _lastTimeFileWatcherEventRaised = DateTime.Now;


    .. handle event
小…红帽 2024-07-19 03:07:23

Frederik 的解决方案是迄今为止我遇到的最好的解决方案。 然而我发现 500 毫秒太慢了。 在我的应用程序中,用户能够在 0.5 秒内轻松地对文件执行两个操作,因此我将其降低到 100,到目前为止效果很好。 他的 C# 有点 fubar(它不会转换),所以这是 VB 版本:

Public LastTimeFileWatcherEventRaised As DateTime

If DateTime.Now.Subtract(LastTimeFileWatcherEventRaised).TotalMilliseconds < 100 Then Return

LastTimeFileWatcherEventRaised = DateTime.Now

.. handle event here

Frederik's solution is by the far the best thing I've come across. However I found 500 milliseconds to be far too slow. In my app a user is able to perform two actions on a file easily within .5 seconds so I lowered it to 100 and so far it's working out fine. His C# was a little fubar (it wouldn't convert) so here's the VB version:

Public LastTimeFileWatcherEventRaised As DateTime

If DateTime.Now.Subtract(LastTimeFileWatcherEventRaised).TotalMilliseconds < 100 Then Return

LastTimeFileWatcherEventRaised = DateTime.Now

.. handle event here
情绪失控 2024-07-19 03:07:23

我对这个问题的解决方案有点像 Erics,除了我使用 System.Windows.Forms.Timer 而不是启动新线程。 我的想法是,仅当 x 毫秒过去且没有任何文件更改事件时才处理更改事件。 请注意,一切都发生在 GUI 线程上,因此不存在线程问题。 我使用 x = 100。

    private Dictionary<String, FileSystemEventArgs> xmlFileChangedEvents = new Dictionary<string, FileSystemEventArgs>();
    private void debugXmlWatcher_Changed(object sender, FileSystemEventArgs e)
    {
        if (!xmlFileChangedEvents.ContainsKey(e.Name))
            xmlFileChangedEvents.Add(e.Name, e);
        xmlChangeTimer.Stop();//Reset the Forms.Timer so that it times out in 100 ms
        xmlChangeTimer.Start();
    }

    private void xmlChangeTimer_Tick(object sender, EventArgs e)
    {
        foreach (FileSystemEventArgs eventArg in xmlFileChangedEvents.Values)
        {
            //
            //Handle the file changed event here
            //
        }
        xmlFileChangedEvents.Clear();
    }

My solution to this problem is a bit like Erics except I use a System.Windows.Forms.Timer in stead of starting a new thread. The idea is that I handle the change event only when x ms have passed without any file changed events. Note that everything takes place on the GUI thread so there are no threading issues. I use x = 100.

    private Dictionary<String, FileSystemEventArgs> xmlFileChangedEvents = new Dictionary<string, FileSystemEventArgs>();
    private void debugXmlWatcher_Changed(object sender, FileSystemEventArgs e)
    {
        if (!xmlFileChangedEvents.ContainsKey(e.Name))
            xmlFileChangedEvents.Add(e.Name, e);
        xmlChangeTimer.Stop();//Reset the Forms.Timer so that it times out in 100 ms
        xmlChangeTimer.Start();
    }

    private void xmlChangeTimer_Tick(object sender, EventArgs e)
    {
        foreach (FileSystemEventArgs eventArg in xmlFileChangedEvents.Values)
        {
            //
            //Handle the file changed event here
            //
        }
        xmlFileChangedEvents.Clear();
    }
在梵高的星空下 2024-07-19 03:07:23

观察者更改事件处理程序将触发 3 个事件...创建、删除、更改。 仅当您重命名文件时,才会触发 onrenamed 事件。 这可能就是您收到 4 个警报的原因。
此外,大多数程序在关闭文件之前会对文件运行多个操作。 每个事件都被视为更改,因此每次都会触发 on_changed 事件。

the watcher changed event handler will fire on 3 events... create, delete,change. Only when you rename a file will the onrenamed event fire. That is probably why you are getting 4 alerts.
Also most programs run multiple operations on a file before closing it. Every event is considered a change and so the on_changed event is fired every time.

无畏 2024-07-19 03:07:23

假设每次路径都相同,您用来保存文件的程序是否可能实际上是分段保存的? 或者您是否实例化了多个 Blah


编辑:
您是否正在运行任何防病毒自动保护软件? 这些可能会在此过程中接触文件。

来自 MSDN 文档

常见的文件系统操作可能
引发多个事件。 为了
例如,当文件从一个
目录到另一个,几个
OnChanged 和一些 OnCreated 和
可能会引发 OnDeleted 事件。
移动文件是一项复杂的操作
由多个简单的
操作,因此筹集了多个
事件。 同样,一些应用程序
(例如防病毒软件)
可能会导致额外的文件系统
检测到的事件
文件系统观察器。


编辑:或者也许与 Windows 保存文件的方式有关。 您可能会从不同的更改中获得多个事件。 (一个用于大小,一个用于上次写入时间戳,一个用于上次访问时间戳,还有一个用于...其他内容。)尝试设置 FileSystemWatcherNotifyFilter 属性更改为单一类型并查看是否继续获取多个事件。

Assuming the path is the same every time, is it possible the program you are using to save the file is actually doing the save in pieces? Or do you have more than one Blah instantiated?


Edit:
Do you have any antivirus auto-protect software running? Those might be touching the file in the process.

From the MSDN Documentation:

Common file system operations might
raise more than one event. For
example, when a file is moved from one
directory to another, several
OnChanged and some OnCreated and
OnDeleted events might be raised.
Moving a file is a complex operation
that consists of multiple simple
operations, therefore raising multiple
events. Likewise, some applications
(for example, antivirus software)
might cause additional file system
events that are detected by
FileSystemWatcher.


Edit: Or maybe there's something to do with how windows is saving the file. You might be getting more than one event from different changes. (One for the size, one for the last write timestamp, one for the last access timestamp, and one more for...something else.) Try setting the FileSystemWatcher's NotifyFilter property to a single type of change and see if you continue to get multiple events.

栀梦 2024-07-19 03:07:23

还有另一种可能性,你犯了错误:)
也许您在将“Blah”类用于文件监视目的之前实例化并终止它,并且忘记通过 Dispose/或任何相关的拆卸方法来实现RemoveHandler。 (?)

There is another possibility, which you are making mistake :)
Maybe you are instantiate and terminate your "Blah" class before using it for filewatching purpose, and forgetting to implement RemoveHandler by Dispose/or any related teardown method. (?)

煮酒 2024-07-19 03:07:23

我编写了一些代码来解决这个问题以及 FileSystemWatcher 的其他巧妙功能。 它发布在我的博客中: http:// precisionsoftware.blogspot.com /2009/05/filesystemwatcher-done-right.html

I wrote some code that solves this problem and other neat features of FileSystemWatcher. Its posted in my blog at: http://precisionsoftware.blogspot.com/2009/05/filesystemwatcher-done-right.html

我找到了这个页面来解决同样的问题。 从表面上看,即使您添加逻辑来有条件地处理多个事件,当后续(重复)事件发生时,任何应该处理的代码都将被中断/中止,从而导致不良行为。 我认为解决这个问题的方法是以某种方式在不同的线程上实现事件处理程序......希望这是有道理的。

干杯,

尼科

I found this page for the same problem. And from what it looks like, even if you add logic to conditionally process multiple events, any code that is supposed to be processed will be interrupted/aborted when a subsequent (duplicate) event occurs thereby causing an undesired behavior. I think a way around this would be to implement an event handler on a different thread somehow... hope this makes sense.

Cheers,

Nico

落日海湾 2024-07-19 03:07:23

我做了一个简单的课程,对我来说效果很好。 它可能对其他人有用。

using System;
using System.IO;
using System.Timers;

namespace Demo
{
    class FileWatcher
    {
        private FileSystemWatcher watcher = new FileSystemWatcher();
        private Timer t = new Timer();

        public event EventHandler FileChanged;

        public FileWatcher()
        {
            t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
            t.Interval = 1000;
        }

        public void Start(String path)
        {
            watcher.Path = Path.GetDirectoryName(path);
            watcher.Filter = Path.GetFileName(path);
            watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.CreationTime;
            watcher.EnableRaisingEvents = true;
            watcher.Changed += new FileSystemEventHandler(watcher_Changed);
        }

        void watcher_Changed(object sender, FileSystemEventArgs e)
        {
            if (!t.Enabled)
                t.Start();
        }

        void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            t.Stop();
            if (FileChanged != null)
                FileChanged(this, null);
        }
    }
}

可以这样使用:

FileWatcher FileWatcher1 = new FileWatcher();
FileWatcher1.FileChanged += new EventHandler(FileWatcher1_FileChanged);
FileWatcher1.Start("c:\test.txt");

I made a simple class that works fine for me. It may be useful for someone else.

using System;
using System.IO;
using System.Timers;

namespace Demo
{
    class FileWatcher
    {
        private FileSystemWatcher watcher = new FileSystemWatcher();
        private Timer t = new Timer();

        public event EventHandler FileChanged;

        public FileWatcher()
        {
            t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
            t.Interval = 1000;
        }

        public void Start(String path)
        {
            watcher.Path = Path.GetDirectoryName(path);
            watcher.Filter = Path.GetFileName(path);
            watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.CreationTime;
            watcher.EnableRaisingEvents = true;
            watcher.Changed += new FileSystemEventHandler(watcher_Changed);
        }

        void watcher_Changed(object sender, FileSystemEventArgs e)
        {
            if (!t.Enabled)
                t.Start();
        }

        void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            t.Stop();
            if (FileChanged != null)
                FileChanged(this, null);
        }
    }
}

Can be used like that:

FileWatcher FileWatcher1 = new FileWatcher();
FileWatcher1.FileChanged += new EventHandler(FileWatcher1_FileChanged);
FileWatcher1.Start("c:\test.txt");
就是爱搞怪 2024-07-19 03:07:23

自第一天(自 Windows 3.x 起)以来,这一直是 FindFirstChangeNotification() Win32 API 的一个令人抓狂的怪癖,而且看起来 FileSystemWatcher 只是包装了该 API。 计时器方法(如上所述)是常见的解决方法。

我通常创建一个类来包装 FileSystemWatcher 并执行多次更改调用过滤。 需要编写一些额外的工作,但在重用中会得到回报。

public class FileChangeMonitor
{
    private FileSystemWatcher _fsw;
    DateTime _lastEventTime;

    public event FileSystemEventHandler Changed;

    public FileChangeMonitor(string path, string filter)
    {
        _fsw = new FileSystemWatcher(path, filter);
        _fsw.Changed += new FileSystemEventHandler(_fsw_Changed);
        _fsw.EnableRaisingEvents = true;
        _fsw.NotifyFilter = NotifyFilters.LastWrite;
        _fsw.IncludeSubdirectories = false;
    }

    private void _fsw_Changed(object sender, FileSystemEventArgs e)
    {
        // Fix the FindFirstChangeNotification() double-call bug
        if (DateTime.Now.Subtract(_lastEventTime).TotalMilliseconds > 100)
        {
            _lastEventTime = DateTime.Now;
            if (this.Changed != null)
                this.Changed(sender, e);  // Bubble the event
        }
    }
}

然后,您可以像使用 FileSystemWatcher 一样使用 FileChangeMonitor:

FileChangeMonitor fcm = new FileChangeMonitor(path, filter);
fsm.Changed += new FileSystemEventHandler(fsm_Changed);
...

当然,上面的代码仅处理 Changed 事件和 NotifyFilters.LastWrite,但您明白了。

This has been a maddening quirk of the FindFirstChangeNotification() Win32 API since day 1 (since Windows 3.x), and it looks like FileSystemWatcher simply wraps that API. The timer approach (presented above) is the common workaround.

I usually create a class that wraps FileSystemWatcher and does the multiple-change-call filtering. A bit of extra work to write, but it pays off in reuse.

public class FileChangeMonitor
{
    private FileSystemWatcher _fsw;
    DateTime _lastEventTime;

    public event FileSystemEventHandler Changed;

    public FileChangeMonitor(string path, string filter)
    {
        _fsw = new FileSystemWatcher(path, filter);
        _fsw.Changed += new FileSystemEventHandler(_fsw_Changed);
        _fsw.EnableRaisingEvents = true;
        _fsw.NotifyFilter = NotifyFilters.LastWrite;
        _fsw.IncludeSubdirectories = false;
    }

    private void _fsw_Changed(object sender, FileSystemEventArgs e)
    {
        // Fix the FindFirstChangeNotification() double-call bug
        if (DateTime.Now.Subtract(_lastEventTime).TotalMilliseconds > 100)
        {
            _lastEventTime = DateTime.Now;
            if (this.Changed != null)
                this.Changed(sender, e);  // Bubble the event
        }
    }
}

You can then use FileChangeMonitor pretty much like you would FileSystemWatcher:

FileChangeMonitor fcm = new FileChangeMonitor(path, filter);
fsm.Changed += new FileSystemEventHandler(fsm_Changed);
...

Of course, the code above only handles the Changed event and NotifyFilters.LastWrite, but you get the idea.

烟若柳尘 2024-07-19 03:07:23

平台无关的技巧:

// Class level variable
bool m_FileSystemWatcherIsMessy = true;

// inside call back
if (m_FileSystemWatcherIsMessy) {
    m_FileSystemWatcherIsMessy = false;
    return;
} else {
    m_FileSystemWatcherIsMessy = true;
}

Platform independent trick :

// Class level variable
bool m_FileSystemWatcherIsMessy = true;

// inside call back
if (m_FileSystemWatcherIsMessy) {
    m_FileSystemWatcherIsMessy = false;
    return;
} else {
    m_FileSystemWatcherIsMessy = true;
}
无风消散 2024-07-19 03:07:23

如果您需要在表单上发生更改事件时显示它们,那么您需要使用线程。 Eric 的解决方案在这方面是最好的,因为无论有没有表单,它都可以轻松使用,从而使解决方案最灵活。 它还可以很好地处理多个重复事件,并确保仅在同一文件时才处理重复事件。 在已接受的解决方案中,如果两个文件几乎同时更改,则其中一个事件可能会被错误地忽略。

If you need to display the change events while they are happening on a form, then you need to use threading. Eric's solution is the best in this regard since it can be easily used with or without a form making the solution most flexible. It also handles the multiple duplicate events nicely and makes sure that it only eats duplicate events only if it is for THE SAME FILE. In the accepted solution, if two files are changed at near the same time, one of their events could be incorrectly ignored.

德意的啸 2024-07-19 03:07:23

这是我如何处理这个问题的概念证明。

要进行测试,请创建一个新的 Windows 窗体应用程序。 在窗体上,添加一个名为“tbMonitor”的多行文本框。 右键单击表单并转到“查看代码”。 将该代码替换为我在下面包含的代码。 请注意,我将等待时间设置为一个非常高的数字,以便您可以稍微尝试一下。 在生产中,您可能希望使这个数字低得多,可能在 10 或 15 左右。

Imports System.IO
Imports System.Threading
Public Class Form1
Private Const MILLISECONDS_TO_WAIT As Integer = 1000
Private fw As FileSystemWatcher
Private Shared AccessEntries As List(Of String)
Private Delegate Sub UpdateBoxDelegate(ByVal msg As String)
Private Sub UpdateBox(ByVal msg As String)
    If tbMonitor.InvokeRequired Then
        Invoke(New UpdateBoxDelegate(AddressOf UpdateBox), New Object() {msg})
    Else
        tbMonitor.AppendText(msg + vbCrLf)
    End If
End Sub

Private Sub AccessEntryRemovalTimer(ByVal RawFileName As Object)
    UpdateBox("Sleeping to watch for " + RawFileName.ToString + " on thread ID " + Thread.CurrentThread.ManagedThreadId.ToString)
    Thread.Sleep(MILLISECONDS_TO_WAIT)
    AccessEntries.Remove(RawFileName.ToString)
    UpdateBox("Removed " + RawFileName.ToString + " in thread ID " + Thread.CurrentThread.ManagedThreadId.ToString)
End Sub

Private Sub Changed(ByVal source As Object, ByVal e As FileSystemEventArgs)
    If AccessEntries.Contains(e.Name) Then
        UpdateBox("Ignoring a " + e.ChangeType.ToString + " notification for " + e.Name + " in thread ID " + Thread.CurrentThread.ManagedThreadId.ToString)
        Return
    End If
    Dim AccessTimerThread As Thread

    AccessEntries.Add(e.Name)
    UpdateBox("Adding " + e.Name + " to the collection and starting the watch thread.")
    AccessTimerThread = New Thread(AddressOf AccessEntryRemovalTimer)
    AccessTimerThread.IsBackground = True
    AccessTimerThread.Start(e.Name)

End Sub

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    tbMonitor.ScrollBars = ScrollBars.Both
    AccessEntries = New List(Of String)
    fw = New FileSystemWatcher
    fw.Path = "C:\temp"
    fw.NotifyFilter = NotifyFilters.LastWrite Or NotifyFilters.LastAccess Or NotifyFilters.FileName
    AddHandler fw.Changed, AddressOf Changed
    AddHandler fw.Created, AddressOf Changed
    AddHandler fw.Renamed, AddressOf Changed
    fw.EnableRaisingEvents = True
End Sub

Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
    fw.EnableRaisingEvents = False
    RemoveHandler fw.Changed, AddressOf Changed
    RemoveHandler fw.Created, AddressOf Changed
    RemoveHandler fw.Renamed, AddressOf Changed
    fw.Dispose()
End Sub
End Class

Here is a proof of concept for how I handle this.

To test, create a new Windows Forms application. On the form, add a multiline text box named "tbMonitor". Right-click on the form and go to View Code. Replace that code with the code I've included below. Note that I set the wait time to a really high number so you can play around with it a bit. In production, you'll probably want to make this number much lower, probably around 10 or 15.

Imports System.IO
Imports System.Threading
Public Class Form1
Private Const MILLISECONDS_TO_WAIT As Integer = 1000
Private fw As FileSystemWatcher
Private Shared AccessEntries As List(Of String)
Private Delegate Sub UpdateBoxDelegate(ByVal msg As String)
Private Sub UpdateBox(ByVal msg As String)
    If tbMonitor.InvokeRequired Then
        Invoke(New UpdateBoxDelegate(AddressOf UpdateBox), New Object() {msg})
    Else
        tbMonitor.AppendText(msg + vbCrLf)
    End If
End Sub

Private Sub AccessEntryRemovalTimer(ByVal RawFileName As Object)
    UpdateBox("Sleeping to watch for " + RawFileName.ToString + " on thread ID " + Thread.CurrentThread.ManagedThreadId.ToString)
    Thread.Sleep(MILLISECONDS_TO_WAIT)
    AccessEntries.Remove(RawFileName.ToString)
    UpdateBox("Removed " + RawFileName.ToString + " in thread ID " + Thread.CurrentThread.ManagedThreadId.ToString)
End Sub

Private Sub Changed(ByVal source As Object, ByVal e As FileSystemEventArgs)
    If AccessEntries.Contains(e.Name) Then
        UpdateBox("Ignoring a " + e.ChangeType.ToString + " notification for " + e.Name + " in thread ID " + Thread.CurrentThread.ManagedThreadId.ToString)
        Return
    End If
    Dim AccessTimerThread As Thread

    AccessEntries.Add(e.Name)
    UpdateBox("Adding " + e.Name + " to the collection and starting the watch thread.")
    AccessTimerThread = New Thread(AddressOf AccessEntryRemovalTimer)
    AccessTimerThread.IsBackground = True
    AccessTimerThread.Start(e.Name)

End Sub

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    tbMonitor.ScrollBars = ScrollBars.Both
    AccessEntries = New List(Of String)
    fw = New FileSystemWatcher
    fw.Path = "C:\temp"
    fw.NotifyFilter = NotifyFilters.LastWrite Or NotifyFilters.LastAccess Or NotifyFilters.FileName
    AddHandler fw.Changed, AddressOf Changed
    AddHandler fw.Created, AddressOf Changed
    AddHandler fw.Renamed, AddressOf Changed
    fw.EnableRaisingEvents = True
End Sub

Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
    fw.EnableRaisingEvents = False
    RemoveHandler fw.Changed, AddressOf Changed
    RemoveHandler fw.Created, AddressOf Changed
    RemoveHandler fw.Renamed, AddressOf Changed
    fw.Dispose()
End Sub
End Class
酷到爆炸 2024-07-19 03:07:23

我通过上面的老挝示例启发了我的解决方案。
我为该文件夹实现了一个观察程序,每次触发它时,我都会停止计时器并再次启动它以重置它。 我仅在计时器结束时触发操作,这可以防止观察者为文件创建触发操作两次。
按照要求,它是在 VB.Net 中:)

    <PermissionSet(SecurityAction.Demand, Name:="FullTrust")> Public Sub StartWatcher()

    Dim watcher As FileSystemWatcher = New FileSystemWatcher()
    watcher.Path = _MWVM.TemplatesFolder

    'Watch for changes in LastWrite times, And the renaming of files Or directories. 
    watcher.NotifyFilter = NotifyFilters.LastWrite Or NotifyFilters.FileName Or NotifyFilters.DirectoryName

    ' Only watch text files.
    watcher.Filter = "*.txt"

    'Define timer to 100 ms
    WatcherTimer.Interval = New TimeSpan(0, 0, 0, 0, 100) '100 ms

    ' Add event handlers.
    AddHandler watcher.Changed, AddressOf WatcherHandler
    AddHandler watcher.Created, AddressOf WatcherHandler
    AddHandler watcher.Deleted, AddressOf WatcherHandler
    AddHandler watcher.Renamed, AddressOf WatcherHandler

    ' Begin watching
    watcher.EnableRaisingEvents = True

End Sub

'Instantiate a timer which will prevent the 
Private WithEvents WatcherTimer As New System.Windows.Threading.DispatcherTimer
Private xmlFileChangedEvents As New Dictionary(Of String, FileSystemEventArgs)

Private Sub WatcherHandler(ByVal Sender As Object, ByVal e As FileSystemEventArgs)
    WatcherTimer.Stop() 'Reset the timer
    WatcherTimer.Start()
End Sub

Private Sub WatcherTimer_Tick(ByVal Sender As Object, ByVal e As EventArgs) Handles WatcherTimer.Tick
    WatcherTimer.Stop()
    PopulateMailTemplateList()
End Sub

I've inspired my solution with LAOS example here above.
I implement a watcher for the folder, and every times it is triggered, I stop a timer and start it again to reset it. I fire my actions only when the timer ends, which prevent watcher from triggering action twice for a file creation.
And as requested, it is in VB.Net :)

    <PermissionSet(SecurityAction.Demand, Name:="FullTrust")> Public Sub StartWatcher()

    Dim watcher As FileSystemWatcher = New FileSystemWatcher()
    watcher.Path = _MWVM.TemplatesFolder

    'Watch for changes in LastWrite times, And the renaming of files Or directories. 
    watcher.NotifyFilter = NotifyFilters.LastWrite Or NotifyFilters.FileName Or NotifyFilters.DirectoryName

    ' Only watch text files.
    watcher.Filter = "*.txt"

    'Define timer to 100 ms
    WatcherTimer.Interval = New TimeSpan(0, 0, 0, 0, 100) '100 ms

    ' Add event handlers.
    AddHandler watcher.Changed, AddressOf WatcherHandler
    AddHandler watcher.Created, AddressOf WatcherHandler
    AddHandler watcher.Deleted, AddressOf WatcherHandler
    AddHandler watcher.Renamed, AddressOf WatcherHandler

    ' Begin watching
    watcher.EnableRaisingEvents = True

End Sub

'Instantiate a timer which will prevent the 
Private WithEvents WatcherTimer As New System.Windows.Threading.DispatcherTimer
Private xmlFileChangedEvents As New Dictionary(Of String, FileSystemEventArgs)

Private Sub WatcherHandler(ByVal Sender As Object, ByVal e As FileSystemEventArgs)
    WatcherTimer.Stop() 'Reset the timer
    WatcherTimer.Start()
End Sub

Private Sub WatcherTimer_Tick(ByVal Sender As Object, ByVal e As EventArgs) Handles WatcherTimer.Tick
    WatcherTimer.Stop()
    PopulateMailTemplateList()
End Sub
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文