Process Start and Errors from Slow Applications - vb.net

I made an application that our company uses to launch databases and updates them on the users machine, when needed.
I am having a slight problem when it comes to launching databases and the database starts up slow. When this occurs my application throws an exception, as I assuming its awaiting some kind of response back.
As of now the error thrown is: The system cannot find the file specified
I am trying to prevent this exception logging for cases like this(Slow Application), but still allow the logging if a real error occurs while opening a database.
Current Code I am using:
Private Sub OpenApplication()
If File.Exists(LocalPathString) Then ' File Found. Open the File.
Try
Dim ps As New Process
ps = Process.Start(LocalPathString)
Catch ex As Exception
ex.Source += " | " & LocalPathString
RaiseEvent ShowError(ex)
Finally
RaiseEvent CancelIt() ' Thread Complete. Close the ActionForm
End Try
Else
If LocalPathString = vbNullString Then
RaiseEvent CancelIt() ' No file exits. Cancel thread.
Else
RaiseEvent ShowError(New Exception("Database Not Located: " & LocalPathString))
End If
End If
End Sub
StackTrace:
System.Diagnostics.Process.StartWithShellExecuteEx(startInfo As ProcessStartInfo)
App.exe: N 00912
System.Diagnostics.Process.Start()
App.exe: N 00136
System.Diagnostics.Process.Start(startInfo As ProcessStartInfo)
App.exe: N 00049
SAMi.ActionClass.OpenApplication()
App.exe: N 00117

Maybe I'm missing something, but why don't you simply omit the logging if you found that specific exception?
Catch ex As Exception
ex.Source += " | " & LocalPathString
if not ex.Message.Contains("The system cannot find the file specified") Then
RaiseEvent ShowError(ex)
end if

Related

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

Unhandled Exception in Background Worker

Hi there I am carrying out some integration testing in an application I am developing. The specific element that is causing an issue is a call to a background worker which interrogates an Oracle database. When an error is encountered in the query I want the exception detail to percolate up the call stack to the application level and at that point provide an appropriate user compatible message. In the example test there is a syntax error in the underlying SQL which results in an OraEx Exception:
Oracle.DataAccess.Client.OracleException ORA-00907: missing right parenthesis
Unfortunately the code generates the following exception:
System.Reflection.TargetInvocationException was unhandled
Message: An unhandled exception of type
'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
Additional information: Exception has been thrown by the target of an
invocation.
in the DoWork sub of the backgroundworker, despite my belief that I am handling the exception correctly. Its pretty obvious that I am missing something fundamental here, can someone suggest a solution please.
Thanks in Advance
Paul J.
Here is the code that makes the call to the background worker:
Private Sub EventSearch(ByVal mySQL As String)
Const procName As String = "EventSearch"
Try
_eventMngr = New ScadaEventManager(_CurrentDB, _userName, _myPwd)
_eventMngr.SQL = mySQL
'Set the flag and stop query tool status accordingly
_Stopped = False
uxStopQueryTool.Enabled = True
'activate the timer object to ensure that the execute query menu
'and tool remain disabled until all background processing is complete
uxBackWorkTimer.Enabled = True
_logger.SendLog(Me.Name & "." & procName & " - Scanning for data.", NLog.LogLevel.Trace)
ReviseStatus(2, "Scanning for data. Please wait...", Color.Black, True, True)
'Force the thread to sleep for half a second so the user can see the scanning state taking place
Threading.Thread.Sleep(500)
'Launch the background worker to retrieve the required data from the database
uxBWScan.RunWorkerAsync(_eventMngr)
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Exclamation, My.Application.Info.ProductName)
_logger.SendLog(ex.Message & ". Thrown in module " & Me.Name.ToString & "." & procName, NLog.LogLevel.Error, ex)
Call ResetStatus()
Finally
End Try
End Sub
And here is the code executed by the background worker:
Private Sub uxBWScan_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles uxBWScan.DoWork
Const procName As String = "uxBWScan_DoWork"
Try
e.Argument.CountRecords(_queryTypeID)
e.Result = e.Argument.RecordCount
Catch NullEx As NullReferenceException
_logger.SendLog(NullEx.Message & ". Thrown in module " & Me.Name.ToString & "." & procName, NLog.LogLevel.Error, NullEx)
Throw
Catch OraEx As OracleException
_logger.SendLog(OraEx.Message & ". Thrown in module " & Me.Name.ToString & "." & procName, NLog.LogLevel.Error, OraEx)
Throw
Finally
End Try
End Sub
And here is the low level code that generates the error:
Public Sub CountRecords(ByVal queryType As Integer)
_myDataset = New DataSet
Try
_myDataset = _myScadaEventDB.CountRecords(_sqlText)
If _myDataset.Tables(0).Rows.Count > 0 Then
If queryType = Enums.QueryType.General Or queryType = Enums.QueryType.KeyPerformanceIndicators Or queryType = Enums.QueryType.TelecontroAnalysis Then
_recordCount = _myDataset.Tables(0).Rows(0).Item("RecordCount")
Else
'The query is grouped therefore count the number of records in the table
_recordCount = _myDataset.Tables(0).Rows.Count
End If
Else
_recordCount = 0
End If
Catch ex As Exception
Throw
Finally
End Try
End Sub
Ok, problem solved. Removed the try catch block from DoWork and moved my exception handling into 'RunWorkerCompleted' using e.error. Some reading of the documentation (RTFM...), highlighted the fact that using Try/Catch in the worker thread interferes with the native functionality of the BackgroundWorker. Thanks again everyone for your input.
Paul,

VB.NET directory.EnumerateFiles: Access to path X is denied

Ok so I am trying to write a program to find a specific file on the C drive and get its location. However the following code does not work! I've researched this a lot and gone from GetFiles to Directory.enumerateFiles. However I keep running into an exception claiming that I have no access, simply ignoring the message (closing it/pressing ok) does not continue the search and instead stops it altogether, I need a way of bypassing this if possible, so If a directory causes an exception it will skip it and move along with no error on screen if possible.
Currently the manifest file is set to "requireAdministrator" so I know thats not the issue. Running VB as administrator does not solve, and running the compiled file as admin does not work either. Hope someone can help!
Note: I'm using Visual Basic 2010 Express and have no plans to upgrade to a newever version due to hardware limitation and operating system.
Imports System.IO
Public Class GuardianScanner
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
TextBox1.ReadOnly = True
TextBox1.ScrollBars = ScrollBars.Vertical
Button1.Enabled = False
TextBox1.AppendText(Environment.NewLine & "[INFO] Please wait while scanning. This can take a few minutes")
Try
For Each file As String In Directory.EnumerateFiles("C:\", "GodlyNorris.exe", SearchOption.AllDirectories)
TextBox1.AppendText(Environment.NewLine & "[INFO] Found virus: AUTONORRIS: " & file.ToString)
Next file
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
I tried posting this as a comment but it's pretty messy. This should work, basically it tries to create a directory out of every subdirectory in the C drive, and if it fails with the unauthorized access exception, it moves on to the next subdirectory.
For Each directory In New DirectoryInfo("C:\").GetDirectories()
Try
For Each file In directory.GetFiles("*", SearchOption.AllDirectories)
Try
TextBox1.Text += Environment.NewLine + file.Name
Catch ex As Exception
'MsgBox(ex.Message)
Continue For
End Try
Next
Catch ex As Exception
'MsgBox(ex.Message)
Continue For
End Try
Next

Filestream read only locking PC

I'm trying to read the Windows update log on remote PCs on my LAN. Most of the time I can successfully read the file but at times the program locks up. Likely due to one issue or another - doesn't really matter. What I do need is a way to recover when the Filestream/Streamreader locks up - I'm not sure which is causing the lock. Some streams can set a timeout but the filestream below returns False on a .CanTimeout call.
How can I break out if the stream locks up? (Sometimes the lock is so tight a power off is needed to recover.)
Is there a way to test if the stream will fail before I actually attempt the read?
Is there an alternate way to read a remote log file that another program has open? (I'm using the stream method because the regular File.IO was blocked because the file is open on the remote PC.)
I'm getting closer (I think) with this code. I browed the pathExists code from the referenced post but it was the OP and not an answer.
Imports System.IO
Import System.Threading
...
Function GetAULog(PCName As String) As String
Try
Dim sLogPath As String = String.Format("\\{0}\c$\Windows\SoftwareDistribution\ReportingEvents.log", PCName)
If PCName = My.Computer.Name Then
sLogPath = String.Format("C:\Windows\SoftwareDistribution\ReportingEvents.log", PCName)
End If
' read file open by another process
If Not pathExists(sLogPath) Then
MsgBox("AU log file not found - PC on?")
Return "NA"
End If
Using fs As New FileStream(sLogPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
Using sr As New StreamReader(fs)
Dim s As String = sr.ReadToEnd
Return s
End Using
End Using
Catch ex As Exception
MsgBox(ex.Message)
Return ""
End Try
End Function
Public Function pathExists(path As String) As Boolean
Dim exists As Boolean = True
Dim t As New Thread(New ThreadStart(Sub() exists = System.IO.File.Exists(path)))
t.Start()
Dim completed As Boolean = t.Join(500)
'half a sec of timeout
If Not completed Then
exists = False
t.Abort()
End If
t = Nothing
Return exists
End Function
At least when the PC is off the pathExists() code returns False in short order.
My problem now is the process does not end when the program exits - at least in the IDE, didn't check runtime.
I added t = Nothing but that didn't help. I couldn't figure out the proper Using syntax to test that. How do I properly cleanup after a thread timeout?
I've had the situation with this locking until restart problem. It seems to be caused by the tcpip auto tuning feature. You can cure this issue by running
netsh interface tcp set global autotuninglevel=disable
Run this on both machines if you have access. I tried a few workarounds for this issue with checking locks etc but the only way I could solve it was to disable this. The issue is not really with locking but with something at a lower level in the file sharing protocol.
See this article for more detail
"Final" code shown below. The exceptions are not firing when the timeout occurs so the .Abort was evidently OK.
When the timeout does occur, because the remote PC did not respond, there is a process left hanging which goes away after 30 seconds or so. I notice this when using the IDE, I run the program and test a PC that is off. If I then exit the program the form closes but the IDE hangs for ~30 seconds - I can click Stop-Debugging at this point and it works, but the IDE continues on its own after the ~30 second timeout.
I guess the t = Nothing in the Finally block does not dispose of the thread. t.Dispose does not exists.
So, things are working OK with the exception of the dangling thread that eventually clears itself up. The program is no longer hanging to the point where it cannot not be stopped.
'Imports System.IO
'Imports System.Threading
Public Function pathExists(path As String) As Boolean
' check for file exists on remote PC
Dim exists As Boolean = False
Dim t As New Thread(New ThreadStart(Sub() exists = System.IO.File.Exists(path)))
Try
t.Start()
Dim completed As Boolean = t.Join(500)
'half a sec of timeout
If Not completed Then
exists = False
t.Abort()
End If
Catch ex2 As ThreadInterruptedException
MsgBox("timeout on AU log exists test" & vbNewLine & ex2.Message,, "ThreadInterruptedException")
Catch exAbort As ThreadAbortException
MsgBox("timeout on AU log exists test" & vbNewLine & exAbort.Message,, "ThreadAbortException")
Catch ex As Exception
MsgBox("exception on AU log exists test" & vbNewLine & ex.Message)
Finally
t = Nothing
End Try
Return exists
End Function

How to continue insert if one row fails

I have a while loop where it fetches record from csv and inserts to sql table. Now csv may contain many rows.
What I want is if one row fails just log to a file and continue with next record. I was thinking of try and catch but that will exit the program. So, any suggestions?
while (csv.readnextline)
'assign csv columns to objects
try
'insert to db
Catch ex As Exception
'write to log file
End Try
I need the above code to continue after catching an exception.
Thanks
Try and catch do not exit the program, they just control the flow of the code in case something exceptional happens.
When an exception happens in the try block, the execution continues on the first line of the (corresponding) catch block. After the execution of the catch block, the code continues on the first line after the catch, which in your case could be the End While which will continue the loop.
So an construction like this
While dr.Read
Try
InsertRowIntoDataBase()
Catch ex As Exception
LogErrorToFile(ex)
End Try
End While
should work for you.
However, this is a bad design, as it will generate and log an exception, no matter what the problem is, whether the data is invalid, or the sql server is down, or even if there is an error in your code (e.g. some lurking NullReferenceException). You should limit the handling of exception to a specific case, e.g. to a problem with the database, like this:
While dr.Read
Try
InsertRowIntoDataBase()
Catch ex As SqlClient.SqlException
LogDataBaseErrorToFile(ex)
End Try
End While
Also, if there are known possible problems with the data (e.g. a string in the csv where an integer is expected) it's better to just check that than to use an exception mechanism, something along these lines:
While dr.Read
Try
If Not IsRowValid() Then
LogInvalidDataToFile()
Continue While
End If
InsertRowIntoDataBase()
Catch ex As SqlClient.SqlException
LogDataBaseErrorToFile()
Catch ex As Exception
LogGenericErrorToFile()
End Try
End While
no it won't exit the program, depending on how/where you handle the exception. If you do something like :
Dim WrongValuedLinesList As New List(Of String)
Dim ConversionFailedList As New List(Of String)
Dim InsertionFailedList As New List(Of String)
Dim NumberOfInsertedLines As integer = 0
For Each (CurrentLine in my csv)
' 1. line processing
Try
' (process my line : split, convert, check range...)
If (I know the insertion will fail) Then
' (Store information about that wrong line, in List, log, or do nothing)
WrongValuedLinesList.Add(" This line : " & CurrentLine
& " has wrong values because...
Continue For
End If
Catch ex as exception
' (here handle the line conversion failed : store in list, or log, or do nothing ...)
' for expl :
ConversionFailedList.Add(" Conversion failed for line " & CurrentLine
& " exception details : " & ex.message " )
End Try
' 2. Line insertion
Try
'(insert my processed data into database)
NumberOfInsertedLines +=1
Catch ex as exception
' (here handle the insertion failed exception (expl : primary key might not be unique)
' : store in list, log, do nothing...)
' for example :
InsertionFailedList.Add(" Insertion failed for line " & CurrentLine
& " exception details : " & ex.message " )
End Try
Next
(Here you might wanna report how things went to your user using
your error list.)