How can we make this code run with option strict on? - vb.net

Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
lblSystemSerialNumbers.Text = SystemSerialNumber()
lblCpuIds.Text = CpuId()
End Sub
Private Function SystemSerialNumber() As String
' Get the Windows Management Instrumentation object.
Dim wmi As Object = GetObject("WinMgmts:")
' Get the "base boards" (mother boards).
Dim serial_numbers As String = ""
Dim mother_boards As Object = wmi.InstancesOf("Win32_BaseBoard")
For Each board As Object In mother_boards
serial_numbers &= ", " & board.SerialNumber
Next board
If serial_numbers.Length > 0 Then serial_numbers = serial_numbers.Substring(2)
Return serial_numbers
End Function
Private Function CpuId() As String
Dim computer As String = "."
Dim wmi As Object = GetObject("winmgmts:" & _
"{impersonationLevel=impersonate}!\\" & _
computer & "\root\cimv2")
Dim processors As Object = wmi.ExecQuery("Select * from Win32_Processor")
Dim cpu_ids As String = ""
For Each cpu As Object In processors
cpu_ids = cpu_ids & ", " & cpu.ProcessorId
Next cpu
If cpu_ids.Length > 0 Then cpu_ids = cpu_ids.Substring(2)
Return cpu_ids
End Function
End Class
This code will retrieve the CPU id and the motherboard id. How do I ensure that this will work even when option strict is on.
Why this could be a problem?
Well, let's see. The type of wmi is Object. That wmi do not necessarily support methods like InstancesOf, and SerialNumber
So how can we pull this out?
I think object that we got from GetObject is not just pure object. I think we should ctype or direct cast it to a more appropriate type. That more appropriate type will support methods like InstancesOf, SerialNumber, etc.
However what are the appropriate types?

You could use the ManagementObjectSearcher from the WMI classes hosted inside the System.Management.dll assembly.
(And you need to add the appropriate reference).
In this way you could write the SystemSerialNumber as
Private Function SystemSerialNumber(computer As String) As String
Dim wmi = New ManagementObjectSearcher(computer & "\root\cimv2", "select * from Win32_BaseBoard")
Dim boards = New List(Of String)()
For Each board In wmi.Get()
Dim temp = board.Properties("SerialNumber").Value?.ToString()
If Not String.IsNullOrEmpty(temp) Then
boards.Add(temp)
End If
Next board
Return String.Join(", ", boards)
End Function
The CpuId function is a bit more complex because you want to set the Impersonate flags but it is still possible to write a method around the NET wrapper classes for WMI interfaces
Private Function CpuId(computer As String) As String
Dim cpu = New List(Of String)()
Dim options = New ConnectionOptions()
options.Impersonation = System.Management.ImpersonationLevel.Impersonate
Dim scope = New ManagementScope(computer & "\root\cimv2", options)
scope.Connect()
Dim query = New ObjectQuery("Select * from Win32_Processor")
Dim wmi = New ManagementObjectSearcher(scope, query)
For Each m As ManagementObject In wmi.Get()
Dim temp = m.Properties("ProcessorId").Value?.ToString()
If Not String.IsNullOrEmpty(temp) Then
cpu.Add(temp)
End If
Next
Return String.Join(", ", cpu)
End Function

Related

Explicit (runtime) DLL linking with Visual Basic 2013 Fails

I wrote a dll in Visual Basic on Visual Studio 2013 (what it does is irrelevant now):
Namespace TextFiles
Public Class ConfigTextFiles
'Library of functions to handle configuration text files holding pairs of parm-name, param-value.
'Param-name and Param-value are separated by the Delimiter property, or "=" if not set.
Shared strDelimiter As String
Shared intRowsCount As Integer
Public Shared Function getParamValue(ByVal strFileLocation As String, ByVal strFileName As String, ByVal strParamName As String) As String
'Return the value of a specific parameter within the text file
'Return an empty string if param-name is not found
'Return vbNullChar if file was not found
Dim TextLine() As String
Dim strFullFileName As String
If Right(strFileLocation, 1) = "\" Then
strFullFileName = strFileLocation & strFileName
Else
strFullFileName = strFileLocation & "\" & strFileName
End If
getParamValue = ""
If System.IO.File.Exists(strFullFileName) = True Then
If (IsNothing(strDelimiter)) Then strDelimiter = "="
Dim objReader As New System.IO.StreamReader(strFullFileName)
Do While objReader.Peek() <> -1
TextLine = Split(objReader.ReadLine(), strDelimiter)
If (Trim(strParamName) = Trim(TextLine(0))) Then
getParamValue = Trim(TextLine(1))
Exit Do
End If
Loop
objReader = Nothing
Else
MsgBox("File Does Not Exist: " & strFullFileName, MsgBoxStyle.OkOnly, "File Open Error")
getParamValue = vbNullChar
End If
End Function
End Class
End Namespace
This DLL is called: SisConfigTextFiles.dll and is stored in the folder of the executing (calling) (exe) file.
I want to explicitly link to it at runtime, from another Visual Basic application:
Public Class Initializing
Public Const CONFIG_FILE_NAME As String = "AppConfig.ini"
Public Shared Function getConfigParam(ParamName As String) As String
'Load external dll
Dim asm As Assembly = Assembly.Load("SisConfigTextFiles")
Dim type As Type = asm.GetType("TextFiles.ConfigTextFiles")
' Create an instance of a Type by calling Activator.CreateInstance
Dim dynamicObject As Object = Activator.CreateInstance(type)
Dim appPath As String = My.Application.Info.DirectoryPath
getConfigParam = ""
Dim returnValue = DirectCast(type.InvokeMember("getParamValue", _
BindingFlags.InvokeMethod Or BindingFlags.Static, _
Nothing, dynamicObject, {appPath, CONFIG_FILE_NAME, ParamName}), String)
getConfigParam = returnValue
End Function
End Class
However, I keep getting this runtime error:
System.ArgumentNullException: Value cannot be null.
Parameter name: type
Why is "type" Null? How can I straighten this out?
Thanks!
You are almost there but this line isnt valid:
asm.GetType("TextFiles.ConfigTextFiles")
It should be
asm.GetType("SisConfigTextFiles.TextFiles.ConfigTextFiles")
You can see the available types in the Assembly.ExportedTypes property.
Following Sam's advice, here's the fully working code that invokes a method in a DLL with late (run-time) binding:
Imports System.Reflection
Public Class Initializing
Public Const CONFIG_FILE_NAME As String = "AppConfig.ini"
Public Shared Sub Config_Init()
End Sub
Public Shared Function getConfigParam(ParamName As String) As String
'Load external dll
Dim asm As Assembly = Assembly.Load("SisConfigTextFiles")
Dim type As Type = asm.GetType("SisConfigTextFiles.TextFiles.ConfigTextFiles")
' Create an instance of a Type by calling Activator.CreateInstance
Dim dynamicObject As Object = Activator.CreateInstance(type)
Dim appPath As String = My.Application.Info.DirectoryPath
getConfigParam = ""
'Invoking a function "setParamValue" in the dll
Dim returnValue = DirectCast(type.InvokeMember("getParamValue", _
BindingFlags.InvokeMethod Or BindingFlags.Static Or BindingFlags.Public, _
Nothing, dynamicObject, {appPath, CONFIG_FILE_NAME, ParamName}), String)
getConfigParam = returnValue
End Function
End Class

Mongodb Driver 2.0 find() in VB.net not working

I am trying to practice with the C# Driver 2.0 for MongoDB. I don't know C# so I am writing the code in Visual Basic.
When I try to print out a list code won't compile. I know I have the For Each contents commented out but that's not the problem. I want to query the db for all documents in a collection and print them out to a text box.
Below is the code that isn't working. The last part is giving me trouble.
Private Sub btnListUsers_Click(sender As Object, e As EventArgs) Handles btnListUsers.Click
Dim ConnString As String
ConnString = txtConnStr.Text
Dim vDbName As String
vDbName = txtDb.Text
Dim vColName As String
vColName = txtColl.Text
Dim vClient As MongoClient
vClient = DbConnection(ConnString, vDbName, vColName)
Dim vDb As MongoDatabaseBase
vDb = vClient.GetDatabase(vDbName)
Dim vCol As IMongoCollection(Of BsonDocument)
vCol = vDb.GetCollection(Of BsonDocument)(vColName)
Dim query As BsonDocument
query = New BsonDocument("Names", txtListUsers.Text)
For Each item As BsonDocument In vCol.Find(query).ToListAsync()
'print a count
'print bson document
Next
End Sub
Any help is appreciated. I have been banging my head against the keyboard for hours.
BELOW IS THE CODE THAT WORKED FOR ME AFTER ALEX GAVE ME AN ANSWER:
Private Async Sub btnListUsers_Click(sender As Object, e As EventArgs) Handles btnListUsers.Click
Dim ConnString As String
ConnString = txtConnStr.Text
Dim vDbName As String
vDbName = txtDb.Text
Dim vColName As String
vColName = txtColl.Text
Dim vClient As MongoClient
vClient = DbConnection(ConnString, vDbName, vColName)
Dim vDb As MongoDatabaseBase
vDb = vClient.GetDatabase(vDbName)
Dim vCol As IMongoCollection(Of BsonDocument)
vCol = vDb.GetCollection(Of BsonDocument)(vColName)
Dim query As BsonDocument
query = New BsonDocument("Name", txtListUsers.Text)
Dim myList As List(Of BsonDocument) = Await vCol.Find(query).ToListAsync()
Dim i As Integer = 0
For Each vItem As BsonDocument In myList
'count
i += 1
'print bson document
rtfDataDisplay.Text = rtfDataDisplay.Text & vbCrLf & "#" & i.ToString & " - " & vItem.ToString & vbCrLf
Next
End Sub
I had to add Async to the sub. I also had my key "Name" incorrect. After that things went smoothly. Woo Hoo!
Your problem is in this line:
For Each item As BsonDocument In vCol.Find(query).ToListAsync()
The call to the ToListAsync() method returns an awaitable Task(Of List(Of BsonDocument)), i.e. a promise that it will return a list that you can enumerate when it has completed fetching it. You need to Await it, to get to the list. As in:
Dim myList As List(Of BsonDocument) = Await vCol.Find(query).ToListAsync()
For Each item As BsonDocument in myList
'print a count
'print bson document
Next
An alternative construct offered by the MongoDB driver, is to use its ForEachAsync method, and supply it with a lambda delegate argument.

Connect to MongoDB server with Driver 2.0 best practice

I have VB.net code that connects to a MongoDB. When the database is up and running my code works fine, but when the database is not running I don't get any errors back.
How do I check that the Server is up and running so I can connect to it and do my work? Basically IF the Server is up do work ELSE return a message to user that server is not available.
I look at the documentation about the MongoClient Class but I can't seem to find anything I can use.
MongoClient Class (http://api.mongodb.org/csharp/2.0/html/T_MongoDB_Driver_MongoClient.htm)
Below is my code that works to connect to the MongoDB:
Public Function DbConnection(ByRef ConnString As String, vDbName As String, vColName As String) As MongoClient
'default port
'ConnString = "mongodb://localhost:27017"
'example DB and Collection
'vDbName = "blog"
'vColName = "users"
'Root Object
Dim vClient As MongoClient
vClient = New MongoClient(ConnString)
Dim vDb As MongoDatabaseBase
vDb = vClient.GetDatabase(vDbName)
Dim vCol As IMongoCollection(Of BsonDocument)
vCol = vDb.GetCollection(Of BsonDocument)(vColName)
Return vClient
End Function
Below is additional code where I use InsertOneAsync without creating an error:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If txtName.Text = "" Then
MsgBox("Enter a Name for the Database.")
Else
Dim ConnString As String
ConnString = txtConnStr.Text
Dim vDbName As String
vDbName = txtDb.Text
Dim vColName As String
vColName = txtColl.Text
'Root Object
Dim vClient As MongoClient
vClient = DbConnection(ConnString, vDbName, vColName)
Dim vDb As MongoDatabaseBase
vDb = vClient.GetDatabase(vDbName)
Dim vCol As IMongoCollection(Of BsonDocument)
vCol = vDb.GetCollection(Of BsonDocument)(vColName)
Dim vAddUser As BsonDocument
vAddUser = New BsonDocument
vAddUser.Add("_Id", txtID.Text)
vAddUser.Add("Name ", txtName.Text)
vAddUser.Add("Email", txtEmail.Text)
vAddUser.Add("City", txtCity.Text)
rtfDataDisplay.Text = "BsonDocument = " & vAddUser.ToString & ", #" & vAddUser.Count
Await vCol.InsertOneAsync(vAddUser)
End If
End Sub
Below is the solution I came up with. I am only posting the Try...Catch since I have already posted entire procedures above.
Try
Dim watch As Stopwatch = New Stopwatch
watch.Start()
Dim insertResult As Task = vCol.InsertOneAsync(vAddUser)
Await insertResult
watch.Stop()
MsgBox("Faulted =" & insertResult.IsFaulted.ToString & ", Status = " & insertResult.Status.ToString & ", Watch = " & watch.Elapsed.ToString)
Catch ex As Exception
If ex.HResult.ToString = "-2146233083" Then
MsgBox("unable to insert data due to a timeout exception")
Else
MsgBox("Unable to insert data = " & ", HResult = " & ex.HResult.ToString & "!" & ex.ToString)
End If
End Try
Since the asynch only returns Task, it doesn't wait until the operation is complete. If you wait after the task and then you will capture the exception and process it accordingly, Here is the sample
Change this
Await vCol.InsertOneAsync(vAddUser)
Var insertTask = vCol.InsertOneAsync(vAddUser); insertTask.Wait();
and then remove the async keyword from the button_click method signature.

communicating with a scale using USB vb.net

I have been given a project at work to write VB.net code that will communicate with a shipping scale using USB. Unfortunately I have never written anything to communicate with USB before so I have no idea how to go about this. I have searched the net and found plenty of ways to do it with C#. But everything I find with VB.net I cannot get to work. I came across this code which looked promising unfortunately I'm getting the error "System.Managermant.ManagementObject is not defined". I imported System.management and I still have the error. I need an example of how to communicate with a USB device
Imports System.Management
Public Class Form1
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim strDeviceName As String
Dim strQuotes As String
Dim arrDeviceNames As Array
Dim USBDevice As System.Management.ManagementObject
Dim objReturnCollection As System.Management.ManagementObjectCollection
'Dim ObjScope As New System.Management.ManagementScope("\\FullNameOfYourComputer\root\cimv2") 'This is optional. Can be used for remote connections.
Dim SearcherUSBDevicesCollection As New System.Management.ManagementObjectSearcher("Select * from Win32_USBControllerDevice")
Dim ReturnUSBDevicesCollection As System.Management.ManagementObjectCollection
ReturnUSBDevicesCollection = SearcherUSBDevicesCollection.Get
'Or
'Dim USBDevicesClass As New System.Management.ManagementClass("Win32_USBControllerDevice")
'Dim ReturnUSBDevicesCollection As System.Management.ManagementObjectCollection = USBDevicesClass.GetInstances()
For Each USBDevice In ReturnUSBDevicesCollection
strDeviceName = USBDevice.Properties("Dependent").Value.ToString()
strQuotes = Chr(34)
strDeviceName = Replace(strDeviceName, strQuotes, "")
arrDeviceNames = Split(strDeviceName, "=")
strDeviceName = arrDeviceNames(1)
Dim objSearcher As New System.Management.ManagementObjectSearcher("Select * From Win32_PnPEntity Where DeviceID = '" & strDeviceName & "'")
objReturnCollection = objSearcher.Get()
Dim objReturn As System.Management.ManagementObject
For Each objReturn In objReturnCollection
Me.ListBox4.Items.Add("Description: " & objReturn("Name").ToString())
Me.ListBox4.Items.Add("DeviceID: " & objReturn("DeviceID").ToString())
Next
Next
End Sub
End Class
Go to your project settings and add a reference to System.Management.

Visual Basic - system.nullReferenceException

So I'm still a bit of a newbie when it comes to programming, hence why I'm using visual basic. I'm getting this exception raised repeatedly, but the variables that vb is saying have unassigned values have been given values in my code. Can anyone point out where I'm going wrong with this?
EDIT: just a few more details: the file exists, I can read from it using just the ReadLine method, but I need to split the fields so I can compare the scores and get the highest 2 scores
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim srdFile As System.IO.StreamReader
Dim strLine As String
Dim strField(1) As String
Dim strName() As String
Dim strScore() As String
Dim i = 0
srdFile = New System.IO.StreamReader("HighScores.dat")
rtbOut.AppendText("HighScores:" & vbNewLine & vbNewLine)
Do Until srdFile.Peek() = -1
strLine = srdFile.ReadLine()
strField = strLine.Split(",")
strName(i) = strField(0)
strScore(i) = strField(1)
rtbOut.AppendText(strName(i) & ", " & strScore(i) & vbNewLine)
i = i + 1
Loop
End Sub
Following two arrays are never initialized: strName and strScore
I don't know the logic, but one way would be to use a List(Of String) instead which does not need to get the correct size in the first place and can be resized. I would also use the Using-statement to dispose the stream properly:
Using srdFile As New System.IO.StreamReader("HighScores.dat")
Dim strLine As String
Dim strField(1) As String
Dim strName As New List(Of String)
Dim strScore As New List(Of String)
Dim i = 0
rtbOut.AppendText("HighScores:" & vbNewLine & vbNewLine)
Do Until srdFile.Peek() = -1
strLine = srdFile.ReadLine()
strField = strLine.Split(","c)
strName.Add(strField(0))
strScore.Add(strField(1))
rtbOut.AppendText(strName(i) & ", " & strScore(i) & vbNewLine)
i += 1
Loop
End Using
Side-note: i recommend to set Option Strict to On by default.
By the way, here is a completely different approach doing the same but with LINQ:
Dim lines = From line In IO.File.ReadLines("HighScores.dat")
Where Not String.IsNullOrWhiteSpace(line)
Let fields = line.Split(","c)
Let name = fields.First()
Let score = fields.Last()
Select String.Format("{0}, {1}", name, score)
rtbOut.Text = String.Join(Environment.NewLine, lines)
I find this more readable.
Before you use an array, you need to assign a fixed array size in the computer memory locations. You can do this by initialising an array with the number of array elements. In your code, you have not allocated any memory to strName() and strScore() before using them, hence the code will throw an exception.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim srdFile As System.IO.StreamReader
Dim strLine As String
Dim strField(1) As String
Dim strName(10) As String ''fixed size array (Using List(Of T) is a better option)
Dim strScore(10) As String ''fixed size array (Using List(Of T) is a better option)
Dim i = 0
srdFile = New System.IO.StreamReader("HighScores.dat")
rtbOut.AppendText("HighScores:" & vbNewLine & vbNewLine)
Do Until srdFile.Peek() = -1
strLine = srdFile.ReadLine()
strField = strLine.Split(",")
strName(i) = strField(0)
strScore(i) = strField(1)
rtbOut.AppendText(strName(i) & ", " & strScore(i) & vbNewLine)
i = i + 1
Loop
End Sub
You can also create a dynamic array. Please follow Resizing an array at runtime in VB.NET on Stackoverflow about dynamic array.