get the complete error message from ErrorDataReceived event - vb.net

I have written a process monitoring tool that restarts an application in some error scenarios. I also want to catch unhandled .NET errors and this works fine with following code:
ExterneAnwendung = New System.Diagnostics.Process()
ExterneAnwendung.Start()
ProcessName = ExterneAnwendung.ProcessName
AddHandler ExterneAnwendung.ErrorDataReceived, AddressOf ErrorDataHandler
ExterneAnwendung.BeginErrorReadLine()
Private Sub ErrorDataHandler(sendingProcess As Object, errLine As DataReceivedEventArgs)
If Not String.IsNullOrEmpty(errLine.Data) Then
errFromApp = errLine.Data
End If
End Sub
If Not errFromApp Is Nothing Then
Log("Die Anwendung " & ExterneAnwendung.StartInfo.FileName & " hat einen Fehler gemeldet: " + errFromApp)
ExterneAnwendung.CloseMainWindow()
End If
Now it gives me errors like this:
Die Anwendung C:\BEA\NIF\NIF-Funkstelle.exe hat einen Fehler gemeldet:
at System.Threading.Thread.StartCallback()
However, the whole stack trace would be much more useful. Its like this:
Application: NIF-Funkstelle.exe CoreCLR Version: 6.0.21.52210 .NET
Version: 6.0.0 Description: The process was terminated due to an
unhandled exception. Exception Info: System.IO.IOException: Unable to
write data to the transport connection: Eine bestehende Verbindung
wurde softwaregesteuert durch den Hostcomputer abgebrochen.. --->
System.Net.Sockets.SocketException (10053): Eine bestehende Verbindung
wurde softwaregesteuert durch den Hostcomputer abgebrochen. at
System.Net.Sockets.NetworkStream.Write(ReadOnlySpan1 buffer) --- End of inner exception stack trace --- at System.Net.Sockets.NetworkStream.Write(ReadOnlySpan1 buffer) at
MizuVOIP.Mizu_TCP_Handler.writeLine(String dataTX) at
MizuVOIP.SIPstack.register(Boolean log) at
MizuVOIP.SIPstack.controlThread(Object obj) at
System.Threading.QueueUserWorkItemCallbackDefaultContext.Execute()
at System.Threading.ThreadPoolWorkQueue.Dispatch() at
System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
at System.Threading.Thread.StartCallback()
Now my final question:
Is there any way to get the stack trace with the DataReceivedEventArgs?

Related

How to understand the cast exception in automating Word from a VB code in Visual Studio?

I am developing a form application in the German Visual Studio 2019 that should automate Word using VB. In my VB project, I added a reference to the Microsoft.Office.Interop.Word.dll and can compile the application. To debug the application in VS, I use the code:
Dim oWord As Microsoft.Office.Interop.Word.Application
oWord = CreateObject("Word.Application")
oWord.Visible = True
This trivial code should instantiate and show Word. However, I get the exception instead that Microsoft.Office.Interop.Word.ApplicationClass cannot be cast in Microsoft.Office.Interop.Word._Application
System.InvalidCastException: Das COM-Objekt des Typs "Microsoft.Office.Interop.Word.ApplicationClass" kann nicht in den Schnittstellentyp "Microsoft.Office.Interop.Word._Application" umgewandelt werden. Dieser Vorgang konnte nicht durchgeführt werden, da der QueryInterface-Aufruf an die COM-Komponente für die Schnittstelle mit der IID "{00020970-0000-0000-C000-000000000046}" aufgrund des folgenden Fehlers nicht durchgeführt werden konnte: Fehler beim Laden der Typbibliothek/DLL. (Ausnahme von HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY))
As far as I understood, the reason is not quite clear. Some assume that the reason is installation of several Word versions that results in versions' incompatibility and the exception. To fix the problem, they suggest to delete double {00020970-0000-0000-C000-000000000046} items in the Windows register. I tried it, but the problem remains.
How do I fix the problem or does somebody has a similar sample automation project in VB (VB# or VC++) I can experiment with?
At the top of the file...
Imports Microsoft.Office.Interop
Then create the object with the New keyword which calls the constructor.
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim oWord As New Word.Application
oWord.Visible = True
End Sub
The reason of this exception was Windows with a corrupt registry.

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,

When I try to divert standard output with System.Diagnostics.Process, the called program gives an error

To summarise the following: If I use Process to call WinZip, WinZip performs its task correctly but I lose the console output. If I divert the standard output, I get that output but WinZip fails to create a zip file and reports “FATAL ERROR: win32/windows system error (print.cpp#315): The handle is invalid.” Can anyone identify my error?
I have a security system written in Excel VBA. It identifies files to archive and uses batch files to call the command line interface of WinZip and maintains an index within an Excel worksheet. This system has served me well for many years but it had some deficiencies that I believed would be solved by recoding with VB.Net.
The VB.Net version was more of a port than a recode and I maintained the use of batch files. The new versions solved most of the deficiencies with the VBA version but one remained.
Within a batch file, a file name must be strictly ASCII to avoid problems. Most of my file names are ASCII but a few are not, for example: “009 Ålesund - Art Deco tower.jpg”. This is a photograph from a holiday in Norway where I have included the name of the town in the file name. Within batch files, hexadecimal C5 is sometimes treated as Unicode (display value Å) and sometimes as code page 437 (display value ┼). I suspect what is passed to a program is the UTF-8 code for hexadecimal C5 split into two bytes to give C3 85. Whatever is happening, no program called via a batch file can find file “009 Ålesund - Art Deco tower.jpg”.
I avoided this problem with the VBA solution by checking filenames for non-ASCII characters. If found, the processing would be of the form:
VBA macro renames file “009 Ålesund - Art Deco tower.jpg” as “009 $C5lesund - Art Deco tower.jpg”.
Batch file calls WinZip to zip file “009 $C5lesund - Art Deco tower.jpg”.
VBA macro renames file “009 $C5lesund - Art Deco tower.jpg” as “009 Ålesund - Art Deco tower.jpg”.
VBA macro records both the correct name and the name under which it had been archived in the index.
Although this was messy, it worked and was the best I could think of.
I have implemented the same approach with VB.Net but I have been looking for a better alternative.
I use Shell to execute the batch file so I first investigated using Shell to call WinZip directly rather than via a batch file. WinZip outputs progress and error information to the console which I redirect to text file “zip.txt” using ZipCommand > zip.txt. I thoroughly check for possible error conditions prior to creating the batch file so I rarely get error messages but I would prefer not to give up access to WinZip’s console output. I cannot discover any method of diverting console output using Shell.
I then tried Process which appears to offer the functionality I seek but I am encountering an error.
The code below was intended as proof-of-concept. Within it there are three statements that are currently commented out:
'1 .RedirectStandardOutput = True
'2 .UseShellExecute = False
'3 zipOutput = proc.StandardOutput.ReadToEnd()
If I run the code as is with the file to be zipped missing, the process finishes with exit code 12. WinZip’s error codes are not published and its documentation claims only exit code 0 meaning “no errors” is reliable.
If I run the code as is with the file to be zipped present, the process finishes with exit code 0. The zip file is formed correctly.
I get appropriate exception errors if statement 3 is enabled by removing “'3” or if both statements 1 and 3 are enabled. However, if I run the code with all three statements enabled I get exit code 254 and the zip file is not created. The diverted console output is:
WinZip(R) Command Line Support Add-On Version 4.0 32-bit (Build 10562)
Copyright (c) 1991-2013 WinZip International LLC - All Rights Reserved
using encryption AES-256
FATAL ERROR: win32/windows system error (print.cpp#315): The handle is invalid.
Program is terminating!
Please send the file wzCLine.rpt to the address below.
To help solve this problem, please include as detailed as possible
a description of what you were doing before the problem occurred,
so we can try to reproduce the problem here.
WinZip Computing
EMail: support#winzip.com
Web: http://www.winzip.com
The file “wzCLine.rpt” contains:
Please send the file wzCLine.rpt to the address below.
To help solve this problem, please include as detailed as possible
a description of what you were doing before the problem occurred,
so we can try to reproduce the problem here.
WinZip Computing
EMail: support#winzip.com
Web: http://www.winzip.com
FATAL ERROR: win32/windows system error (print.cpp#315): The handle is invalid.
Output_context_info: 528d4140
Return address = 0000000a
Windows NT 6.0 build 6002 Service Pack 2
Current date/time: 01/02/2016 14:56
WinZip(R) Command Line Support Add-On (10562cl)
Build 10562
Module name = c:\Program files\WinZip\wzzip.exe
Command line: <"c:\Program files\WinZip\wzzip.exe" -a -s"12345678" -ycAES256 -whs "C:\Secure\Temp\test.zip" "C:\Secure\Temp\009 Ålesund - Art Deco tower.jpg">
Memory in use = 61%
Total physical memory = 2097151 Kbytes
Physical memory available = 1199000 Kbytes
Total virtual memory = 2097024 Kbytes
Virtual memory available = 2035664 Kbytes
Country code: 44 Language: English Code-page: 1252
[eof]
I will probably send a report to WinZip.com but I assume the cause is an error in my use of Process and not an error within WinZip.
Any advice on how to avoid the error and/or improve my code will be gratefully received.
Public Class Form1
Private Sub btnExit_Click(sender As System.Object, e As System.EventArgs) _
Handles btnExit.Click
Me.Close()
End Sub
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) _
Handles MyBase.Load
' Process, ProcessStartInfo and ProcessWindowStyle within namespace System.Diagnostics
' Threading within namespace System
Dim proc As Process = Nothing
Dim procInfo As New ProcessStartInfo
Dim zipOutput As String = ""
Dim zipArguments As String = "-a -s""12345678"" -ycAES256 -whs ""C:\Secure\Temp\test.zip""" & _
" ""C:\Secure\Temp\009 Ålesund - Art Deco tower.jpg"""
With procInfo
.Arguments = zipArguments
.FileName = "c:\Program files\WinZip\wzzip.exe"
'1 .RedirectStandardOutput = True
'2 .UseShellExecute = False
.WindowStyle = ProcessWindowStyle.Minimized
End With
Dim endTime = DateAdd(DateInterval.Second, 30, Now())
Dim finished As Boolean = False
Try
proc = Process.Start(procInfo)
Catch ex As Exception
lblMsg.Text = "Unable to start process: " & ex.Message
Exit Sub
End Try
If proc.HasExited Then
' The started process may have activated an existing instance of itself and then exited.
Debug.Assert(False)
End If
' Pause until process has exited or timed out
Do While True
'3 zipOutput = proc.StandardOutput.ReadToEnd()
Debug.Print("Sleep")
Threading.Thread.Sleep(500)
If proc.HasExited Then
finished = True
Exit Do
End If
If Now() > endTime Then
Exit Do
End If
Loop
If finished Then
lblMsg.Text = "Process finished with exit code " & proc.ExitCode
Else
Try
proc.Kill()
lblMsg.Text = "Process did not terminate so I killed it"
Catch ex As Exception
lblMsg.Text = "Process did not terminate and my attempt to kill it failed."
End Try
End If
lblMsg.Text &= vbLf & zipOutput
Debug.Print(lblMsg.Text)
End Sub
End Class
Figured it out. Try this.
' Pause until process has exited or timed out
zipOutput = proc.StandardOutput.ReadToEnd()
proc.WaitForExit()
lblMsg.Text = "Process finished with exit code " & proc.ExitCode
lblMsg.Text &= vbLf & zipOutput
I'm not sure exactly why you're getting the invalid handle error, but it might be something to do with this excerpt from msdn here. I'm guessing that adding the Proc.WaitForExit makes your code wait until the previous zipOutput = proc.StandardOutput.ReadToEnd() has finished working.
The redirected StandardOutput stream can be read synchronously or
asynchronously. Methods such as Read, ReadLine, and ReadToEnd perform
synchronous read operations on the output stream of the process. These
synchronous read operations do not complete until the associated
Process writes to its StandardOutput stream, or closes the stream.
Full Code Here -
Public Class Form1
Private Sub btnExit_Click(sender As System.Object, e As System.EventArgs) _
Handles btnExit.Click
Me.Close()
End Sub
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) _
Handles MyBase.Load
' Process, ProcessStartInfo and ProcessWindowStyle within namespace System.Diagnostics
' Threading within namespace System
Dim proc As Process = Nothing
Dim procInfo As New ProcessStartInfo
Dim zipOutput As String = ""
Dim zipArguments As String = "-a -s""12345678"" -ycAES256 -whs ""C:\Secure\Temp\test.zip""" &
" ""C:\Secure\Temp\009 Ålesund - Art Deco tower.jpg"""
With procInfo
.Arguments = zipArguments
.FileName = "c:\Program files\WinZip\wzzip.exe"
.RedirectStandardOutput = True
.UseShellExecute = False
.WindowStyle = ProcessWindowStyle.Minimized
End With
Dim endTime = DateAdd(DateInterval.Second, 30, Now())
Dim finished As Boolean = False
Try
proc = Process.Start(procInfo)
Catch ex As Exception
lblMsg.Text = "Unable to start process: " & ex.Message
Exit Sub
End Try
If proc.HasExited Then
' The started process may have activated an existing instance of itself and then exited.
Debug.Assert(False)
End If
' Pause until process has exited or timed out
zipOutput = proc.StandardOutput.ReadToEnd()
proc.WaitForExit()
lblMsg.Text = "Process finished with exit code " & proc.ExitCode
lblMsg.Text &= vbLf & zipOutput
Debug.Print(lblMsg.Text)
End Sub
End Class

Process Start and Errors from Slow Applications

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

Handling an exception thrown from inside a dll

I'm working on a project for school in which a dll is loaded.
The dll that is loaded is a bridge between my program and the Twincat System Manager which forms the bridge between the computer and a PLC via the local network.
I need to read Variables trough this whole chain from the plc to my program.
This is the way I do this:
Public Function adsReadReal(ByVal Variabelenaam As String) As Single
Dim ds = New TwinCAT.Ads.AdsStream(4 * 8) ' new data stream
Dim br = New System.IO.BinaryReader(ds) 'new binary
Dim hVar = New Integer
Try
ConState(1)
tcclient = New TcAdsClient
ConState(2)
tcclient.Connect(Form1.amsAdress, 801) 'connects the tcclient to the PLC
hVar = tcclient.CreateVariableHandle(Variabelenaam) 'creats a handle for the variable
tcclient.Read(hVar, ds) 'read it
ConState(5)
Return br.ReadSingle() 'convert it from binary to readable for vb
Catch ex As Exception
ConState(0)
PrintEx(ex) 'print out the exception
finally
tcclient.Dispose() 'make the object stop being used to prevent a lingering connection
End Try
Return False
End Function
Now the program loads a dll called TwinCAT.ADS.dll at the start of the connection module. If the Twincat system manager is running the program ends normally, but when it is not it crashes and gives me this error:
System.DllNotFoundException was unhandled
Message="Kan DLL tcadsdll.dll niet laden: Kan opgegeven module niet vinden. (Uitzondering van HRESULT: 0x8007007E)"
Source="TwinCAT.Ads"
TypeName=""
StackTrace:
bij TwinCAT.Ads.Internal.TcAdsDllWrapper.TcAdsDll.AdsAmsUnRegisterRouterNotification()
bij TwinCAT.Ads.Internal.TcAdsDllWrapper.AmsUnRegisterRouterNotification(Boolean
throwAdsException)
bij TwinCAT.Ads.Internal.TcLocalSystem.Dispose(Boolean disposing)
bij TwinCAT.Ads.Internal.TcLocalSystem.Finalize()
which is roughly translated to:
Cannot load DLL tcadsdll.dll: Cannot find given module. (Exception at
HRESULT: 0x8007007E)
This is not a dll that I have imported, so it must be from the TwinCAT.ADS.dll
How can I prevent the program from throwing this error at me and instead close the program peacefully? I have tried to catch all the exceptions of every dll related operation possible.
Also the source is on Bitbucket. I will make it public on request.
Some links on the official but quite unhandy Beckhoff site:
http://infosys.beckhoff.com/espanol.php?content=../content/1034/tcquickstart/html/tcquickstart_samplevisualbasicnet.htm&id=10449
Edit:
Apparently using tcclient.dispose() causes the error since the finnaly statement was use instead of just after the try block
Edit: This currently catches the exception but it does not handle it.
Dim currentDomain As AppDomain = AppDomain.CurrentDomain
AddHandler currentDomain.UnhandledException, AddressOf MyHandler
Dim tick As Byte = 0
Sub MyHandler(sender As Object, args As UnhandledExceptionEventArgs)
Dim ex As Exception = DirectCast(args.ExceptionObject, Exception)
MsgBox("exception tick" & Str(tick))
tick = tick + 1
PrintEx(ex)
End Sub
Edit:
The exception isn't caught properly because in vs2008 a couple of errors occurs but the tick appears after I press F5 (continue)
When the program is run directly, I only see 1 Tick. Then windows gives an error.
Did you try an unhandled exception handler?
Dim currentDomain As AppDomain = AppDomain.CurrentDomain
AddHandler currentDomain.UnhandledException, AddressOf MyHandler
Sub MyHandler(sender As Object, args As UnhandledExceptionEventArgs)
Dim e As Exception = DirectCast(args.ExceptionObject, Exception)
Console.WriteLine("MyHandler caught : " + e.Message)
End Sub