BluetoothLEDevice.FromIdAsync Returns Uncastable __ComObject - vb.net

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.

Related

Strange bug in MSHTML Assembly

Dim u As UInteger = 0
Try
Do
u += 1
j = DirectCast(o.item(d), HTMLTableRow).cells
Loop
Catch ex As Exception
MsgBox("Access No." & u & " throws:" & ex.GetType.ToString & ":" & ex.Message)
End Try
This is the piece of code I used as a test - a dead loop, infinitely accessing the variable O (assigned in code before) and assigning it to the variable J with some operation (O and J are both MSHTML.IHTMLElementCollection type). Under the debug mode, I can run it normally until the counter u reaches its upper limit. However, under the release mode, after loop for 5000~6000 times (in each test the number is different) it will throw "NullReferenceException". Note that I've just accessed O, never changed it, why is the exception? Is this a bug of MSHTML the assembly? Moreover, if I make a minor change:
Dim u As UInteger = 0, v As Object
Try
Do
u += 1
v = DirectCast(o.item(d), HTMLTableRow)
Loop
Catch ex As Exception
MsgBox("Access No." & u & "throws:" & ex.GetType.ToString & ":" & ex.Message)
End Try
That is, to remove the ".cells", and then there will be no exceptions. What's going on here? (This cannot be used as a workaround because in my program the ".cells" must be accessed)
If I use TryCatch block to ignore the exception and just try again, it won't run normally any more - throwing the exception for each loop. There must be some qualitative changes.
OK, MSHTML, you win. I have to use the dumbest workaround. Just try...catch the exception and try again. After numerous tests, I got the following possible exceptions that must be handled:
COMException, when you can simply try the statement throwing this again.
UnauthorizedAccessException, when you can simply try again like the last one.
NullReferenceException, when you CANNOT simply try again as you'll again catch the same exception. You'll have to initialize a new HTMLDocument and reload the URL, then do the remaining work.
Wanting anyone with a more elegant solution. I swear I'll never use the hideous Assembly if possible.

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.

How do I handle NullReferenceException with my code

How do I handle this type of error or exception?
Try
If log.Trim = txtUSN.Text Then
MessageBox.Show("USN found: " & log)
Else
MessageBox.Show("USN not found: " & log)
End If
Catch ex as Exception
MessageBox.Show(ex.Message)
The message was "Object reference not set to an instance of an object."
This is the rest of the code:
Dim log As String
Dim sql As New SqlCommand
sql.Connection = MyConnection
sql.CommandText = "SELECT * FROM dbo.tblAcc WHERE USN = '" & txtUSN.Text & "' "
MyConnection.Open()
log = sql.ExecuteScalar
MyConnection.Close()
The simple answer is your trying to use an object that is nothing. If it's nothing you can't use it, hence "Object reference not set to an instance of an object."
As already mentioned in my comments above, the culprit is: log. I'm not sure where you have declared this or when your using it and how your using it, for all I know it's nothing. If you have more code that would be greatly appreciated as I can point out where it's nothing, until then here's how to get around your issue.
Try
If log IsNot Nothing Then
If log.Trim = txtUSN.Text Then
MessageBox.Show("USN found: " & log)
Else
MessageBox.Show("USN not found: " & log)
End If
Else
MessageBox.Show("Log is NOTHING!")
End If
Catch ex as Exception
MessageBox.Show(ex.Message)
End Try
**EDIT**
After you posted more code in a comment (please post code in the area, not in comment's) it seem's there are a few issue's. You have log defined as a string; when you do this set it to something like: String.Empty instead of nothing. Also you want to sse the ExecuteScalar method to retrieve a single value (for example, an aggregate value) from a database which could be an integer, long, single etc data types. In your query your selecting everything, you can't call ExecuteScalar to return that data... I would recommend looking up information about building queries and executing them, it's to long for me to get in depth with it here.
Happy Coding!
Make sure that (log) is not an empty string.
if not String.IsNullorEmpty(log) then
end if

Vb.net: Calling a Function That Does not Match Prototype Passes Compiler Check

I have encountered something I would like explained. I have a function InitializeValues() that sets up a combobox, and sets the datasource to a datatable. The datatable is retrievedfrom an instance of the class DGVMain_Functions using the public method GetFileSourceData which takes no parameters.
The issue is that a call to GetFileSourceData(MyConnectionString) will actually compile and run. A run time error occurs when the datatable is returned and attempted to set to the datasource of the cbo. The normal call GetFileSourceData() works properly.
I had asked another developer about this, and he thought I had some stale reference, so I cleaned the project, then deleted everything in my debug folder, and rebuilt, but it still had the same behavior.
My question is this, Why does the compiler accept this and not throw a syntax error, and furthermore why does it even get to the point where you can actually step into this function that should not exist, and have it fail on return?
EDIT: Putting Option Strict On, does make the compiler catch this. "Option Strict On disallows implicit conversions from 'String' to 'Integer'. But that is still not the error I want to see. I would like to know why it does not display something along the lines of "No such overload/definition of that function exists".
The error is this:
An error occurred creating the form. See Exception.InnerException for details. The error is: ERROR: ERROR: Conversion from string "user id=XXXX;data source=XXXXX" to type 'Integer' is not valid.Microsoft.VisualBasicFileProcessor.
"InnerException = {"Input string was not in a correct format."}"
Private Sub InitializeValues()
cboFileSource.DisplayMember = "filesource"
cboFileSource.ValueMember = "filesource"
'first call works fine since it matches
cboFileSource.DataSource = DgvMain_functs.GetFileSourceData()
'below is the call that gets through the complier and actually runs, fails on return
cboFileSource.DataSource = DgvMain_functs.GetFileSourceData(MyConnectionString)
End Sub
Public Function GetFileSourceData() As DataTable
Try
Dim dt As DataTable
Dim strSQL As String = "select distinct filesource from FileUpload_FolderPath"
dt = SqlHelper.ExecuteDataset(MyConnectionString, CommandType.Text, strSQL).Tables(0)
Return dt
Catch ex As Exception
Throw New Exception("Error in DGVMain_Functions: " & ex.Message & ex.Source)
End Try
End Function
As you've hinted in your comment, surely Option Explicit On would solve this issue.

Building A True Error Handler

I am trying to build an error handler for my desktop application. The code Is in the class ZipCM.ErrorManager listed below.
What I am finding is that the outputted file is not giving me the correct info for the StackTrace.
Here is how I am trying to use it:
Try
'... Some stuff here!
Catch ex As Exception
Dim objErr As New ZipCM.ErrorManager
objErr.Except = ex
objErr.Stack = New System.Diagnostics.StackTrace(True)
objErr.Location = "Form: SelectSite (btn_SelectSite_Click)"
objErr.ParseError()
objErr = Nothing
End Try
Here is the class:
Imports System.IO
Namespace ZipCM
Public Class ErrorManager
Public Except As Exception
Public Location As String
Public Stack As System.Diagnostics.StackTrace
Public Sub ParseError()
Dim objFile As New StreamWriter(Common.BasePath & "error_" & FormatDateTime(DateTime.Today, DateFormat.ShortDate).ToString().Replace("\", "").Replace("/", "") & ".log", True)
With objFile
.WriteLine("-------------------------------------------------")
.WriteLine("-------------------------------------------------")
.WriteLine("An Error Occured At: " & DateTime.Now)
.WriteLine("-------------------------------------------------")
.WriteLine("LOCATION:")
.WriteLine(Location)
.WriteLine("-------------------------------------------------")
.WriteLine("FILENAME:")
.WriteLine(Stack.GetFrame(0).GetFileName())
.WriteLine("-------------------------------------------------")
.WriteLine("LINE NUMBER:")
.WriteLine(Stack.GetFrame(0).GetFileLineNumber())
.WriteLine("-------------------------------------------------")
.WriteLine("SOURCE:")
.WriteLine(Except.Source)
.WriteLine("-------------------------------------------------")
.WriteLine("MESSAGE:")
.WriteLine(Except.Message)
.WriteLine("-------------------------------------------------")
.WriteLine("DATA:")
.WriteLine(Except.Data.ToString())
End With
objFile.Close()
objFile = Nothing
End Sub
End Class
End Namespace
What is happenning is the .GetFileLineNumber() is getting the line number from objErr.Stack = New System.Diagnostics.StackTrace(True) inside my Try..Catch block. In fact, it's the exact line number that is on.
Any thoughts of what is going on here, and how I can catch the real line number the error is occuring on?
Edit: Changed the code to account for the Exception.StackTrace being a string rather than a real StackTrace
You're creating a new StackTrace, so then it will be for the line you're declaring it on, if you want the line number of the original exception, use the stack trace in Exception.StackTrace.
I think you're being a little confused, I can't see why you create the new StackTrace at all?
Edit: Added more bits to the answer here since easier to see the syntax than in a comment
Currently you have the line
objErr.Stack = New System.Diagnostics.StackTrace(True)
Which means that you're creating a whole new stacktrace, starting when you're creating it.
Instead change that line to:
objErr.Stack = New System.Diagnostics.StackTrace(ex, True)
Which will have the stacktrace from when the error actually happened.
Edit: Added complete sample:
Private Sub a1()
Try
a2()
Catch ex As Exception
Dim st As New StackTrace(True)
Debug.WriteLine(String.Format("ST after exception, will give line number for where st was created. Line No: {0}", st.GetFrame(0).GetFileLineNumber()))
st = New StackTrace(ex, True)
Debug.WriteLine(String.Format("ST after exception using exception info, will give line number for where exception was created. Line No: {0}", st.GetFrame(0).GetFileLineNumber()))
End Try
End Sub
Private Sub a2()
Dim st As New StackTrace(True)
Debug.WriteLine(String.Format("ST before exception, will give line number for where st was created. Line No: {0}", st.GetFrame(0).GetFileLineNumber()))
Dim b As Integer = 0
Dim a As Integer = 1 / b
End Sub
SOLVED: You should change the wrong instruction, of the original code:
.WriteLine(Stack.GetFrame(0).GetFileLineNumber())
with this new one:
.WriteLine(Stack.GetFrame(Stack.FrameCount - 1).GetFileLineNumber)
and you will see, it will return the exact Line_Number of code
where the run time error occurs!!
You do not need to include a Stack property for your ErrorManager, because you have access to the stack trace through the exception.
From experience, I would create a Shared Sub Write(ex As Exception, location As String) method on the ErrorManager, and call in your Catch statement as:
ZipCM.ErrorManager.Write(ex, "Form: SelectSite (btn_SelectSite_Click)")
This change results in cleaner code, reduces the need to write lots of code in each Catch statement, and allows you to change the implementation of Write without having to revisit/rework/refactor each Catch statement.
For instance, you can change the Write method to also call Debug.WriteLine(ex) so you can see which exceptions you are handling during debugging without having to open the file. Moreover, you may want to include WriteNotify method that displays a message box of the exception then calls the Write method to log the exception.