A few weeks ago I wrote a wrapper for the ServiceController control to enhance and streamline the base ServiceController. One of the changes I made was to add a monitoring component using the System.Threading.Timer object. On any change of status, an event is raised to the parent class. The actual monitoring works fine, but when the event is handled in the main form, my program abruptly ends - no exceptions, no warning, it just quits. Here's a skeleton version of the control:
Public Class EnhancedServiceController
Inherits ServiceController
Public Event Stopped(ByVal sender As Object, ByVal e As System.EventArgs)
Public Event Started(ByVal sender As Object, ByVal e As System.EventArgs)
Private _LastStatus As System.ServiceProcess.ServiceControllerStatus
Private serviceCheckTimer as System.Threading.Timer
Private serviceCheckTimerDelegate as System.Threading.TimerCallback
...
Private Sub StartMonitor()
MyBase.Refresh()
_LastStatus = MyBase.Status
serviceCheckTimerDelegate = New System.Threading.TimerCallback(AddressOf CheckStatus)
serviceCheckTimer = New System.Threading.Timer(serviceCheckTimerDelegate, Nothing, 0, 60*1000)
End Sub
Private Sub CheckStatus()
MyBase.Refresh()
Dim s As Integer = MyBase.Status
Select Case s
Case ServiceControllerStatus.Stopped
If Not s = _LastStatus Then
RaiseEvent Stopped(Me, New System.EventArgs)
End If
Case ServiceControllerStatus.Running
If Not s = _LastStatus Then
RaiseEvent Started(Me, New System.EventArgs)
End If
End Select
_LastStatus = s
End Sub
End Class
And the form:
Public Class Form1
Private Sub ServiceStarted(ByVal sender As Object, ByVal e As System.EventArgs) Handles ESC.Started
Me.TextBox1.Text = "STARTED"
End Sub
Private Sub ServiceStopped(ByVal sender As Object, ByVal e As System.EventArgs) Handles ESC.Stopped
Me.TextBox1.Text = "STOPPED"
End Sub
End Class
If I had to guess, I'd say that there's some sort of thread problem, but I'm not sure how to handle that in the form. Any ideas?
IF it is a threading issue then you are probably trying to update the UI from a non-UI thread.
So something like this should solve that...
Private Delegate Sub UpdateTextBoxDelegate(byval tText as String)
Private Sub UpdateTextBox(byval tText As String)
If Me.InvokeRequired Then
Me.Invoke(New UpdateTextBoxDelegate(AddressOf UpdateTextBox), tText)
Exit Sub
End If
TextBox1.Text = tText
End Sub
Private Sub ServiceStarted(ByVal sender As Object, ByVal e As System.EventArgs) Handles ESC.Started
UpdateTextBox ("STARTED")
End Sub
Private Sub ServiceStopped(ByVal sender As Object, ByVal e As System.EventArgs) Handles ESC.Stopped
UpdateTextBox("STOPPED")
End Sub
Related
I'm facing a big problem here, I'm developing a app to read data from a Weight.
Everything is working perfectly, but the result is not what I expected: when reading the data from the scale, it keeps printing the data without stopping and I would like it to read a single line and whenever there is any change in the scale, just change the value and not add a new line...
The way it's printed:
My code:
Public Class Form1
Dim Q As Queue(Of String) = New Queue(Of String)
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
For Each s In System.IO.Ports.SerialPort.GetPortNames()
ComboBox1.Items.Add(s)
Next s
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
If ComboBox1.SelectedIndex = -1 Then
MsgBox("Please select a port")
Exit Sub
Else
SerialPort1.BaudRate = 9600
SerialPort1.DataBits = 8
SerialPort1.Parity = IO.Ports.Parity.None
SerialPort1.StopBits = IO.Ports.StopBits.One
SerialPort1.PortName = ComboBox1.SelectedItem.ToString
SerialPort1.Open()
Timer1.Start()
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, _
ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
Handles SerialPort1.DataReceived
Q.Enqueue(SerialPort1.ReadExisting())
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Timer1.Tick
SyncLock Q
While Q.Count > 0
TextBox1.Text &= Q.Dequeue
End While
End SyncLock
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
SerialPort1.Close()
Timer1.Stop()
End Sub
End Class
Here's a simple example using Control.BeginInvoke() to set a Control's property with some data coming from a secondary Thread.
You can use this method since you have all your code in a Form and you use
a SerialPort Component.
Otherwise, build a class to handle the SerialPort and pass a delegate to its Constructor, capture SynchronizationContext.Current and Post() to the delegate.
Or use an IProgress<T> delegate (Progress(Of String) here) and pass this delegate to the class, then just call its Report() method from the event handler.
Assuming ReadExisting() works for you and the Encoding is set correctly (it appears to be, from the results shown in the OP), you could change the code in the DataReceived handler to:
It's required to check whether the handles of the Controls involved (the Form, mainly) are created before calling Invoke() / BeginInvoke(), otherwise you may (will, at some point) get an exception that may kill the application (the IProgress<T> pattern is preferable).
Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
Dim comPort = DirectCast(sender, SerialPort)
UpdateUI(comPort.ReadExisting())
End Sub
' [...]
Private lastWeight As String = String.Empty
Private Sub UpdateUI(weight As String)
If IsHandleCreated Then
BeginInvoke(New Action(
Sub()
If Not lastWeight.Equals(weight) Then
lastWeight = weight
If TextBox1.IsHandleCreated Then TextBox1.Text = lastWeight
End If
End Sub))
End If
End Sub
Make a test with a threaded Timer, setting the Interval to a low value (e.g., 50ms).
You can also try to close the Form without stopping the Timer.
Found the soluction:
Public Class Form5
Delegate Sub SetTextCallback(ByVal data As String)
Private Delegate Sub UpdateLabelDelegate(theText As String)
Private Sub UpdateLabel(theText As String)
If Me.InvokeRequired Then
Me.Invoke(New UpdateLabelDelegate(AddressOf UpdateLabel), theText)
Else
TextBox1.Text = theText
End If
End Sub
Private Sub SerialPort1_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
Dim returnStr As String
returnStr = SerialPort1.ReadExisting
Me.BeginInvoke(Sub()
UpdateLabel(returnStr)
End Sub)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
SerialPort1.Open()
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
SerialPort1.Close()
End Sub
End Class
I would like to know how I should code my VB.net application to react to the form load event in a third-party application (also written in VB.net)
To test, I have created two basic programs, one with two forms (program A) and one (program B) that attempts to the listen to program A's appropriate form load event. I have tried using WithEvents but it does not get fired when Program's A second form loads.
Here is the code for Program A:
Public Class StartPage
Public WithEvents loadtimer As New System.Windows.Forms.Timer
Private Sub StartPage_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
loadtimer.Interval = 1000
loadtimer.Enabled = True
loadtimer.Start()
End Sub
Private Sub loadtimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles loadtimer.Tick
loadtimer.Stop()
SystemStatus.Show()
End Sub
End Class
Public Class SystemStatus
Inherits StartPage
Private Sub StartPage_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Me.Label1.Text = "This is the form that I want to listen for the load event"
Me.loadtimer.Enabled = False
End Sub
End Class
Here is the code for Program B:
Imports Program_A
Public Class ListeningForm
Dim WithEvents testlisten As New Program_A.SystemStatus
Private Sub testlisten_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles testlisten.Load
Label1.Text = "SystemStatus form loaded"
End Sub
Private Sub ListeningForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Label1.Text = "Waiting for SystemStatus load event..."
End Sub
End Class
I am quite new when it comes to programming so maybe this is not even possible or I just haven't been understanding what I've been reading. In any case please enlighten me as to what my next course of action should be.
Thanks very much in advance,
theoleric
This will do what your asking.
Imports Program_A
Public Class ListeningForm
Dim WithEvents testlisten As New SystemStatus
Private Sub testlisten_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles testlisten.Load
' now this event will fire
Label1.Text = "SystemStatus form loaded"
End Sub
Private Sub ListeningForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Label1.Text = "Waiting for SystemStatus load event..."
' the load event will not fire until you call this
testlisten.Show()
End Sub
End Class
why does the FileSystemWatcher fire twice? Is there an easy way to fix it? Surely if I update or edit the text file it should only fire once?
this link here http://weblogs.asp.net/ashben/archive/2003/10/14/31773.aspx says
Events being raised twice - An event will be raised twice if an event handler (AddHander FSW.Created, AddressOf FSW_Created) is
explicitly specified. This is because, by default, the public events
automatically call the respective protected methods (OnChanged,
OnCreated, OnDeleted, OnRenamed). To correct this problem, simply
remove the explicit event handler (AddHandler ...).
What does "remove the explicit event handler" mean?
Imports System.IO
Public Class Form2
Private Sub FileSystemWatcher1_Changed(ByVal sender As System.Object, ByVal e As System.IO.FileSystemEventArgs) Handles FileSystemWatcher1.Changed
'this fires twice
MessageBox.Show("test")
End Sub
Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
FileSystemWatcher1.Path = "C:\Users\c\Desktop\test\"
FileSystemWatcher1.NotifyFilter = NotifyFilters.LastAccess Or NotifyFilters.LastWrite Or NotifyFilters.FileName Or NotifyFilters.DirectoryName Or NotifyFilters.CreationTime
FileSystemWatcher1.IncludeSubdirectories = False
FileSystemWatcher1.Filter = "text.txt"
End Sub
End Class
Update:
I have come up with 2 solutions. One uses Threads, and the other doesn't. Take your pick :-).
Without threading:
Imports System.IO
Public Class Form1
Private Sub FileSystemWatcher1_Changed(ByVal sender As System.Object, ByVal e As System.IO.FileSystemEventArgs) Handles FileSystemWatcher1.Changed
Dim watcher As System.IO.FileSystemWatcher = sender
watcher.EnableRaisingEvents = False
'Do work here while new events are not being raised.
MessageBox.Show("Test")
watcher.EnableRaisingEvents = True 'Now we can begin watching for new events.
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
FileSystemWatcher1.Path = "C:\Users\c\Desktop\test"
FileSystemWatcher1.NotifyFilter = NotifyFilters.LastWrite
FileSystemWatcher1.IncludeSubdirectories = False
FileSystemWatcher1.Filter = "test.txt"
End Sub
Private Sub FileSystemWatcher_OnChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)
End Sub
End Class
This solution (without threading), sets the watcher.EnableRaisingEvents to False. It is after this point where you would normally process whatever files are affected (or changed). It then sets the EnableRaisingEvents back to True after your work is done.
With threading:
Imports System.IO
Public Class Form1
Private Sub FileSystemWatcher1_Changed(ByVal sender As System.Object, ByVal e As System.IO.FileSystemEventArgs) Handles FileSystemWatcher1.Changed
FileSystemWatcher1.EnableRaisingEvents = False
Threading.Thread.Sleep(250)
FileSystemWatcher1.EnableRaisingEvents = True
MessageBox.Show("test")
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
FileSystemWatcher1.Path = "C:\Users\c\Desktop\test"
FileSystemWatcher1.NotifyFilter = NotifyFilters.LastWrite
FileSystemWatcher1.IncludeSubdirectories = False
FileSystemWatcher1.Filter = "test.txt"
End Sub
Private Sub FileSystemWatcher_OnChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)
End Sub
End Class
This solution, although a bit hacky, does work. It disables checking for new changes/events for 250ms and then re-enables checking, based on the assumption that you won't been needing to check for a change every 250ms. I have tried almost everything that I could think of to get a real solution for you but this will work in the meantime.
Check e.ChangeType. I imagine you're getting two different notifications. Perhaps LastAccess and LastModified. In which case, that's the expected behavior.
Today i crashed in FileSystemWatcher and found this site. Suggested Thread.Sleep cannot completely eliminate problem. Tested with fast counter-integer. And is blocking UI. Most problematic is startup, it sliped trough at 5s. Then i set FileSystemWatcher1.EnableRaisingEvents = False immediately in TimerWatcherChanged.Tick and never enabled again... But surprisingly counter still catch up-to 4 events! I would like to share my solution, non-blocking, with adjustable Timer. Feedback is welcome.
Imports System.IO
Imports System.Diagnostics
Public Class Form1
Dim fileName As String
Dim Fsw_counter As Integer
WithEvents TimerWatcherChanged As New Windows.Forms.Timer
WithEvents TimerTest As New Windows.Forms.Timer
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TimerWatcherChanged.Interval = 100
TimerTest.Interval = 100 : TimerTest.Start()
TextBox1.Text = "C:\Downloads\New Text Document.txt"
TextBox1.SelectionStart = TextBox1.Text.Length
WatcherSetup()
End Sub
Sub WatcherSetup()
fileName = TextBox1.Text
FileSystemWatcher1.IncludeSubdirectories = False
FileSystemWatcher1.Path = Path.GetDirectoryName(fileName)
FileSystemWatcher1.Filter = Path.GetFileName(fileName)
FileSystemWatcher1.NotifyFilter = NotifyFilters.LastWrite
FileSystemWatcher1.EnableRaisingEvents = True
End Sub
Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
WatcherSetup()
End Sub
Private Sub FileSystemWatcher1_Changed(sender As Object, e As FileSystemEventArgs) Handles FileSystemWatcher1.Changed
If TimerWatcherChanged.Enabled = False Then
TimerWatcherChanged.Enabled = True
Fsw_counter += 1
' ***** Your WATCH Code put here... *****
End If
End Sub
Private Sub TimerWatcherChanged_Tick(sender As Object, e As EventArgs) Handles TimerWatcherChanged.Tick
TimerWatcherChanged.Enabled = False
End Sub
Private Sub TimerTest_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles TimerTest.Tick
TextBox2.Text = "Changed: " & Fsw_counter
If TimerWatcherChanged.Enabled = True Then
TextBox2.BackColor = Color.Red
Else
TextBox2.BackColor = Color.LawnGreen
End If
End Sub
End Class
I'm looking for a way to detect if the user has been idle for 5 min then do something, and if and when he comes back that thing will stop, for example a timer.
This is what i have tried (but this will only detect if form1 has been inactive / not clicked or anything):
Public Class Form1
Private Sub form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'You should have already set the interval in the designer...
Timer1.Start()
End Sub
Private Sub form1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress
Timer1.Stop()
Timer1.Start()
End Sub
Private Sub form1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
Timer1.Stop()
Timer1.Start()
End Sub
Private Sub form1_MouseClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseClick
Timer1.Stop()
Timer1.Start()
End Sub
Private Sub Timer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick
MsgBox("Been idle for to long") 'I just have the program exiting, though you could have it do whatever you want.
End Sub
End Class
This is done easiest by implementing the IMessageFilter interface in your main form. It lets you sniff at input messages before they are dispatched. Restart a timer when you see the user operating the mouse or keyboard.
Drop a timer on the main form and set the Interval property to the timeout. Start with 2000 so you can see it work. Then make the code in your main form look like this:
Public Class Form1
Implements IMessageFilter
Public Sub New()
InitializeComponent()
Application.AddMessageFilter(Me)
Timer1.Enabled = True
End Sub
Public Function PreFilterMessage(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage
'' Retrigger timer on keyboard and mouse messages
If (m.Msg >= &H100 And m.Msg <= &H109) Or (m.Msg >= &H200 And m.Msg <= &H20E) Then
Timer1.Stop()
Timer1.Start()
End If
End Function
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Timer1.Stop()
MessageBox.Show("Time is up!")
End Sub
End Class
You may have to add code that disables the timer temporarily if you display any modal dialogs that are not implemented in .NET code.
This might work by setting it to just call the Reset idk i just want it work all over the program idk how to do it, i just created this code :
Public Class test
Dim IdleTimer As String
Dim testsave As String
Dim idle_TimerSet As String = 60 '<---- Here You choose The timer (1 per Sec)
Private Sub test_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
IdleTimer = idle_TimerSet
End Sub
Private Sub Idle_Tick(sender As Object, e As EventArgs) Handles Idle.Tick
If IdleTimer <= 1 Then
MsgBox("[ Idle Screen ]")
Else
IdleTimer = IdleTimer - 1 '<--- The Counter
IdleTracker.Text = IdleTimer
End If
End Sub
Private Sub test_MouseDown(sender As Object, e As MouseEventArgs) Handles Me.MouseDown
Call Reset_Idle() 'This is on the main Form
End Sub
Private Sub test_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
Call Reset_Idle() 'This is on the main Form
End Sub
Public Sub Reset_Idle() '<-- The Reset Action
'Idle.Enabled = False
IdleTimer = idle_TimerSet
'Idle.Enabled = True
End Sub
End Class
Hey all i am trying to figure out why my 2nd form is not displaying the value i recived in my first form.
The code for the first form is:
Private Sub scannerOnCom_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
responceBack = scannerOnCom.ReadLine
Call frm1110.clickButton(responceBack)
End Sub
The second form code is this:
Public Sub clickButton(ByRef theResponse As String)
txtNumber.Text = theResponse
'Call cmdNextFinish_Click(Nothing, Nothing)
End Sub
However, when i debug it to make sure there is something stored for theResponse, there is but for some reason it does not put it into the textbox. It's blank.
Any help would be great!
David
UPDATE
Ok so Form1:
Dim tmpForm3020 As New frm3020
Private Sub cmd3020_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd3020.Click
tmpForm3020.Show()
Me.WindowState = FormWindowState.Minimized
End Sub
Private Sub scannerOnCom_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
responceBack = scannerOnCom.ReadLine
tmpForm3020.txtNumber.Text = responceBack
End Sub
If thats correct then i get an error on line:
xForm.txtNumber.Text = responceBack
Saying:
Cross-thread operation not valid: Control 'txtNumber' accessed from a thread other than the thread it was created on.
Are you explicitly creating an instance of your second form, or relying on the default instance? I.e. is "frm1110" the second form's class name, or an instance that you have new'd up? Make sure in either case that it is the same instance that is actually being displayed.
Dim tmpForm3020 As New frm3020
Private Sub cmd3020_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmd3020.Click
tmpForm3020.Show()
Me.WindowState = FormWindowState.Minimized
End Sub
Private Sub scannerOnCom_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
responceBack = scannerOnCom.ReadLine
TestData(responceBack)
End Sub
Private Sub TestData(ByVal xVal As String)
If InvokeRequired Then
Me.Invoke(New MethodInvoker(AddressOf TestData))
' change Me to tmpForm3020 (if it does not work)
' tmpForm3020.Invoke(New MethodInvoker(AddressOf TestData))
Else
tmpForm3020.txtNumber.Text = xVal
End If
End Sub