InvalidCastException on select XP machines - vb.net

I am having a problem with a tool that I wrote in VB.NET running on certain XP machines. The tool runs fine and as expected on Windows 7 and XP machines that have .NET 4.0.30319.1022.
I've noticed the XP machines that I am having trouble on are those that did not have .NET 4 even on it, so I went and installed .NET 4 which puts on version .NET 4.0.30319.1.
Here is the error:
Here is the code at line 86 (86 is the For Each):
'_BIOSVer
Dim bios_query As String = "SELECT * FROM " & "Win32_BIOS"
Dim bios_searcher As New ManagementObjectSearcher(bios_query)
For Each info As ManagementObject In bios_searcher.Get()
_BIOSVer = info.Properties("BIOSVersion").Value.ToString()
Next info
I am running Windows Updates for .NET 4 Framework to see if that will fix the issue, but I was wondering if you guys might have a sense of what is happening, or if my code is just wrong.
Updates just finished, restarted. It is running off of .NET 4.0.30319.1022 but still throws that error. Must be my code then.

There are several problems, but mainly BIOSVersion is a string array. Rather than poking into that, you can just get the Name which returns the version info (so does Caption and Description). Since you are after one thing, you dont need a loop, but I left it in as a template:
Dim myver As String = ""
Using searcher As New ManagementObjectSearcher("Select Name From Win32_Bios")
For Each mo As ManagementObject In searcher .Get
' EXAMPLE
'DebugProperties(mo)
' no real need to Loop - just for illustration
For Each pd As System.Management.PropertyData In mo.Properties
' if you do SELECT * and want to find something,
' compare Pd.Name to the target property
' some things can be nothing. Such as when a property does not
' exist on older systems, so always test:
If pd.Value IsNot Nothing Then
myver = pd.Value.ToString
Exit For
End If
Next
Next
End Using
Return myver
This is a helpful WMI debug tool to display all the property names and values (the code above has the call to this commented out):
Private Shared Sub DebugProperties(mo As Management.ManagementObject)
For Each pd As PropertyData In mo.Properties
If pd.Value IsNot Nothing Then
Console.WriteLine("{0} {1}", pd.Name,
If(pd.Value IsNot Nothing,
pd.Value.ToString,
"Nothing"))
End If
Next
End Sub

BIOS Version returns a string array (for more information check this link).
Try reading the data like this
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim BIOSSearcher As New ManagementObjectSearcher("SELECT BIOSVersion FROM Win32_BIOS")
Dim BIOSVersionInfo() As String = BIOSSearcher.Get(0).Properties("BIOSVersion").Value
Dim BIOSVersion As String = ""
If Not BIOSVersionInfo Is Nothing Then
BIOSVersion = String.Join(vbCrLf, BIOSVersionInfo)
End If
MsgBox(BIOSVersion)
End Sub

Related

Get List of all PC's in a local area network

The following code works intermittently. Why is that so? Sometimes result is blank,without any error.
Imports System.DirectoryServices
Public Class Form1
Private Sub Button_Click(sender As System.Object, e As System.EventArgs) Handles Button.Click
Dim result As String = ""
Dim domainEntry As DirectoryEntry = New DirectoryEntry("WinNT://CA")'CA is the workgroup
domainEntry.Children.SchemaFilter.Add("Computer")
For Each computer As DirectoryEntry In domainEntry.Children
result = result & computer.Name & Environment.NewLine
Next
MsgBox(result)
End Sub
End class
Windows workgroups are a peer-to-peer arrangement, with no central server that controls who is on the workgroup. As such, the only way to list all the computers in the workgroup is to contact every computer in your network and check if they are part of the workgroup. That means each computer has to be powered on and connected to the same LAN.
My guess is that some computers are offline when you run that, some other reason that they cannot be contacted at the time you run it.
Old answer:
I'm not terribly familiar with using the WinNT provider, but see if you get different results with LDAP. This would find all computers on the domain (I assume "CA" is the name of your domain):
Dim ds As New DirectorySearcher(New DirectoryEntry("LDAP://CA"), "(objectClass=computer)", New String() {"cn"})
Using src As SearchResultCollection = ds.FindAll()
For Each sr As SearchResult In src
result = result & sr.Properties("cn")(0) & Environment.NewLine
Next
End Using

VB.NET Releasing Excel file after reading so it can be overwritten

I am having the same issue that many before me have had, and I have found several threads on the issue but my application of their fixes have not yielded any change.
I am reading an excel file and populating a DataGridView with it. Very simply the user can modify it and then save it. The problem, like the other threads, is that the program has such a tight grip on the file I can't overwrite it.
Among the many fixes offered, Garbage Collection was the most frequently mentioned, but my results are unchanged. I have tried disposing of the OLEDB connection, commands, adapters and datasets also without success.
I've been running through several different examples and tutorials to write this program and so if these fixes are required, I've obviously implemented them in the wrong spot.
Here is the reading block which happens on the form load (I've removed all my attempts to fix it to unclutter the code):
Private Sub Inventory_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Using MyConnection = New System.Data.OleDb.OleDbConnection("provider=Microsoft.Jet.OLEDB.4.0;Data Source='c:\AllTrade\Inventory.xlsx';Extended Properties=Excel 8.0;")
Using MyCommand = New System.Data.OleDb.OleDbDataAdapter("select * from [InventorySheet$]", MyConnection)
DtSet = New System.Data.DataSet
MyCommand.Fill(DtSet)
InventoryGridView.DataSource = DtSet.Tables(0)
MyConnection.Close()
End Using
End Using
End Sub
Here is the saving block (This is the code that generates the "File in Use" error):
Private Sub UpdateInventory_Click(sender As Object, e As EventArgs) Handles UpdateInventory.Click
Dim ExcelApp As New Excel.Application()
ExcelApp.Application.Workbooks.Add(Type.Missing)
For i As Integer = 0 To InventoryGridView.Rows.Count - 1
Dim row As DataGridViewRow = InventoryGridView.Rows(i)
For j As Integer = 0 To row.Cells.Count - 1
ExcelApp.Cells(i + 1, j + 1) = row.Cells(j).Value
Next
Next
ExcelApp.ActiveWorkbook.SaveAs("C:\Alltrade\Inventory.xlsx")
ExcelApp.ActiveWorkbook.Saved = True
ExcelApp.Quit()
End Sub
What's the trick to let go of the file? If the GC.Collect() and GC.WaitForPendingFinalizers() are required, where would they go?
I appreciate any help and apologize since I haven't been able to successfully implement the other answers from similar threads.
You need to marshal to release the COM object: ExcelApp...
Private Sub ReleaseObject(ByVal obj As Object)
Try
Dim intRel As Integer = 0
Do
intRel = System.Runtime.InteropServices.Marshal.ReleaseComObject(obj)
Loop While intRel > 0
Catch ex As Exception
MsgBox("Error releasing object" & ex.ToString)
obj = Nothing
Finally
GC.Collect()
End Try
End Sub
You can call this method like this at the end of your sub.
ReleaseObject(ExcelApp)
You can get more here Excel application not quitting after calling quit
EDIT
I also noticed your connection string is wrong for the newer .xlxs file extention...
Normal ConnectionString : (work for xls files)
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=\"Excel 8.0;HDR=YES;\""
Office 2007 ConnectionString : (work for xlsx files)
"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0;HDR=YES;\""
After much research and frustration, I found a solution.
The connection string of my OleDB connection needs the tidbit added: OLE DB Services = -4 which disables connection pooling. As it happens, the connection is not disposed of (which was the initial hypothesis) when the connection is closed, but instead dumps the connection back into the pool.
Another forum had suggested OLE DB Services = -2 which was supposed to do the same thing, but had no effect in my instance. It's possible it varies between versions but I haven't researched this to conclude that for certain (or someone typoed in one of the forums)
No further code was required in my program to dump the OleDB connection's hold on the file. MyConnection = Nothing and the line that drains the pool followed by a GC.Collect were also recommended but did not resolve my symptoms.
Thanks everyone for the input

VB.Net Threading and Saving Results

I'm working on the program for work, where the user is able to search AD for a asset tag of a certain computer. If it is found it will give them a list in a listbox. I have all of that working, but when they do the search the UI freezes. I'm new to VB and OO for that matter. I understand that it is freezing because the search is running on the same thread as the UI, but I cannot for the life of me get another thread to do the work. When I tried to do the search in another thread I can't update the listbox because its not on the same thread. Any help would be greatly appreciated.
Function that searches AD:
Private Function searchAd()
'clear the results from previous entries
' AdResultListBox.Items.Clear()
Try
Dim rootEntry As New DirectoryEntry("GC://mydomaininfo")
Dim searcher As New DirectorySearcher(rootEntry)
'selects the Computer Name property
searcher.PropertiesToLoad.Add("cn")
Dim compname As String = PropertyTagTextbox.Text
'searches using wildcards
compname = "*" + compname + "*"
searcher.Filter = "(&(name=" + compname + ")(objectcategory=moreADinformation))"
Dim results As SearchResultCollection
results = searcher.FindAll()
Dim result As SearchResult
For Each result In results
'this is the part i'm having trouble with
Me.AdResultListBox.Items.Add(result.Properties("cn")(0)
Next
Catch ex As Exception
End Try
End Function
Private Sub ADSearchButton_Click(sender As Object, e As RoutedEventArgs) Handles ADSearchButton.Click
AdResultListBox.Items.Clear()
'create the new thread for searching
Dim SearchThread As New Thread(AddressOf searchAd)
SearchThread.Start()
End Sub
There happens to be an MSDN article "Using System.DirectoryServices to Search the Active Directory" which shows running it in another thread at http://msdn.microsoft.com/en-us/library/ms973834.aspx#dotnetadsearch_topic9.
This is how I typically do it. The Invoke function is part of the control and that passes a delegate to the UI thread so it can be processed in the correct thread.
Invoke(Sub
Me.AdResultListBox.Items.Add(result.Properties("cn")(0)
End Sub)
http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx

My vb.net program for Excel won't run on different computer

I wrote a vb.net program using Visual Studio 2010 Express to open and manipulate an Excel workbook. I have MS Office 2010 and my program works fine on my computer. When I try to run it on my wife's computer... nothing. I get no error messages... nothing. She has the same version of Office as I do, but she has Windows XP and I have Vista and her computer is has more security protocols than mine, but my other program, which does not import any Office namespaces, runs fine on her computer.
When I look at the task manager on her computer there is nothing under the Applications tab, but under the Processes tab I see an instance of Excel.exe, and if I try to run the program again, still nothing under applications, but yet another instance of Excel.exe under processes.
I have tried to install my program using the Click-Once technology and just the Release folder after a Rebuild... nothing.
I have also tried to compile it under versions 2.0, 3.0, etc.... nothing.
Here is some of the relevant code:
Public Class CleanUpDataForm
Dim m_objExcel As New Excel.Application
Dim m_rngRange, m_rngEnd As Excel.Range
Dim m_blnEntireRow, m_blnCancel As Boolean
Dim m_strRange, m_strSearch, m_strLogFrameCode As String
Const c_strCode As String = "log"
Friend Const c_intScrollBarWidth As Integer = 30
Private Sub CleanUpDataForm_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
m_objExcel = Nothing
Dispose()
End Sub
Private Sub tsbOpen_Click(sender As System.Object, e As System.EventArgs) Handles tsbOpen.Click
ofdGetWorksheet.Title = "Select Worksheet"
ofdGetWorksheet.Filter = "All Excel Files (*.xl*)|*.xl*|(*.xlsx)|*.xlsx|(*.xlsm)|*.xlsm|(*.xlsb)|*.xlsb|(*.xlam)|*.xlam|(*.xltx)|*.xltx|(*.xltm)|*.xltm|(*.xls)|*.xls|(*.xla)|*.xla|(*.xlt)|*.xlt|(*.xlm)|*.xlm|(*.xlw)|*.xlw"
ofdGetWorksheet.FileName = ""
If ofdGetWorksheet.ShowDialog <> System.Windows.Forms.DialogResult.Cancel Then
Me.Text = ofdGetWorksheet.FileName
m_objExcel.Workbooks.Open(Me.Text)
tsbCopySheet.Enabled = True
tsbFindRows.Enabled = True
tsbClose.Enabled = True
m_objExcel.Visible = True
Else
Me.Text = ""
End If
Me.Activate()
End Sub
Any ideas??
I believe that I was having this issue as well, but this line of code (after your m_objExcel.visible statement) was what fixed it (going from memory).
m_objExcel.Windows(1).Visible = True
One thing you may want to look at is the Marshal.BindToMoniker method in the InteropServices library. I found this incredibly useful. If the file is not open, then it opens it. If it is open already, then it will use that instance. Like this...
wb = System.Runtime.InteropServices.Marshal.BindToMoniker(fileName) 'open the file. If the file is already open, then this uses the open file instead of trying to open again
exApp = wb.Parent
wb is my workbook variable, exApp is my Excel variable.
One other thing; to make sure Excel is closed and to remove those pesky processes from Task Manager
Private Sub KillExcel()
For Each RunningProcess In Process.GetProcessesByName("Excel")
RunningProcess.Kill()
Next
End Sub

VB.NET/WMI - Real-Time Windows Service Monitoring?

So there's an application at my work that installs several Windows services to a server. As a side project, I've been asked to make a simple GUI that will list these services with a "light" (a picture box with a red or green dot) next to the name of each service. The idea is that in the event these services were to stop running, the "light" would change from green to red.
I have the GUI part built, and I can query a remote server's services, then compare it to an array of the ones I'm interested and set the "light" next to each service to green/red depending on the service state. The part I'm hung up on is how to monitor these services in real time? Currently, I just have the following code in the Form_Load event:
Dim myConnectionOptions As New System.Management.ConnectionOptions
With myConnectionOptions
.Impersonation = System.Management.ImpersonationLevel.Impersonate
.Authentication = System.Management.AuthenticationLevel.Packet
End With
Try
Dim myManagementScope As System.Management.ManagementScope
myManagementScope = New System.Management.ManagementScope("\\" & SERVERNAME & "\root\cimv2", myConnectionOptions)
myManagementScope.Connect()
Dim query As New Management.ObjectQuery("SELECT * FROM Win32_Service")
Dim searcher As New Management.ManagementObjectSearcher(myManagementScope, query)
Dim i As Integer = 0
For Each queryObj As Management.ManagementObject In searcher.Get()
For Each service As String In arrServices
If queryObj("DisplayName").Equals(service) Then
If queryObj("State").Equals("Stopped") Then
arrLights(i).Image = My.Resources.redlight
End If
i += 1
End If
Next
Next
Catch err As Management.ManagementException
MessageBox.Show("WMI query failed with the following error: " & err.Message)
Catch unauthorizedErr As System.UnauthorizedAccessException
MessageBox.Show("Authentication error: " & unauthorizedErr.Message)
End Try
Would a simple timer that executes this code repeatedly be the best approach, or is there a more elegant solution? I have a little experience in VB.NET and WMI, but none in any type of real-time monitoring activity like this.
First of all i would put it into a thread, that way even if your connection times out you dont freeze your UI, then i would use a custom wait timer not the built in one as cross threading can be a pain.
wait timer:
Public Sub Wait(ByVal wait_time As Integer)
Dim time As Date
time = Now.AddMilliseconds(wait_time)
Do While time > Now
Application.DoEvents()
Loop
End Sub
example of threading:
Private services_check As Thread
private sub form1_load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
services_check = new thread(AddressOf 'Current code in a public sub')
services_cheack.IsBackground = True
Services_check.start()
It may not be the most elegant solution but its how i would do it, as for your current code im sorry i dont know enough about remote connections to help you.