Multiple timers, same handler? MultiThreading, Server requests - vb.net

I am trying to make a program that will test a small server.
The task is to make cyclic readings at some intervals.
For this, I thought to create separate timers (each one with its own interval), but on the Tick event, to have same handler.
Some code:
If testStarted Then
Dim timer As New CustomTimer(tempCyclicReading.ReadInterval)
timer.AssignedNodeId = tempCyclicReading.NodeId
timer.DisplayName = tempCyclicReading.DisplayName
AddHandler timer.Elapsed, AddressOf CyclicReadingTimer_Tick
timer.Enabled = True
cyclicReadingTimers.Add(timer)
End If
Private Sub CyclicReadingTimer_Tick(sender As Object, e As EventArgs)
Try
Dim obj As CustomTimer = TryCast(sender, CustomTimer)
Dim info As DataValue = ServerObj.ReadValue(tEO.Session, obj.AssignedNodeId)
Dim temp As String = ""
temp &= " Cyclic reading: "
temp &= "ItemName: " & obj.DisplayName & ", "
temp &= "Value: " & CStr(info.Value) & ", "
LogInBox(rtbLiveData, Color.ForestGreen, temp)
Catch ex As Exception
Utils.ReadException(ex)
logger.Error("Error when ticking cycling reading timer!" & vbCrLf & Utils.ReadException(ex))
End Try
End Sub
Now I have some questions:
Is it ok to have same handler for all timers? What happens if 2 or timers will call the function ServerObj.ReadValue (which connects to the server and read a value from a node) ? I know that each timer is created in each own thread. BUt when the tick event happens, and the handler function is called, is a new instance (corresponding to each timer) of the handler, created ?
DO I need to provide a lock mechanism or the server will handle this itself?
I also have a LogInBox function, that writes some results in a richtexbox. Again, does the richtextBox have a buffer or a queue and knows to prioritize and server each call to display data?
Thank you so much!

Related

Making my Multi-threaded Task Asynchronous VB.net

So I have a task which loops through a sub as many times as there are people in my array. I know that making this Async will be a lot better, but I am not sure the best way to go about doing this. Here is my code:
Private Sub status()
Dim selectedStreamers As New List(Of String)
For Each item In streamerList.CheckedItems
selectedStreamers.Add(item)
Next
Dim tasks As New List(Of Task)()
For Each streamerName In selectedStreamers
Dim t As Task = Task.Run(Sub()
streamerOnline(streamerName)
Select Case condition
Case StreamersCondition.Streaming
Me.Invoke(Sub() console.Text &= streamerName + " is online." & vbNewLine)
Case StreamersCondition.Offline
Me.Invoke(Sub() console.Text &= streamerName + " is offline." & vbNewLine)
Case StreamersCondition.UserNotFound
Me.Invoke(Sub() console.Text &= streamerName + " hasn't been found." & vbNewLine)
End Select
End Sub)
tasks.Add(t)
Next
Task.WaitAll(tasks.ToArray())
End Sub
Also, nothing related to the question at hand, but if I wanted to constantly check that the streamers were streaming. I would set up a timer, right? Except I can't seem to get this working whilst running this sub? So is there any good way to keep constantly checking (so running this sub) twice per minute?

VB.NET Constantly polling a directory for files is causing an ongoing increase in memory usage

I have a small application that on startup instantiates a class, reads in a JSON array from a text file and builds an object. It then initialises a new system.timers.timer and scans a directory every xx minute(s) to get a file count.
When my application starts up, Windows Task Manager shows it at around 13MB of Memory, however, leaving it to run for about 12 hours now shows it at 700+MB of Memory when no files were even detected in this directory. I'm worried that this is just going to grow forever and eventually crash. It doesn't seem like anything is getting cleaned up by the GC (probably because my timer and objects are always active).
Although this class is quite simple in theory, I do have other functions and timers involved that don't get called until required. I am happy to post all my code somewhere, or even just the class, but it's a little too long to add to this post.
I have included the main elements of my class however:
Public Sub Initialize()
Try
If AgentSettings.DebugMode Then CreateLog("Entered Module: Initialize()")
CreateLog("Initializing Alerting Rule: <" & Me.Name & ">")
CreateLog("Validating all Rule Profiles")
'Loop through all the Rule Profiles and make sure they are all valid
'If not, disable the profile so it never scans
For Each profile As RuleProfile In Me.RuleProfiles
CreateLog("Verifying Profile: <" & profile.Name & ">")
'Check to make sure the Profile is functionally valid
If ProfileIsValid(profile) Then
'Profile is valid so setup an event handler to handle the isBroken event
profile.Active = True
CreateLog("Rule Profile: <" & profile.Name & "> Enabled")
AddHandler profile.BrokenStatusChanged, AddressOf CheckIfRuleIsBroken
Else
'Disable the Profile
CreateLog("Rule Profile: <" & profile.Name & "> Disabled")
profile.Active = False
End If
Next
'Setup the checking timer and set the interval to the default checking time
If Me.ruleCheckTimer Is Nothing Then
Me.ruleCheckTimer = New System.Timers.Timer
Me.ruleCheckTimer.AutoReset = True
Me.ruleCheckTimer.Interval = Me.DefaultMinutesToCheck * 60000
AddHandler Me.ruleCheckTimer.Elapsed, AddressOf CheckRule
End If
'If there is an escalation plan then setup a timer for it
If Me.EscalationPlan.PlanItems.Count > 0 Then
'Setup the escalation timer and set the interval to the first plan item
If Me.escalationTimer Is Nothing Then
Me.escalationTimer = New System.Timers.Timer
Me.escalationTimer.AutoReset = True
AddHandler Me.escalationTimer.Elapsed, AddressOf Escalate
End If
End If
Catch ex As Exception
CreateLog("Module: Initialize()" & vbNewLine & "Exception Error: " & ex.Message)
End Try
End Sub
Private Sub CheckRule(ByVal sender As Object, ByVal e As EventArgs)
Try
If AgentSettings.DebugMode Then CreateLog("Entered Module: CheckRule()")
'Loop through all the profiles
For Each profile As RuleProfile In Me.RuleProfiles
CreateLog("Scanning for active profiles")
'Make sure the Profile is active
If profile.Active Then
Select Case profile.Name
Case "FileCount"
CreateLog("Checking Rule Profile: <" & profile.Name & "> for isBroken status")
'Get file count of the specified directory
Dim files As String()
files = Directory.GetFiles(profile.DirectoryPath)
'Determine if we're checking for files greater than or less than
If profile.GreaterThan Then
If files.Count > profile.FileCount Then
profile.isBroken = True
Else
profile.isBroken = False
End If
Else
If files.Count < profile.FileCount Then
profile.isBroken = True
Else
profile.isBroken = False
End If
End If
files = Nothing
End Select
End If
Next
Catch ex As Exception
CreateLog("Module: CheckRule()" & vbNewLine & "Exception Error: " & ex.Message)
End Try
End Sub
When profile.isBroken receives an update, it triggers an event which then loops through all profiles to determine which ones are broken. If all are broken, then it begins its alerting and escalation process. However, none of this is occurring and my memory is still increasing constantly.
Can anyone see anything wrong with this? Or maybe a better way (and more efficient way) of polling this directory?
The theory behind my application is to instantiate multiple classes to allow various directories or files to be scanned, but if the memory has increased this much with just the one instance, I'm concerned about running multiple.
EDITED
My log handling is as follows:
Public Sub CreateLog(ByVal text As String)
Try
'Create Log Directory if it doesn't exist
If (Not System.IO.Directory.Exists(GetFolderPath(SpecialFolder.CommonApplicationData) & "\MyCompany\Agent\Log")) Then System.IO.Directory.CreateDirectory(GetFolderPath(SpecialFolder.CommonApplicationData) & "\MyCompany\Agent\Log\")
'Write log entry
LogFile = GetFolderPath(Environment.SpecialFolder.CommonApplicationData) & "\MyCompany\Agent\Log\" & "Logfile - " & Format(Now, "ddMMyyyy") & ".txt"
File.AppendAllText(LogFile, DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss") & " " & text & Environment.NewLine)
'Update the activity text on the main UI
frmMain.UpdateActivityText(text)
Catch ex As Exception
'No need to create a log entry
End Try
End Sub

Stop complete process tree on vb.net

I've writte this simple algorithm to stop a complete process tree from vb.net:
Private Sub TerminateProcessTree2(P As Process)
Dim Tree = GenerateProcessTree(P)
For Each childproc As Process In Tree
Try
If childproc.HasExited = False Then childproc.Kill()
Catch ex As Exception
AddError("Could not delete process " & childproc.ProcessName & ". " & ex.Message)
End Try
Next
Dim pName As String = "<unknown>"
Try
If P IsNot Nothing Then
pName = P.ProcessName
If P.HasExited = False Then P.Kill()
End If
Catch ex As Exception
AddError("Error killing process " & pName & ". " & ex.Message)
End Try
End Sub
Function GenerateProcessTree(p As Process) As Collections.Generic.HashSet(Of Process)
Dim hash As New Collections.Generic.HashSet(Of Process)
GenerateProcessTreeNode(p, hash)
Return hash
End Function
Private Sub GenerateProcessTreeNode(parent As Process, hash As Collections.Generic.HashSet(Of Process))
Dim searcher As New ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" & parent.Id)
Dim moc As ManagementObjectCollection = searcher.[Get]()
For Each mo As ManagementObject In moc
Dim i As Integer = CInt(mo("ProcessID"))
Dim childP As Process
Try
childP = Process.GetProcessById(i)
If childP IsNot Nothing AndAlso hash.Contains(childP) = False Then
hash.Add(childP)
GenerateProcessTreeNode(childP, hash)
End If
Catch ex As Exception
AddError("Could not get process ID for " & mo.ToString)
Continue For
End Try
Next
End Sub
But, some of my program users are telling me that, every once in a while (like one or two percent of the times), this algorithm closes ALL processes, and not only child process from the given process. How can this be possible? and does anything need to be fixed from the algorithm? I suppose there are easiest ways to do this, but I want to know why this one fails.
Your code works as expected and is correct. IMO the problem occurs because of the WMI property ParentProcessId. MSDN says:
ParentProcessId
Data type: uint32
Access type: Read-only
Unique identifier of the process that creates a process.
Process identifier numbers are reused, so they only identify
a process for the lifetime of that process. It is possible that
the process identified by ParentProcessId is terminated, so
ParentProcessId may not refer to a running process. It is also
possible that ParentProcessId incorrectly refers to a process
that reuses a process identifier. You can use the CreationDate
property to determine whether the specified parent was created
after the process represented by this Win32_Process instance
was created.
I assume, that your HashSet holds at some point ProcessId's that where replaced by the system with new processes and the new processes and not child processes anymore but are still in the collection and are terminated when fetched from the list.
You could extensively log every call of the process.Kill() (name, process id, timestamp, and so on) and then try to track the problem using the log.

process.start freezing my application(VS 2013)

So I am trying to make an application that starts a 3rd party exe to do some file operations,
based on a list of filenames.
So if the list has 13 items I am going through a loop 13 times, each time starting the external process, notifying the user which file is processed right now, starting the process and waiting for it to exit. To notify the user, another listbox is used as a shoutbox. The problem is, that .waitforexit() somehow freezes the whole thread in a strange way, so that the external program is called nmormaly, tyhe files get proccesed normaly but the main window is frozen until all items are done. So basically the Shoutbox is frozen and gets spammed with all the info only after the whole loop is finished. I've tried numerous ways to implement this, such as starting new threads, using threadpool, timers and whatnot. Any help is appreciated.
code:
Imports System.Windows.Threading
Imports System.Windows.Forms
Imports System.IO
Imports System.Threading
If Listbox2.Items.Count > 0 Then
tabctrl.SelectedIndex = 2
Listbox3.Items.Add(DateTime.Now.ToString & ": Process initiated.")
For i = 0 To Listbox2.Items.Count - 1
Listbox3.Items.Add(DateTime.Now.ToString & ": Processing :" & Listbox1.Items.Item(i))
If System.IO.File.Exists(Listbox2.Items.Item(i)) = False Then
Dim pInfo As New ProcessStartInfo()
With pInfo
.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden
.FileName = System.IO.Directory.GetCurrentDirectory & "\" & "myapp.exe"
.argouments = "w/e"
End With
Dim p As Process = Process.Start(pInfo)
p.WaitForExit()
p.Dispose()
Else
Listbox3.Items.Add(DateTime.Now.ToString & ":! " & Listbox2.Items.Item(i) & " already exists. Moving to next file..")
End If
Next
Listbox3.Items.Add("*-*")
Listbox3.Items.Add(DateTime.Now.ToString & ": Done.")
End If
The problem is that you (at least in the code you posted) are calling WaitForExit() on the UI thread. The UI thread is responsible for redrawing the window, so if you block it, by calling WaitForExit() for example, its not redrawing the ui and the app appears to be frozen.
What you need to do is call it on another thread or on the thread pool, I recommend using Tasks:
Task.Run( Sub()
Dim p As Process = Process.Start(pInfo)
p.WaitForExit()
End Sub)
However, since you're not doing anything with the results of the Process.Start() call, you can also consider not calling WaitForExit() at all.
Since you're using VS2013 you can also use the await operator to wait for the process to finish:
await Task.Run( Sub()
Dim p As Process = Process.Start(pInfo)
p.WaitForExit()
End Sub)
Note that you also have to add the async keyword to the surrounding method as well

VB.NET/WMI - Real-Time Windows Service Monitoring?

So there's an application at my work that installs several Windows services to a server. As a side project, I've been asked to make a simple GUI that will list these services with a "light" (a picture box with a red or green dot) next to the name of each service. The idea is that in the event these services were to stop running, the "light" would change from green to red.
I have the GUI part built, and I can query a remote server's services, then compare it to an array of the ones I'm interested and set the "light" next to each service to green/red depending on the service state. The part I'm hung up on is how to monitor these services in real time? Currently, I just have the following code in the Form_Load event:
Dim myConnectionOptions As New System.Management.ConnectionOptions
With myConnectionOptions
.Impersonation = System.Management.ImpersonationLevel.Impersonate
.Authentication = System.Management.AuthenticationLevel.Packet
End With
Try
Dim myManagementScope As System.Management.ManagementScope
myManagementScope = New System.Management.ManagementScope("\\" & SERVERNAME & "\root\cimv2", myConnectionOptions)
myManagementScope.Connect()
Dim query As New Management.ObjectQuery("SELECT * FROM Win32_Service")
Dim searcher As New Management.ManagementObjectSearcher(myManagementScope, query)
Dim i As Integer = 0
For Each queryObj As Management.ManagementObject In searcher.Get()
For Each service As String In arrServices
If queryObj("DisplayName").Equals(service) Then
If queryObj("State").Equals("Stopped") Then
arrLights(i).Image = My.Resources.redlight
End If
i += 1
End If
Next
Next
Catch err As Management.ManagementException
MessageBox.Show("WMI query failed with the following error: " & err.Message)
Catch unauthorizedErr As System.UnauthorizedAccessException
MessageBox.Show("Authentication error: " & unauthorizedErr.Message)
End Try
Would a simple timer that executes this code repeatedly be the best approach, or is there a more elegant solution? I have a little experience in VB.NET and WMI, but none in any type of real-time monitoring activity like this.
First of all i would put it into a thread, that way even if your connection times out you dont freeze your UI, then i would use a custom wait timer not the built in one as cross threading can be a pain.
wait timer:
Public Sub Wait(ByVal wait_time As Integer)
Dim time As Date
time = Now.AddMilliseconds(wait_time)
Do While time > Now
Application.DoEvents()
Loop
End Sub
example of threading:
Private services_check As Thread
private sub form1_load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
services_check = new thread(AddressOf 'Current code in a public sub')
services_cheack.IsBackground = True
Services_check.start()
It may not be the most elegant solution but its how i would do it, as for your current code im sorry i dont know enough about remote connections to help you.