|
web
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Threading(?) & FileSystemWatcherI am trying to use FileSystemWatcher to watch a log file which is written to via a 3rd party app and display the log in listbox on a form. The FileSystemWatch seems to work okay but I get the following error when I try to write to the listbox: "A first chance exception of type 'System.ArgumentNullException' occurred in System.Windows.Forms.dll" >From my search so far it seems this may be to do with the watcher being in a different thread but to be honest I haven't really figuredthe ins and outs of threading. This was meant to be a nice simple project to learn from so I'd really appreciate any tips. My form has a run button which calls the following routine (in a separate module): Private mstrPathToWatch As String Private mstrFileToWatch As String Private mfswWatchFolder As FileSystemWatcher Private frm As frmMain Public Sub StartFileWatch(ByVal frmLogForm As frmMain) mfswWatchFolder = New FileSystemWatcher mfswWatchFolder.SynchronizingObject = frmLogForm.lbxRunLog 'this was just a guess but I don't understand it and it doesn't seem to help 'Create module level reference to form so can access it from changed event frm = frmLogForm 'Specify the path we want to watch mstrPathToWatch = "C:\Documents and Settings\Andrew\My Documents\TEMP" mstrFileToWatch = "demo.txt" mfswWatchFolder.Path = mstrPathToWatch mfswWatchFolder.Filter = mstrFileToWatch 'Add a list of Filter we want to specify mfswWatchFolder.NotifyFilter = IO.NotifyFilters.DirectoryName mfswWatchFolder.NotifyFilter = mfswWatchFolder.NotifyFilter Or IO.NotifyFilters.FileName mfswWatchFolder.NotifyFilter = mfswWatchFolder.NotifyFilter Or IO.NotifyFilters.Attributes 'Add a handler for each event (in this case just file change) AddHandler mfswWatchFolder.Changed, AddressOf FileHasChanged 'Set this property to true to start watching mfswWatchFolder.EnableRaisingEvents = True End Sub The 'FileHasChanged' event created above reads the log file (successfully) and then calls a sub back on the form which writes the array to the listbox. Thanks a lot! Andrew Hi hooksie,
I think perhaps the most important piece of code you didn't post there was how you are writing to the list on the form from the thread and what parameters you are sending through. You should ideally be "invoking" any methods on the form, rather than just calling sub-routines on it from your thread. Robin Thanks for the reply.
I write each line of the log to an array and then pass to the sub which is in the form as follows: frm.LogMsg(astrLines) The form code looks like: Public Sub LogMsg(ByVal astrLogText() As String) lbxRunLog.BeginUpdate() lbxRunLog.Items.AddRange(astrLogText) 'Make sure the bottom entry is visible lbxRunLog.TopIndex = lbxRunLog.Items.Count - 1 lbxRunLog.EndUpdate() Me.lbxRunLog.Refresh() End Sub I'm interested to hear what you mean by "invoking" - thanks for taking the time to look at this. Andrew Show quoteHide quote On May 2, 10:39 pm, "Robin Tucker" <rtgro***@hotmail.co.uk> wrote: > Hi hooksie, > > I think perhaps the most important piece of code you didn't post there was > how you are writing to the list on the form from the thread and what > parameters you are sending through. You should ideally be "invoking" any > methods on the form, rather than just calling sub-routines on it from your > thread. > > Robin Hi,
Looking at your code, I have to assume astrLogText = nothing when it enters the procedure in order to throw that exception, so put a break-point and check its value before AddRange. Secondly, accessing and manipulating windows controls from a thread other than the main process thread is fraught with problems. Because you don't know what the main process thread is doing at the time, its possible to get race, lock and other such synchronisation errors. "Invoke" (BeginInvoke, EndInvoke, Invoke) allow you to call a method on a windows form or control from a thread other than the thread they were created on. I think under the hood the CLR uses the controls message pump to ensure the methods are called in an orderly manner. Use Google to find out more or look in the MSDN literature. Your keywords are: Invoke+VB.NET :). There are many examples around of using Invoke. My preference is to create a method on the main form, in this case called LogMsg and then invoke that method from your thread using a delegate. The invoke procedure would look something like this: ' Declare a delegate Private Delegate Sub _LogMsgDelegate(astrLogText() as String) ' Method to invoke the LogMsg method on the main form. Public Sub InvokeLogMsg(ByVal astrLogText() as String) Dim Parameters(0) As Object Parameters(0) = astrLogText ' If the invoke fails, it's usually because the form has been ' destroyed. The window handle of the form or control must ' be valid for the invocation to succeed. An unhandled exception ' on a form or in a control will cause .NET to dispose of it, ' resulting in any running thread failing when it invokes. Try frm.Invoke(New LogMsgDelegate(AddressOf frm.LogMsg), Parameters) Catch ex As Exception ... End Try End Sub and on your form...... Public Sub LogMsg(ByVal astrLogText() As String) lbxRunLog.BeginUpdate() lbxRunLog.Items.AddRange(astrLogText) ' Make sure the bottom entry is visible lbxRunLog.TopIndex = lbxRunLog.Items.Count - 1 lbxRunLog.EndUpdate() Me.lbxRunLog.Invalidate () End Sub Sorry I didn't test it, but cut and messed around with some code from one of my projects. Hope this is useful. Robin Show quoteHide quote > I write each line of the log to an array and then pass to the sub > which is in the form as follows: > > frm.LogMsg(astrLines) > > The form code looks like: > > Public Sub LogMsg(ByVal astrLogText() As String) > lbxRunLog.BeginUpdate() > lbxRunLog.Items.AddRange(astrLogText) > 'Make sure the bottom entry is visible > lbxRunLog.TopIndex = lbxRunLog.Items.Count - 1 > lbxRunLog.EndUpdate() > Me.lbxRunLog.Refresh() > End Sub > > I'm interested to hear what you mean by "invoking" - thanks for taking > the time to look at this. > > Andrew > > > > On May 2, 10:39 pm, "Robin Tucker" <rtgro***@hotmail.co.uk> wrote: >> Hi hooksie, >> >> I think perhaps the most important piece of code you didn't post there >> was >> how you are writing to the list on the form from the thread and what >> parameters you are sending through. You should ideally be "invoking" any >> methods on the form, rather than just calling sub-routines on it from >> your >> thread. >> >> Robin > > You solved it! My apparent problem turned out to be something simpler
than I had assumed. The last value in my array was nothing and this was actually what was causing the error. I'm still struggling with the concepts around Invoke (web resources so far a bit over my head) but I'll keep trying and have implemented your suggestion anyway - at a high level I can understand how this could cause a problem otherwise. Thanks for your help. Andrew Show quoteHide quote On May 3, 12:24 am, "Robin Tucker" <rtgro***@removehotmail.com> wrote: > Hi, > > Looking at your code, I have to assume astrLogText = nothing when it enters > the procedure in order to throw that exception, so put a break-point and > check its value before AddRange. Secondly, accessing and manipulating > windows controls from a thread other than the main process thread is fraught > with problems. Because you don't know what the main process thread is doing > at the time, its possible to get race, lock and other such synchronisation > errors. "Invoke" (BeginInvoke, EndInvoke, Invoke) allow you to call a > method on a windows form or control from a thread other than the thread they > were created on. I think under the hood the CLR uses the controls message > pump to ensure the methods are called in an orderly manner. Use Google to > find out more or look in the MSDN literature. Your keywords are: > Invoke+VB.NET :). > > There are many examples around of using Invoke. My preference is to create > a method on the main form, in this case called LogMsg and then invoke that > method from your thread using a delegate. The invoke procedure would look > something like this: > > ' Declare a delegate > > Private Delegate Sub _LogMsgDelegate(astrLogText() as String) > > ' Method to invoke the LogMsg method on the main form. > > Public Sub InvokeLogMsg(ByVal astrLogText() as String) > > Dim Parameters(0) As Object > > Parameters(0) = astrLogText > > ' If the invoke fails, it's usually because the form has been > ' destroyed. The window handle of the form or control must > ' be valid for the invocation to succeed. An unhandled exception > ' on a form or in a control will cause .NET to dispose of it, > ' resulting in any running thread failing when it invokes. > > Try > > frm.Invoke(New LogMsgDelegate(AddressOf frm.LogMsg), Parameters) > > Catch ex As Exception > > ... > > End Try > > End Sub > > and on your form...... > > Public Sub LogMsg(ByVal astrLogText() As String) > > lbxRunLog.BeginUpdate() > lbxRunLog.Items.AddRange(astrLogText) > > ' Make sure the bottom entry is visible > > lbxRunLog.TopIndex = lbxRunLog.Items.Count - 1 > lbxRunLog.EndUpdate() > > Me.lbxRunLog.Invalidate () > > End Sub > > Sorry I didn't test it, but cut and messed around with some code from one of > my projects. Hope this is useful. > > Robin > > > > > I write each line of the log to an array and then pass to the sub > > which is in the form as follows: > > > frm.LogMsg(astrLines) > > > The form code looks like: > > > Public Sub LogMsg(ByVal astrLogText() As String) > > lbxRunLog.BeginUpdate() > > lbxRunLog.Items.AddRange(astrLogText) > > 'Make sure the bottom entry is visible > > lbxRunLog.TopIndex = lbxRunLog.Items.Count - 1 > > lbxRunLog.EndUpdate() > > Me.lbxRunLog.Refresh() > > End Sub > > > I'm interested to hear what you mean by "invoking" - thanks for taking > > the time to look at this. > > > Andrew > > > On May 2, 10:39 pm, "Robin Tucker" <rtgro***@hotmail.co.uk> wrote: > >> Hi hooksie, > > >> I think perhaps the most important piece of code you didn't post there > >> was > >> how you are writing to the list on the form from the thread and what > >> parameters you are sending through. You should ideally be "invoking" any > >> methods on the form, rather than just calling sub-routines on it from > >> your > >> thread. > > >> Robin- Hide quoted text - > > - Show quoted text -
Deleting blanks in a string
Sending Mail Show Msgbox in SYSTEM account Suggestions to learn vb.net Best method to parse this flat file Datasource question Access DB Upgrade Help.ShowPopup for all Textboxes on Form? If else and what ever :( (Newbie) Help with WMI error message - "The object exporter specified was not found" |
|||||||||||||||||||||||