I am about to refactor some old VisualBasic application and have come along with the following situation:
Public Sub MySub ()
Try
' execute dangerous operation
Catch ex As System.Exception
Call HandleErrors((ErrObject) ex) ' <-- invalide cast
End Try
End SuB
Public Sub HandleErrors(ByRef objError As ErrObject) ' I can not easily change the signature of this sub as it gets referenced very often.
' process error ..
End Sub
I would like to reuse the already existing Sub ‘HandleErrors()’, which takes an ErrObject as parameter. But since I am more comfortable to use Try and Catch, I would like to pass an Object of the type Syste.Exception, rather than an ErrObject.
Question:
Is there any way of casting or transforming the Exception into an ErrObject ?
Rather than trying to force exceptions into the historic Visual Basic error handling routines, I think the better option here is to create a new overload of HandleErrors (leaving the original sub signature untouched for the places that still use it), possibly with an entirely new function that both the original function and new overload can then call. For instance, lets's assume your function currently looks like this:
Public Sub HandleErrors(ByRef objError As ErrObject)
Log(objError.Description)
PerformSomeGlobalCleanup()
End Sub
And now you want to also be able to log the new-style exceptions too. You could just create a new overload:
Public Sub HandleErrors(ByRef ex As Exception)
Log(ex.Message)
PerformSomeGlobalCleanup()
End Sub
But it would be better for this code to share the underlying structure and logic of whatever HandleErrors is actually doing, so you could do this for instance:
Private Sub InternalHandleErrors(ByVal msg as String)
Log(msg)
PerformSomeGlobalCleanup()
End Sub
Public Sub HandleErrors(ByRef ex As Exception)
InternalHandleErrors(ex.Message)
End Sub
Public Sub HandleErrors(ByRef objError As ErrObject)
'original signature, but refactored
InternalHandleErrors(objError.Description)
End Sub
You can move as much logic from the original HandleErrors into InternalHandleErrors as makes sense - whatever is common between handling an ErrObject and an Exception.
This means you're not "polluting" the newly refactored code with old-style Visual Basic objects, and means that if/when you complete the refactoring to remove the original function when nothing else references it, you don't need to go through your entire code base removing the casts.
Ok,since it seems there is no easy way to do this, I decided to overload the HandleError Sub and introduce a new class to generalise the Exceptions.
Public Sub MySub ()
Try
' execute dangerous operation
Catch ex As System.Exception
Call HandleErrors((ErrObject) ex) ' <-- invalide cast
End Try
End SuB
Class GeneralError
Public ReadOnly Number As Integer
Public ReadOnly Source As String
Public ReadOnly Description As String
Sub New(exception As System.Exception)
Number = 1
Source = exception.Source
Description = exception.Message
End Sub
Sub New(errObject As ErrObject)
Number = errObject.Number
Source = errObject.Source
Description = errObject.Description
End Sub
End Class
Public Sub HandleErrors(ByRef errObject As ErrObject) ' Overload
Dim generalError As GeneralError = New GeneralError(errObject )
Call HandleErrors(generalError)
End Sub
Public Sub HandleErrors(ByRef exception As Exception) ' Overload
Dim generalError As GeneralError = New GeneralError(exception)
Call HandleErrors(generalError)
End Sub
Private Sub HandleErrors(ByRef generalError As GeneralError) ' Original
' process error ..
End Sub
Related
I was beginning to think that I was getting good at VB.net, but not this one has me stumped.
Code looks something like this
Public Class MyServer
.....
Public myMQTTclient = New MqttClient("www.myserv.com")
.....
Private Sub Ruptela_Server(sender As Object, e As EventArgs) Handles
MyBase.Load
<some code>
StartMQTT()
<some more code>
MQTT_Publish(.....)
End Sub
Public Function StartMQTT()
' Establish a connection
Dim code As Byte
Try
code = myMQTTclient.Connect(MQTT_ClientID)
Catch ex As Exception
<error handling code>
End Try
Return code
End Function
Public Sub MQTT_Publish(ByVal DeviceID As String, ByVal Channel As String, ByVal ChannelType As String, ByVal Value As String, ByVal Unit As String)
Dim myTopic As String = "MyTopic"
Dim myPayload As String = "My Payload"
Dim msgId As UShort = myMQTTclient.Publish(myTopic, Encoding.UTF8.GetBytes(myPayload), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, False)
End Sub
As this stands it works 100% OK. The coding may seem a bit odd, but the intent is as follows :
a) create an object 'myMQTTclient' at module level so it has scope throughout the module
b) run StartMQTT() - It can still see the object.
c) within main program call MQTT_Publish many times - I can still see the object
Now the issue is this... it all goes well until "www.myserv.com" fails DNS, then the underlying winsock code throws an exception.
So ... I'm thinking - no problem - just wrap the declaration in a try block or check that www.myserv.com exists before launching the declaration.
Ah, but you can't put code at module level, it has to be in a sub or function.
Hmmm... now I'm stumped. There has to be a 'proper' way to do this, but I'll be darned if I can figure it out.
Anyone able to help ?
I'd follow the advice from #djv about declaring it just as you need it. To wrap that in a Try... Catch block you can do that in an Init method.
Public Class MyServer
Implements IDisposable ' As per djv recommendation which I second...
Private myMQQTclient As MqttClient
Public Sub Init()
Try
myMQQTClient = New MqttClient("<your url>")
Catch ex As Exception
' Do whatever
End Try
End Sub
' more code and implement the Dispose method...
End Class
You can then go on and implement the IDisposble interface to ensure that you release the resources.
Ok, I am not sure if I have the right library. But I found this Nuget package: OpenNETCF.MQTT which seems to have the class you are using.
I would do it this way
Public Class MyServerClass
Implements IDisposable
Public myMQTTclient As MQTTClient
'Private Sub Ruptela_Server(sender As Object, e As EventArgs) Handles MyBase.Load
' ' <some code>
' StartMQTT()
' ' <some more code>
' ' MQTT_Publish(.....)
'End Sub
Public Sub New(brokerHostName As String)
myMQTTclient = New MQTTClient(brokerHostName)
End Sub
Public Function StartMQTT()
' Establish a connection
Dim code As Byte
Try
code = myMQTTclient.Connect(MQTT_ClientID)
Catch ex As Exception
'<error handling code>
End Try
Return code
End Function
Public Sub MQTT_Publish(ByVal DeviceID As String, ByVal Channel As String, ByVal ChannelType As String, ByVal Value As String, ByVal Unit As String)
Dim myTopic As String = "MyTopic"
Dim myPayload As String = "My Payload"
Dim msgId As UShort = myMQTTclient.Publish(myTopic, Encoding.UTF8.GetBytes(myPayload), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, False)
End Sub
#Region "IDisposable Support"
Private disposedValue As Boolean
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
myMQTTclient.Dispose()
End If
End If
disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
End Sub
#End Region
End Class
And now you can see the usage when IDisposable is implemented, in a Using block:
Module Module1
Sub Main()
Using myserver As New MyServerClass("www.myserv.com")
myserver.StartMQTT()
myserver.MQTT_Publish(...)
End Using
End Sub
End Module
This makes it so your object is only in scope in the Using, and the object's Dispose method will automatically be called on End Using
I don't know what the base class was originally and why this was declared Private Sub Ruptela_Server(sender As Object, e As EventArgs) Handles MyBase.Load. It seems like it was possibly a form? You should keep your server code separate from Form code if that was the case. I suppose you could paste the Using into your form load, but then you would be blocking your UI thread. The referenced library has Async support so it might be a good idea to leverage that if coming from a UI.
I've made many assumptions, so I'll stop to let you comment and see how close relevant my answer is.
How would I prevent other developers from enter "" or " " into the following function/sub?
Public Sub MyFunction(MyString as String)
End Sub
' Call:
MyFunction("")
I want them to end up with a non compileable app.
There is no way to prevent compilation based on what is passed to the string. You can, however, simply prevent the method from executing, like this:
Public Sub MyFunction(myString as String)
If Not String.IsNullOrWhitespace(myString) Then
' Do stuff here
End If
End Sub
Your other option is to throw an exception:
Public Sub MyFunction(myString as String)
If String.IsNullOrWhitespace(myString) Then
Throw New ApplicationException("No empty or whitespace strings allowed!")
Else
' Do stuff here
End If
End Sub
My program is throwing an error I should handle. But I cannot return a string message because the it is a function that returns a dataset:
Public Function getUserInfo(ByValue testUserIdAs String) As DataSet
Dim dsUseInfo As DataSet = New DataSet()
Try
Dim objTestWs As New TestWebService.UserMaintenanceSoapClient
dsUseInfo = objTestWs.dsGetUserInfo(TestOU, PAC, paramUserID)
Return (dsUseInfo)
Catch ex As Exception
' TEST FIX ERROR HANDLING -LIWM Please search how to return custom error. I want to return "userid already exists"
Throw
End Try
I was thinking of putting in:
If error
then return "Error Message"
But I can't return it as type string.
It looks like you don't really know what to do with the exception in getUserInfo and just want to pass it on to the outer function.
Well, here's the great thing about exceptions: They are passed on automatically! There's nothing you need to do, and, in particular, you do not need to use the return value of the function for that. Just don't catch the exception until you know what to do with it.
For example, in your case, just handle the error in the calling function instead:
Remove the error handling code from the called function:
Public Function getUserInfo(ByValue testUserIdAs String) As DataSet
Dim objTestWs As New TestWebService.UserMaintenanceSoapClient
Return objTestWs.dsGetUserInfo(TestOU, PAC, paramUserID)
End Function
and add it to the calling function, i.e., replace
...
Dim dsUserInfo As DataSet
dsUserInfo = getUserInfo()
...
with
...
Dim dsUserInfo As DataSet
Try
dsUserInfo = getUserInfo()
Catch ex As Exception
' Do whatever you want to do in case of an error here
MsgBox("Could not get User Info: " & ex.Message)
Return
End Try
...
One you are familiar with this technique, you can go on to more advanced topics like throwing your own exception, like #Justin suggested. Just make sure to include information about the original cause of the error in your own exception, for example, by copying parts of ex.Message into your own exception message and setting the innerException property.
You could throw a generic exception:
Public Function getUserInfo(ByValue testUserIdAs String) As DataSet
Dim dsUseInfo As DataSet = New DataSet()
Try
Dim objTestWs As New TestWebService.UserMaintenanceSoapClient
dsUseInfo = objTestWs.dsGetUserInfo(TestOU, PAC, paramUserID)
Return (dsUseInfo)
Catch ex As Exception
Throw New Exception("Custom message", ex)
End Try
This will set the message on the exception you catch up the call stack to be "Custom Message" with an inner exception that contains the original exception which was thrown.
Or you could create a custom exception(by inheriting from System.Exception) elsewhere if you want to throw a more expressively named exception(and so you don't have to catch all exceptions when you want to catch this custom type).
Public Class UserInfoNotFoundException
Inherits System.Exception
Public Sub New()
End Sub
Public Sub New(message As String)
MyBase.New(message)
End Sub
Public Sub New(message As String, innerException As Exception)
MyBase.New(message, innerException)
End Sub
End Class
You could then, for example, throw a UserInfoNotFoundException.
I would change the whole approach to exceptions and errors:
Create a new assembly (project) and reference it in your application
Declare new shared events which accept your personal datatype (referenced assembly)
Declare at least one shared function (referenced assembly) which does a RaiseEvent on your new events
Add handlers (main application) for your shared events in which you react accordingly
Call your function from within the main application,
passing your own parameters whenever you need to throw errors /
exceptions
This way you circumvent many programming mistakes and centralize error and exception-handling.
Public Module IDENTIFIERS
Public Enum EvtMsg
ERR_MYERR
ERR_MYERR2
End Enum
Public Enum EvtClass
EXCEPTION
ERR
End Enum
End Module
Public Class Events
Shared Event Err(ByVal code As EvtMsg)
Shared Event Exception(ByRef iEx As Exception)
Public Shared Sub Raise(ByVal iEvtClass As EvtClass, ByVal iMsg As EvtMsg, Optional ByRef iEx As Exception = Nothing)
If Not [Enum].IsDefined(GetType(EvtClass), iEvtClass) Then
Dim ex As New ArgumentOutOfRangeException("unbekannte Event-Klasse '" & iEvtClass.ToString & "' übergeben", "iEvtClass")
RaiseEvent Exception(ex)
End If
If Not [Enum].IsDefined(GetType(EvtMsg), iMsg) Then
Dim ex As New ArgumentOutOfRangeException("unbekannte Event-Msg '" & iMsg.ToString & "' übergeben", "iMsg")
RaiseEvent Exception(ex)
End If
Select Case iEvtClass
Case EvtClass.ERR
RaiseEvent Err(iMsg)
Case EvtClass.EXCEPTION
If iEx IsNot Nothing Then
RaiseEvent Exception(iEx)
Else
Dim ex As New MissingFieldException("Raise() ohne Exception aufgerufen, iMsg : " & iMsg & "EvtClass : " & iEvtClass.ToString(), "iEx")
RaiseEvent Exception(ex)
End If
End Select
End Sub
End Class
And now you can easily use these error-handlers in any assembly which references your error-assembly:
Constructor
AddHandler Events.Err, AddressOf Err
AddHandler Events.Exception, AddressOf Except
Class-body
Private Sub Except(ByRef iEx As Exception)
'do your stuff here
End Sub
Private Sub Err(ByVal Err As EvtMsg)
'do your stuff here
End Sub
Afternoon,
I'm struggling at the moment to work out how I can create a view which requires a non default constructor (ie a constructor that requires input),
Public Class Bootstrapper
Inherits UnityBootstrapper
Protected Overrides Function CreateShell() As System.Windows.DependencyObject
Return New LogReader_Modular.Windows.Shell()
End Function
Protected Overrides Sub InitializeShell()
MyBase.InitializeShell()
Application.Current.MainWindow = CType(Me.Shell, Window)
Application.Current.MainWindow.Show()
End Sub
Protected Overrides Sub ConfigureModuleCatalog()
MyBase.ConfigureModuleCatalog()
Dim moduleCatalog As ModuleCatalog = CType(Me.ModuleCatalog, ModuleCatalog)
moduleCatalog.AddModule(GetType(GridModule))
End Sub
....
....
Public Class GridModule
Implements IModule
Private ReadOnly regionManager As IRegionManager
Public Sub Initialize() Implements IModule.Initialize
Try
regionManager.RegisterViewWithRegion("MainDockingRegion", GetType(Views.GridModuleView))
Catch ex As Exception
Trace.WriteLine(String.Format("An Error occured the error was {0}", ex.ToString))
End Try
End Sub
Public Sub New(ByVal regionManager As IRegionManager)
Try
Me.regionManager = regionManager
Catch ex As Exception
Trace.WriteLine(String.Format("An Error occured the error was {0}", ex.ToString))
End Try
End Sub
End Class
my issue is I want to pass in constructor values to GridModuleView, as its not really instantiated as such here, i dont really understand how I can pass in the values, any help with this would be appreciated as I've been looking at this for a few days now. EDIT * the data im wanting to pass in is coming from the shell and not the view, which is why i pasted the bootstrapper
Thanks
Tom.
You could use the RegionManager.RegisterViewWithRegion(IRegionManager, String, Func(Object)) method instead of the RegionManager.RegisterViewWithRegion(IRegionManager, String, Type) method and then pass the input from the shell to the GridModule.
I'm writing an application for a device running Windows XP. There are 2 versions of the device and each version has its own API to communicate with the device's software. The application I'm writing needs to pull the same data from the API. My question is how to write a application that will detect at runtime which version of the device it is on and use the appropriate API. I've figured out how to read the registry to determine the device.
I've created an interface containing all of the common methods and also classes for each device that implement the interface. Now I need to know how to activate the correct one at runtime.
Public Interface IAPI
Sub InitializeMachine()
Function GetActiveProgram() As String
Function GetActiveGCodes() As String
Function GetCurrentBlockNumber() As Integer
''#etc...
End Interface
''#Mill API
Public Class CMAPI : Implements IAPI
Private ObjMachine As Okuma.CMDATAPI.DataAPI.CMachine
Private ObjPgm As Okuma.CMDATAPI.DataAPI.CProgram
Public Sub New()
End Sub
Public Function GetActiveGCodes() As String Implements IAPI.GetActiveGCodes
Try
Return ObjPgm.GetGCodes
Catch ex As Exception
Throw ex
End Try
End Function
Public Function GetActiveProgram() As String Implements IAPI.GetActiveProgram
Try
Return ObjPgm.GetActiveProgramName
Catch ex As Exception
Throw ex
End Try
End Function
Public Function GetCurrentBlockNumber() As Integer Implements IAPI.GetCurrentBlockNumber
Try
Return ObjPgm.GetCurrentBlockNumber
Catch ex As Exception
Throw ex
End Try
End Function
''#....
End Class
''#Lathe API
Public Class CLAPI : Implements IAPI
Private ObjMachine As Okuma.CLDATAPI.DataAPI.CMachine
Private ObjPgm As Okuma.CLDATAPI.DataAPI.CProgram
Public Sub New()
End Sub
Public Function GetActiveGCodes() As String Implements IAPI.GetActiveGCodes
Try
Return ObjPgm.GetGCodes
Catch ex As Exception
Throw ex
End Try
End Function
Public Function GetActiveProgram() As String Implements IAPI.GetActiveProgram
Try
Return ObjPgm.GetActiveProgramName
Catch ex As Exception
Throw ex
End Try
End Function
''#...
End Class
Untested, theory is right - there just might be typos :P
Dim rightAPI As IAPI
If CheckForTypeCMAPI() = true Then ' You said you can determine which device youre on, replace this with the right function
rightAPI = new CMAPI()
Else
rightAPI = new CLAPI()
End If
' Use rightAPI wherever you need it
MessageBox.Show(rightAPI.GetActiveProgram())
I would use a factory method:
Dim rightAPI As IAPI
rightAPI = APIFactory.GetAPI(HowYouDistinguishDevice)
' Use rightAPI wherever you need it
MessageBox.Show(rightAPI.GetActiveProgram())
public class APIFactory
public shared function GetAPI(string HowYouDistinguishDevice) as IAPI
dim oAPI as IAPI
'do whatever it is you need to do to determine which api to use
if CMAPI then oAPI = new CMAPI
if CLAPI then oAPI = new CLAPI
'or you could use select, whatever
return oAPI
end function
end class