How do I migrate CanPropertyChange("Text") from VB6 to VB.NET? - vb.net

I want to upgrade an ActiveX control from VB6 to VB.NET. I'm having trouble upgrading this code:
If CanPropertyChange("Text") Then
Text1.Text = Value
RaiseEvent TextChange()
End If
I get the error: "Name 'CanPropertyChange' is not declared."

VB.Net does not support anything similar to the CanPropertyChange("Text") method. The easiest way around this is to do the assignment within a Try...Catch block.
Try
Text1.Text = Value
RaiseEvent TextChange()
Catch ex As Exception
End Try
If the property cannot be written to an exception will be thrown which you can access in the Catch block.

Related

BluetoothLEDevice.FromIdAsync Returns Uncastable __ComObject

Before we even start: In researching this problem I've looked at dozens of posts here and elsewhere, and realize that VB is the worst for Bluetooth programming. However, this is for a client who has a massive legacy VB system and I have no choice.
According to MS documentation, the BluetoothLEDevice.FromIdAsync is supposed to return a BluetoothLEDevice object but on my system it returns a generic System.__ComObject that I can't cast to a BluetoothLEDevice. I have tried Cast and DirectCast but neither work. The sample C++ code I've looked at doesn't require any type of casting; the variable is declared and set using the BluetoothLEDevice.FromIdAsync function without any dramas.
Here is my test code.
Private Sub ConnectToDevice(di As DeviceInformation)
'
' di.id = "BluetoothLE#BluetoothLE48:5f:99:3c:fd:36-84:2e:14:26:9e:7b"
'
Debug.Print("Connecting To " & di.Name & " at " & Now.ToShortTimeString)
Dim genericObject As Object
Dim myDevice As BluetoothLEDevice
Try
genericObject = BluetoothLEDevice.FromIdAsync(di.Id)
Catch ex As Exception
Debug.Print(ex.Message)
End Try
If Not IsNothing(genericObject) Then Debug.Print("Using 'Object' yeilds " & genericObject.ToString)
Try
myDevice = BluetoothLEDevice.FromIdAsync(di.Id)
Catch ex As Exception
Debug.Print(ex.Message)
End Try
End Sub
And here is the output:
Connecting To TM-2021090161 at 2:08 PM
Using 'Object' yeilds System.__ComObject
Exception thrown: 'System.InvalidCastException' in testBTLE.exe
Unable to cast object of type 'System.__ComObject' to type 'Windows.Devices.Bluetooth.BluetoothLEDevice'.
And a screen shot of the returned genericObject:
The FromIdAsync needs to be run async which I couldn't do because I got an error stating it didn't have an awaiter. It turns out that I needed to NuGet the System.Runtime.Windowsruntime dll. I added the Await and it's working now.
Thanks to Andrew for pointing me in the right direction.

Setting a textbox property value within a catch block

Using VS 2013 VB.net for my ClickOnce application. I've got a function which verifies database functionality and the guts are wrapped in a Try Catch. A portion of my Catch block looks like this:
Catch ex As Exception When Err.Number = "5"
My.Application.Log.WriteException(ex)
If My.Settings.g_blnDebugMode Then
MessageBox.Show(Err.Number & " " & ex.ToString, "Exception Error")
End If
If Err.Description.Contains("The specified table does not exist") Then
MessageBox.Show("Selected file is not a valid database.", "Exception Error")
ElseIf Err.Description.Contains("The specified password does not match the database password.") Then
MessageBox.Show("The specified password does not match the current database password.", "Exception Error")
End If
Return False
What I want to do is, clear two different fields based on the two custom error messages at the bottom. Something like TextBox1.Text = "" or TextBox2.Text = "" depending on which error is thrown (invalid password or invalid database). My problem is that I don't seem to be able to set them directly or set the value of a module or global variable from within the catch block.
Error is:
Cannot refer to an instance member of a class from within a shared method or shared member initializer without an explicit instance of the class.
If it's possible how can I work around this and set my TextBoxes based on the results in the Catch block?
Usually the method to achieve what you are trying is to use a second a catch block
Catch ex As Exception When Err.Number = ""
MessageBox.Show(ex.Message)
FlushTextBox1();
Catch ex As Exception When Err.Number = ""
MessageBox.Show(ex.Message)
FlushTextBox2();
The error is likely appearing because the try-catch block is inside a Shared method. What that means is, the change in the value of TextBox will be repeated for all instances of the class. If you want the TextBox to behave like this, add the Shared keyword to its own declaration and remove it from the Sub's declaration. If you don't want this behaviour at all, just remove the Shared keyword. For more information on the error check the MSDN article
Alternatively you can call a local function (as shown in code) FlushTextBox1() to change the value of the TextBox outside the Sub.

Does the VB ErrorObject work with try/catch blocks or just onError/GoTo?

When converting onError/GoTo statements from VB6 into VB.Net, I've been told to use try/catch statements instead. Most of the VB6 error blocks utilize Microsoft.VisualBasic.ErrObject to provide the error code and description. For example:
CombinePDF_ERROR:
lErrorCode = Err
strErrorSource = Err.Source
strErrorDescription = Err.Description
bInProcess = False
strCombinePDFLastFile1 = strFile1
strCombinePDFLastFile2 = strFile2
ChDrive left$(strCurrentDir, 1)
ChDir strCurrentDir
Call CombinePDFUIUnload
Err.Raise lErrorCode, strErrorSource, strErrorDescription
End Sub
Does the Err (Microsoft.VisualBasic.ErrObject) get its information from the onError/GoTo statements? lErrorCode, strErrorSource, strErrorDescription aren't given values prior to this. How do I replicate this functionality in a try/catch? Catch an exception and messageBox the message? First time using VB6 or VB.Net. Thank you for your time.
That specific code in your question is basically like this Catch block below. The Err.Raise is equivalent to a Throw, and the Err object is roughly equivalent to an Exception object.
Catch ex
bInProcess = False
strCombinePDFLastFile1 = strFile1
strCombinePDFLastFile2 = strFile2
ChDrive left$(strCurrentDir, 1)
ChDir strCurrentDir
Call CombinePDFUIUnload
Throw ex
But that's just that one block. You need to check each VB6 error handler, work out what its doing, and work out the closest equivalent with Try Catch. You need to understand the VB6 On Error and the Err object, and also .Net Try...Catch and Exception object.
You are going to have a very hard time on this project if you don't know VB6 or VB.Net.

What happens when code in a Finally block throws an Exception?

I have a simple block of legacy code that sits inside of a loop that crawls through some potentially bad xml node by node and which needs to be refactored as it is not working as intended:
Try
xmlFrag.LoadXml("<temproot>" & strXMLfragment & "</temproot>")
writer.WriteRaw(strXMLfragment)
Catch ex As Exception
InvalidXML = True
End Try
What this block is meant to do is check for valid xml and then write the xml out. What it actually does is check for invalid xml and then write the xml out only if it is valid. So it needs to be fixed to work as intended.
My first attempt at a fix:
Try
xmlFrag.LoadXml("<temproot>" & strXMLfragment & "</temproot>")
'writer.WriteRaw(strXMLfragment)
Catch ex As Exception
InvalidXML = True
Finally
writer.WriteRaw(strXMLfragment)
End Try
This works on my test data but I am concerned that WriteRaw may throw an exception on other data. I haven't found a conclusive statement about what will cause WriteRaw to throw an exception and what will happen when code in a Finally block throws an exception.
So I tried rewriting it like this:
Try
xmlFrag.LoadXml("<temproot>" & strXMLfragment & "</temproot>")
Catch ex As Exception
InvalidXML = True
End Try
Try
writer.WriteRaw(strXMLfragment)
Catch
End Try
Frankly it looks ugly as hell. Is there a more elegant way to refactor this or will the first attempt be suitable?
When an excpetion is raised in a Finally block, nothing special happens: the exception is propagated out and up like any other exception, and code after the exception in this finally block will not be executed.
Your first attempt will fail if strXMLfragment is null or an empty string (or due to a already running asynchronous operation).
So if you really want to handle/swallow all exceptions, you'll have to use two Try blocks.
To make it cleaner, you might want to pull your first Try/Catch into it's own private function and make it reusable:
Private Function TryParseXml(ByVal xml as String) as Boolean
Try
XDocument.Parse(xml)
Return True
Catch ex As Exception
Return False
End Try
End Function
Then wrap your writer.WriteRaw call in it's own Try/Catch.
Dim myXml = "<temproot>" & strXMLfragment & "</temproot>"
If TryParseXml(myXml) Then
xmlFrag.LoadXml(myXml)
Else
Try
writer.WriteRaw(strXMLfragment)
Catch ex as Exception
' handle exception
End Try
End If
Yes, ultimately this is using two Try/Catch blocks. There is no real way around this as the only real way to determine if Xml is valid is to attempt to parse it and wait for it to blow up.
In the end I came up with this profoundly simple and elegant refactoring:
Try
writer.WriteRaw(strXMLfragment)
xmlFrag.LoadXml("<temproot>" & strXMLfragment & "</temproot>")
Catch ex As Exception
InvalidXML = True
End Try
With the WriteRaw line executing first it will always write out the XML unless it throws an exception. Then the LoadXml line can test for validity without interfering with writing the xml out. This way the InvalidXML flag is set as designed and there won't be any unexpected exceptions.

Double exception throwing in a try / finally block

Here's the code example :
Try
Throw New FirstException()
Finally
Throw New SecondException()
End Try
I figured out it only throws SecondException out and FirstException just vanishes.
I thought FirstException would be inside InnerException property of SecondException but it appears it is not.
I'm not blocked on anything as I don't really need the FirstException to show up, I'm just rather intrigued about this behaviour.
Is there a way to know SecondException did get thrown first when
catching it all at upper level ?
If the first exception really is overriden by the second, what is the
reason ?
Does it happen in every other language ? Is it logical ?
I guess the primary explanation for why this works this way is that you are never catching your first exception and passing it along the chain. If you have a situation like the above where you may be throwing several exceptions on the way back to the original caller then you have to either catch them as they are thrown (and include them as an inner exception when creating the next one) :
Dim ex1 As Exception = Nothing
Try
Throw New Exception("first exception")
Catch ex As Exception
ex1 = ex
Finally
Throw New Exception("second exception", ex1)
End Try
Or, probably better - just don't throw until you have all of the exceptions figured out:
Dim ex1 As Exception = Nothing
Try
ex1 = New Exception("first exception")
Finally
Throw New Exception("second exception", ex1)
End Try
Throwing and catching exceptions is expensive, so it's probably best to not throw until you're ready to return and just log along the way.
One of the limitations of exception handling in .net is that there is no nice way for code in a Finally block to know what exception, if any, caused the code in the Try block to exit, nor is there any normal way for code in a finally block which does have such information to make it available to code which might throw an exception.
In vb.net, it's possible to kludge things in a manner that works pretty well, even though it looks a bit ugly.
Module ExceptionDemo
Function CopySecondArgToFirstAndReturnFalse(Of T)(ByRef dest As T, src As T) As Boolean
dest = src
Return False
End Function
Function AnnotateExceptionAndReturnFalse(ex As Exception, TryBlockException As Exception) As Boolean
If ex Is Nothing Then Return False ' Should never occur
If TryBlockException Is Nothing Then Return False ' No annotation is required
ex.Data("TryBlockException") = TryBlockException
Return False
End Function
Sub ExceptionTest(MainAction As Action, CleanupAction As Action)
Dim TryBlockException As Exception = Nothing
Try
MainAction()
Catch ex As Exception When CopySecondArgToFirstAndReturnFalse(TryBlockException, ex)
' This block never executes, but above grabs a ref to any exception that occurs
Finally
Try
CleanupAction()
Catch ex As Exception When AnnotateExceptionAndReturnFalse(ex, TryBlockException)
' This block never executes, but above performs necessary annotations
End Try
End Try
End Sub
Sub ExceptionTest2(Message As String, MainAction As Action, CleanupAction As Action)
Debug.Print("Exception test: {0}", Message)
Try
ExceptionTest(MainAction, CleanupAction)
Catch ex As Exception
Dim TryBlockException As Exception = Nothing
Debug.Print("Exception occurred:{0}", ex.ToString)
If ex.Data.Contains("TryBlockException") Then TryBlockException = TryCast(ex.Data("TryBlockException"), Exception)
If TryBlockException IsNot Nothing Then Debug.Print("TryBlockException was:{0}", TryBlockException.ToString)
End Try
Debug.Print("End test: {0}", Message)
End Sub
Sub ExceptionDemo()
Dim SuccessfulAction As Action = Sub()
Debug.Print("Successful action")
End Sub
Dim SuccessfulCleanup As Action = Sub()
Debug.Print("Cleanup is successful")
End Sub
Dim ThrowingAction As Action = Sub()
Debug.Print("Throwing in action")
Throw New InvalidOperationException("Can't make two plus two equal seven")
End Sub
Dim ThrowingCleanup As Action = Sub()
Debug.Print("Throwing in cleanup")
Throw New ArgumentException("That's not an argument--that's just contradiction")
End Sub
ExceptionTest2("Non-exception case", SuccessfulAction, SuccessfulCleanup)
ExceptionTest2("Exception in main; none in cleanup", ThrowingAction, SuccessfulCleanup)
ExceptionTest2("Exception in cleanup only", SuccessfulAction, ThrowingCleanup)
ExceptionTest2("Exception in main and cleanup", ThrowingAction, ThrowingCleanup)
End Sub
End Module
The module above starts with a couple helper modules which should probably be in their own "Exception helpers" module. The ExceptionTest method shows the pattern for code which might throw an exception in both the Try and Finally block. The ExceptionTest2 method calls ExceptionTest and reports what exception if any comes back from it. ExceptionDemo calls ExceptionTest2 in such a way as to cause exceptions in different combinations of the Try and Finally blocks.
As shown, if an exception occurs during cleanup, that exception will be returned to the caller, with the original exception being an item in its Data dictionary. An alternative pattern would be to catch the exception that occurs on cleanup and include it in the data of the original exception (which would be left uncaught). My general inclination is that it's probably better in many cases to propagate the exception that occurs during cleanup, since any code which was planning to deal with the original exception will probably expect that cleanup succeeded; if such an expectation cannot be met, the exception that escapes should probably not be the one the caller was expecting. Note also that the latter approach would require a slightly different method of adding information to the original exception, since an exception which is thrown in a nested Try block might need to hold information about multiple exceptions that were thrown in nested Finally blocks.