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
Related
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
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
I'm getting NullReferenceException on:
faxnum = Customer.ContactLink.Contact.DefaultFaxLink.Phone.PhoneNumber
The null ref is on the DefaultFaxLink. Since there isn't a fax number, the DefaultFaxLink isn't initialized and I know that if it were, I wouldn't get the error on the assignment.
So, my question is, is there a way I can trap the exception without having to test each object to see if it is nothing?
I just want to treat the entire right hand portion of a statement so that if any part is nothing, I just assign nothing to the left var.
Short of that, could I use reflection on the base object to evaluate each member and its sub-members and assign an empty value?
You could use a Try-Catch block for NullReferenceException
Public Class Customer
Public ContactLink As ContactLink
End Class
Public Class ContactLink
Public Contact As Contact
End Class
Public Class Contact
Public DefaultFaxLink As FaxLink
End Class
Public Class FaxLink
Public Phone As Phone
End Class
Public Class Phone
Public PhoneNumber As String
End Class
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim objCustomer As New Customer
objCustomer.ContactLink = New ContactLink
objCustomer.ContactLink.Contact = New Contact
objCustomer.ContactLink.Contact.DefaultFaxLink = New FaxLink
Dim PhoneNumber As String = ""
Try
PhoneNumber = objCustomer.ContactLink.Contact.DefaultFaxLink.Phone.PhoneNumber
Catch ex As NullReferenceException
PhoneNumber = ""
Catch ex As Exception
MsgBox(ex.Message)
End Try
If Not String.IsNullOrEmpty(PhoneNumber) Then
MsgBox("Fax number is..." & PhoneNumber)
Else
MsgBox("No fax number!")
End If
End Sub
Write a function.
Public Class Customer
Public Function GetFaxNumberSafe() As String
If Me.ContactLink IsNot Nothing AndAlso
Me.ContactLink.Contact IsNot Nothing AndAlso
Me.ContactLink.Contact.DefaultFaxLink IsNot Nothing AndAlso
Me.ContactLink.Contact.DefaultFaxLink.Phone IsNot Nothing Then
Return Customer.ContactLink.Contact.DefaultFaxLink.Phone.PhoneNumber
Else
Return Nothing
End If
End Function
End Class
You could also set up your objects to lazy-load instantiate on access so you always have an object reference.
Public Class Customer
Private _contactLink As New Lazy(Of ContactLink)()
Public ReadOnly Property ContactLink As ContactLink
Get
Return _contactLink.Value
End Get
End Property
End Class
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 have a custom class with constructor, the class is setup such that it would legitly fail to init in some conditions. How do I return nothing?
You can't return anything from a constructor, it's there to initialize.
There are a couple of things you could do, depending on the situation:
If the failiure to initialize is an exceptional circumstance, throw an exception and catch it using a Try block:
Public Sub New()
'... fail to initialize
Throw New ApplicationException("Some problem") 'Or whatever type of exception is appropriate
End Sub
If it fails a lot and you can't filter input or anything, make the constructor Private and construct in a Shared method:
Public Shared Function CreateMyObject()
If someFailure Then
Return Nothing
End If
Return New MyObject() 'Or something
End Function
Its a bit old school but you could have a LastException Property that is set an exception is handled:
Public Class Foo
Private _LastException As Exception = Nothing
Public ReadOnly Property LastException As Exception
Get
Return _LastException
End Get
End Property
Public Sub New()
Try
'init
Catch ex As Exception
_LastException = ex
End Try
End Sub
End Class
This would require you to check LastException after class creation but it is an option?
Usage:
Dim foo1 As New Foo
If Not foo1.LastException Is Nothing Then
'some error occurred
Else
'carry on
End If