问题
Okay,
Probably going to be seen as an extremely amature post here, I'm not really skilled in VB or much of a programmer but i'm on a "learn by doing" drive.
I'm developing an application which creates directories for a user, and tracks the directories created and the changes to these directories using FileSystemWatcher.
I've got this little snippet so far (Updated in answer you your comments Jimi):
Private Sub of Button_Click:
watchfolder = New System.IO.FileSystemWatcher()
watchfolder.Path = TodaysScanFolder
watchfolder.Filter = ""
watchfolder.IncludeSubdirectories = True
watchfolder.NotifyFilter = IO.NotifyFilters.DirectoryName
watchfolder.NotifyFilter = watchfolder.NotifyFilter Or
IO.NotifyFilters.FileName
watchfolder.NotifyFilter = watchfolder.NotifyFilter Or
IO.NotifyFilters.Attributes
AddHandler watchfolder.Created, AddressOf logchange
AddHandler watchfolder.Deleted, AddressOf logchange
watchfolder.EnableRaisingEvents = True
btnStartScan.Enabled = False
btnStopScan.Enabled = True
End Sub
Public Sub logchange(ByVal source As Object, ByVal e As _
System.IO.FileSystemEventArgs)
If e.ChangeType = IO.WatcherChangeTypes.Created Then
txt_folderactivity.Text &= "File " & e.FullPath &
" has been created" & vbCrLf
End If
If e.ChangeType = IO.WatcherChangeTypes.Deleted Then
txt_folderactivity.Text &= "File " & e.FullPath &
" has been deleted" & vbCrLf
End If
End Sub
With each additional activity, I want to append the output to txt_folderactivity.Text with a 1, prior to this. Then 2, 3, so on so forth.
You see this text box, on change writes output to a text file like so:
Private Sub txt_folderactivity_TextChanged(sender As Object, e As EventArgs) Handles txt_folderactivity.TextChanged
str = txt_folderactivity.Text
My.Computer.FileSystem.WriteAllText(LogFileLoc, str, True)
End Sub
I would like each line/entry to that text file to begin 1, 2, 3 and so on. Plus, as I said total beginner with VB, seem to have pieced together something which looks a bit messy but is functional and will be business critical, any pointers for a good start on relevant VB learning resources would be very helpful. Thanks All
回答1:
This is a class that inherits from FileSystemWatcher
.
It takes care of the configuration, enables and disables the raising of events and has basic logging and reporting capabilities.
Configuration of FileSystemWatcher
:
.BeginInit()
- .EndInit()
BeginInit()
is used to prevent the raising of events before the FileSystemWatcher
setup is completed.
.SynchronizingObject
Set the SynchronizingObject
to a Form component. Since the FileSystemWatcher
events are marshalled from the system thread pool, this ensures that the event delegates are called on the same thread where the component is created.
If SynchronizingObject
is null, accessing the component may result in an Exception or, worse, in a silent failure.
.InternalBufferSize
The internal buffer is used to store the events register and a file path. A buffer larger that the default (8192 bytes) can prevent a buffer overflow, which compromises the rising of events. Here it's set to 32768 bytes.
.Path
For network drives/shares use UNC Paths.
How to use it:
Define a public object that references the FileWatcher Class
:
Public FileWatch As FileWatcher
Then you can initialize it whenever you need it:
FileWatch = New FileWatcher("[Path to watch]",
"*", 'All files
"[Path to LogFile]",
Me, 'Synchronizing Object
Me.[TextBox control used as monitor])
[TextBox control used as monitor]
can be Nothing
if none is used.
The FileWatcher
doesn't immediately start to raise/log events; the EnableRaisingEvents
property is set to False
in the initialization method.
You can start and stop it's activity with its methods:
Me.FileWatch.StartWatcher()
Me.FileWatch.StopWatcher()
Starting and stopping the FileWatcher
is a registered (logged) activity.
A List(Of DateTime)
is used to store these events. See the FW_EventLogger Class
.
Other stored informations are:
- Number of files Deleted.
- Number of files Created
- Overall number of registered events (for comparison).
- Size of the Log File.
The ActivityReport()
property returns a simple report:
Dictionary(Of String, String) = FileWatcher.ActivityReport()
This is what the Log File looks like:
00000001 - 2018/02/28 21:00:25 - File D:\Temp\New Text Document.txt has been created
00000002 - 2018/02/28 21:00:29 - File D:\Temp\New Microsoft Access Database.accdb has been created
00000003 - 2018/02/28 21:00:34 - File D:\Temp\New WinZip File.zip has been created
00000004 - 2018/02/28 21:00:44 - File D:\Temp\New Microsoft Access Database.accdb has been deleted
00000005 - 2018/02/28 21:00:44 - File D:\Temp\New Text Document.txt has been deleted
00000006 - 2018/02/28 21:00:44 - File D:\Temp\New WinZip File.zip has been deleted
Limitatons: If requested (a TextBox control reference is passed to the class initialization), the UI is updated synchronously through a synchronization object. If the UI thread is busy for some reason, the events the underlying FileSystemWatcher is buffering will pile up. This can (and since it can it will) cause the loss of events. This is the reason why the internal buffer is set to 4x the default. Anyway, it won't ever be enough if the monitored activity is high. In normal condition, it can handle 10/sec. events without problem. Beyond that, an asynchronous proxy method that makes use a FIFO queue buffer must be placed between the event listeners and the data consumers.
Built on:
Visual Studio 2013, Update 5
.Net Framework 4.7.1
Updated to:
Visual Studio 2017, 15.8.4
.Net Framework 4.7.1
Imports System.Collections.Generic
Imports System.IO
Imports System.Windows.Forms
Public Class FileWatcher
Inherits FileSystemWatcher
Private EventLogger As FW_EventLogger
Private Prompt As TextBox = Nothing
Public Sub New()
Me.New("", "", "", Nothing, Nothing)
End Sub
Public Sub New(fswPath As String, fswFilter As String, logFile As String, SyncObject As Form, SyncPrompt As TextBox)
Me.Prompt = SyncPrompt
Me.EventLogger = New FW_EventLogger With {.LogFileName = logFile}
SetupFileWatcher(fswPath, fswFilter, SyncObject)
End Sub
Public Sub StartWatcher()
Me.EventLogger.TimeStart.Add(DateTime.UtcNow)
If Me.Prompt IsNot Nothing Then
Me.Prompt.AppendText(String.Format("Logger Start Time: {0}" +
Environment.NewLine, DateTime.UtcNow.ToString()))
End If
Me.EnableRaisingEvents = True
End Sub
Public Sub StopWatcher()
Me.EnableRaisingEvents = False
Me.EventLogger.TimeStop.Add(DateTime.UtcNow)
If Me.Prompt IsNot Nothing Then
Me.Prompt.AppendText(String.Format("Logger Stop Time: {0}" +
Environment.NewLine, DateTime.UtcNow.ToString()))
End If
End Sub
Public ReadOnly Property ActivityReport() As Dictionary(Of String, String)
Get
Return Me.CreateActivityReport()
End Get
End Property
Public Property PromptControl As TextBox
Get
Return Me.Prompt
End Get
Set(value As TextBox)
Me.Prompt = value
End Set
End Property
Public Sub SetupFileWatcher(fwPath As String, fwFilter As String, SyncObject As Form)
If fwPath.Length = 0 Then
Return
End If
Me.BeginInit()
Me.SynchronizingObject = SyncObject
Me.InternalBufferSize = 32768
Me.IncludeSubdirectories = True
Me.Filter = fwFilter
Me.Path = fwPath
Me.NotifyFilter = NotifyFilters.FileName Or NotifyFilters.CreationTime
Me.EnableRaisingEvents = False
'Set the handler to the events you want to receive
AddHandler Me.Created, New FileSystemEventHandler(AddressOf Me.OnCreated)
AddHandler Me.Deleted, New FileSystemEventHandler(AddressOf Me.OnDeleted)
'The other events, should they become necessary.
'this.Changed += new FileSystemEventHandler(this.OnChanged);
'this.Renamed += new RenamedEventHandler(this.OnRenamed);
Me.EndInit()
End Sub
Private Function CreateActivityReport() As Dictionary(Of String, String)
With Me.EventLogger
Dim log As New Dictionary(Of String, String)
log.Add("Created", .FileCreated.ToString())
log.Add("Deleted", .FileDeleted.ToString())
log.Add("TotalEvents", .EventsLogged.ToString())
log.Add("LogFileSize", If(File.Exists(.LogFileName), New FileInfo(.LogFileName).Length.ToString(), "N/A"))
log.Add("StartTime", If(.TimeStart.Count > 0, .TimeStart.First().ToString(), "Not Started"))
log.Add("LastStopTime", If(.TimeStop.Count > 0, .TimeStop.Last().ToString(), "Never"))
log.Add("Status", If(Me.EnableRaisingEvents = True, "Running", "Stopped"))
Return log
End With
End Function
Protected Overloads Sub OnCreated(sender As Object, e As FileSystemEventArgs)
Dim Msg As String = "File " & e.FullPath & " has been created"
Me.EventLogger.Update(Msg, FW_EventLogger.EventType.FileCreated)
If Me.Prompt IsNot Nothing Then
Me.Prompt.AppendText(Msg + Environment.NewLine)
Me.Prompt.ScrollToCaret()
End If
End Sub
Protected Overloads Sub OnDeleted(sender As Object, e As FileSystemEventArgs)
Dim Msg As String = "File " & e.FullPath & " has been deleted"
Me.EventLogger.Update(Msg, FW_EventLogger.EventType.FileDeleted)
If Me.Prompt IsNot Nothing Then
Me.Prompt.AppendText(Msg + Environment.NewLine)
Me.Prompt.ScrollToCaret()
End If
End Sub
'The Event Logger Class
Private Class FW_EventLogger
Sub New()
Me.TimeStart = New List(Of DateTime)
Me.TimeStop = New List(Of DateTime)
End Sub
Public Enum EventType As Integer
FileCreated = 0
FileDeleted
End Enum
Public Property FileDeleted As Integer
Public Property FileCreated As Integer
Public Property EventsLogged As Integer
Public Property TimeStart As List(Of DateTime)
Public Property TimeStop As List(Of DateTime)
Public Property LogFileName As String
Public Sub Update(NewEvent As String, TypeOfEvent As EventType)
If Me.LogFileName <> String.Empty Then
If TypeOfEvent = EventType.FileCreated Then Me.FileCreated += 1
If TypeOfEvent = EventType.FileDeleted Then Me.FileDeleted += 1
Me.EventsLogged += 1
Using LogFileWriter As StreamWriter = New StreamWriter(Me.LogFileName, True, Encoding.UTF8)
LogFileWriter.WriteLine(Me.EventsLogged.ToString().PadLeft(8, "0"c) +
" - {0} - {1}", DateTime.UtcNow.ToString("yyyy/MM/dd hh:mm:ss"), NewEvent)
End Using
End If
End Sub
End Class
End Class
来源:https://stackoverflow.com/questions/49006562/appending-an-entry-number-to-filesystemwatcher-output