Vb .net memory leak? - vb.net

I have a class (too complex to post code), which I think may have a memory leak. However the process workingset and virtual memory size suggest otherwise.
I create and destroy the class like this:
Dim vdt As ValidateDTC
Dim prc As Process = Process.GetCurrentProcess
For I As Integer = 1 To 100
Debug.Print("1:" & "WorkingSet64:" & prc.WorkingSet64.ToString & " VirtualMemorySize64:" & prc.VirtualMemorySize64.ToString)
vdt = New ValidateDTC
Debug.Print("2:" & "WorkingSet64:" & prc.WorkingSet64.ToString & " VirtualMemorySize64:" & prc.VirtualMemorySize64.ToString)
vdt = Nothing
Debug.Print("3:" & "WorkingSet64:" & prc.WorkingSet64.ToString & " VirtualMemorySize64:" & prc.VirtualMemorySize64.ToString)
Next i
Once the class is created for the first time the workingset and virtualmemorysize are the same throughout the test.
My problem is that the debugger "Process Memory" graph climbs throught the test
If I put a break in the loop and snapshot the memory I can see all sorts of numbers increasing.
The class will ultimately be used in threads (via background worker).
Is there a problem, or not.

Related

Get Unique identifier of a USB stick in VB.net

From what I have read and tried myself I am starting to think this is not possible; but if you don't don't ask you don't get...
I am making a tool that formats USB sticks for staff at work (it adds branding, instructions, handbooks etc). Whilst doing this I am attempting to store the "serial/Unique identifier" for the device in a database so that should I find a lost memory stick I can find out who the original owner was, even if it has been reformated!
I got this snippet from another answer on SO
Dim driveNames As New List(Of String)
For Each drive As DriveInfo In My.Computer.FileSystem.Drives
Try
Dim fso As Scripting.FileSystemObject
Dim oDrive As Scripting.Drive
fso = CreateObject("Scripting.FileSystemObject")
oDrive = fso.GetDrive(drive.Name)
ListBox1.Items.Add(drive.Name & " " & oDrive.SerialNumber)
Catch ex As Exception
End Try
Next
End Sub
This returns a number, but that changes when the drive is formatted.
I also tried modifying a snippet I got from online (CodeProject) I think.
'Check for valid drive letter argument.
ToolStripStatusLabel1.Text = "Fetching Device Serial ('" & DriveLetter & "' Validating drive letters)"
Dim ValidDriveLetters As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
If ValidDriveLetters.IndexOf(DriveLetter) <> -1 Then
If DriveLetter.Length = 1 Then
ToolStripStatusLabel1.Text = "Fetching Device Serial ('" & DriveLetter & "' Creating Disk Management Object)"
Dim Disk As New System.Management.ManagementObject("Win32_LogicalDisk.DeviceID=""" & DriveLetter & ":""")
ToolStripStatusLabel1.Text = "Fetching Device Serial ('" & DriveLetter & "' Creating Disk Property)"
Dim DiskProperty As System.Management.PropertyData
For Each DiskProperty In Disk.Properties
ToolStripStatusLabel1.Text = "Fetching Device Serial ('" & DriveLetter & "' Reading " & DiskProperty.Name & ": " & DiskProperty.Value & ")"
'Threading.Thread.Sleep(1000)
If DiskProperty.Name = "VolumeSerialNumber" Then
Return DiskProperty.Value.ToString '.ToString 'Return the volume serial number.
End If
Next DiskProperty
End If
End If
Return Nothing 'Invalid drive letter.
This currently returns the volume serial (best result I could get right now), that obviously changes when the drive is formatted.
I have looked through the property names but I am yet to find something to uniquely identify the drive itself rather than its volumes.
Is there a property that I can read that would be unique to each device? (even the same the manufacturer/model)
OR
Am I going about this the wrong way? I also considered partitioning the USBs with a hidden partition and then storing the volume serial of that. If an end user formats the drive via Explorer they are only going to wipe the visible partition rather than my secret hidden one...but this seems like a workaround rather than an actual solution?
Please note, I am not a VB.net wizard, web is more my thing (I may need some hand holding at times - but I try my best!)

VB.NET Constantly polling a directory for files is causing an ongoing increase in memory usage

I have a small application that on startup instantiates a class, reads in a JSON array from a text file and builds an object. It then initialises a new system.timers.timer and scans a directory every xx minute(s) to get a file count.
When my application starts up, Windows Task Manager shows it at around 13MB of Memory, however, leaving it to run for about 12 hours now shows it at 700+MB of Memory when no files were even detected in this directory. I'm worried that this is just going to grow forever and eventually crash. It doesn't seem like anything is getting cleaned up by the GC (probably because my timer and objects are always active).
Although this class is quite simple in theory, I do have other functions and timers involved that don't get called until required. I am happy to post all my code somewhere, or even just the class, but it's a little too long to add to this post.
I have included the main elements of my class however:
Public Sub Initialize()
Try
If AgentSettings.DebugMode Then CreateLog("Entered Module: Initialize()")
CreateLog("Initializing Alerting Rule: <" & Me.Name & ">")
CreateLog("Validating all Rule Profiles")
'Loop through all the Rule Profiles and make sure they are all valid
'If not, disable the profile so it never scans
For Each profile As RuleProfile In Me.RuleProfiles
CreateLog("Verifying Profile: <" & profile.Name & ">")
'Check to make sure the Profile is functionally valid
If ProfileIsValid(profile) Then
'Profile is valid so setup an event handler to handle the isBroken event
profile.Active = True
CreateLog("Rule Profile: <" & profile.Name & "> Enabled")
AddHandler profile.BrokenStatusChanged, AddressOf CheckIfRuleIsBroken
Else
'Disable the Profile
CreateLog("Rule Profile: <" & profile.Name & "> Disabled")
profile.Active = False
End If
Next
'Setup the checking timer and set the interval to the default checking time
If Me.ruleCheckTimer Is Nothing Then
Me.ruleCheckTimer = New System.Timers.Timer
Me.ruleCheckTimer.AutoReset = True
Me.ruleCheckTimer.Interval = Me.DefaultMinutesToCheck * 60000
AddHandler Me.ruleCheckTimer.Elapsed, AddressOf CheckRule
End If
'If there is an escalation plan then setup a timer for it
If Me.EscalationPlan.PlanItems.Count > 0 Then
'Setup the escalation timer and set the interval to the first plan item
If Me.escalationTimer Is Nothing Then
Me.escalationTimer = New System.Timers.Timer
Me.escalationTimer.AutoReset = True
AddHandler Me.escalationTimer.Elapsed, AddressOf Escalate
End If
End If
Catch ex As Exception
CreateLog("Module: Initialize()" & vbNewLine & "Exception Error: " & ex.Message)
End Try
End Sub
Private Sub CheckRule(ByVal sender As Object, ByVal e As EventArgs)
Try
If AgentSettings.DebugMode Then CreateLog("Entered Module: CheckRule()")
'Loop through all the profiles
For Each profile As RuleProfile In Me.RuleProfiles
CreateLog("Scanning for active profiles")
'Make sure the Profile is active
If profile.Active Then
Select Case profile.Name
Case "FileCount"
CreateLog("Checking Rule Profile: <" & profile.Name & "> for isBroken status")
'Get file count of the specified directory
Dim files As String()
files = Directory.GetFiles(profile.DirectoryPath)
'Determine if we're checking for files greater than or less than
If profile.GreaterThan Then
If files.Count > profile.FileCount Then
profile.isBroken = True
Else
profile.isBroken = False
End If
Else
If files.Count < profile.FileCount Then
profile.isBroken = True
Else
profile.isBroken = False
End If
End If
files = Nothing
End Select
End If
Next
Catch ex As Exception
CreateLog("Module: CheckRule()" & vbNewLine & "Exception Error: " & ex.Message)
End Try
End Sub
When profile.isBroken receives an update, it triggers an event which then loops through all profiles to determine which ones are broken. If all are broken, then it begins its alerting and escalation process. However, none of this is occurring and my memory is still increasing constantly.
Can anyone see anything wrong with this? Or maybe a better way (and more efficient way) of polling this directory?
The theory behind my application is to instantiate multiple classes to allow various directories or files to be scanned, but if the memory has increased this much with just the one instance, I'm concerned about running multiple.
EDITED
My log handling is as follows:
Public Sub CreateLog(ByVal text As String)
Try
'Create Log Directory if it doesn't exist
If (Not System.IO.Directory.Exists(GetFolderPath(SpecialFolder.CommonApplicationData) & "\MyCompany\Agent\Log")) Then System.IO.Directory.CreateDirectory(GetFolderPath(SpecialFolder.CommonApplicationData) & "\MyCompany\Agent\Log\")
'Write log entry
LogFile = GetFolderPath(Environment.SpecialFolder.CommonApplicationData) & "\MyCompany\Agent\Log\" & "Logfile - " & Format(Now, "ddMMyyyy") & ".txt"
File.AppendAllText(LogFile, DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss") & " " & text & Environment.NewLine)
'Update the activity text on the main UI
frmMain.UpdateActivityText(text)
Catch ex As Exception
'No need to create a log entry
End Try
End Sub

How to read and write to controls or settings with Visual Basic

I am copying the string values out of text boxes on a form and saving them in the settings. The way I am doing it here seems kind of long handed. Is there a way to reference the TextBox1.Text and the My.Settings.Value1 with a string. If so then I could just loop through and keep changing the strings to point at the different controls. See the way I am currently doing it.
My.Settings.F1LabelCol0Save = F1LabelCol0.Text
My.Settings.F1LabelCol1Save = F1LabelCol1.Text
My.Settings.F1LabelCol2Save = F1LabelCol2.Text
My.Settings.F1LabelCol3Save = F1LabelCol3.Text
My.Settings.F1LabelCol4Save = F1LabelCol4.Text
My.Settings.F1LabelCol5Save = F1LabelCol5.Text
My.Settings.F1LabelCol6Save = F1LabelCol6.Text
My.Settings.F1LabelCol7Save = F1LabelCol7.Text
My.Settings.F1LabelCol8Save = F1LabelCol8.Text
My.Settings.F1LabelCol9Save = F1LabelCol9.Text
You can access both settings and controls dynamically via My.Settings.Item() and Me.Controls.Item().
I present you with two options:
1) Use a For loop for a fixed number range:
For x = 0 To 9
My.Settings("F1LabelCol" & x & "Save") = Me.Controls("F1LabelCol" & x).Text
Next
Upside: Does not swallow exceptions (see next example).
Downside: You must change the upper bound (currently 9) when you add new settings/controls.
2) Use a While loop for a dynamic number range.
Dim x As Integer = 0
While True
Try
My.Settings("F1LabelCol" & x & "Save") = Me.Controls("F1LabelCol" & x).Text
Catch
Exit While 'If an exception is thrown we've most likely hit the setting/control limit.
End Try
End While
Upside: Dynamic number range, you do not need to change anything when adding new settings/controls.
Downside: Swallows exceptions, i.e. you won't know when an exception is thrown for another reason than when a setting/control does not exist.
If you want to load data dynamically as well just reverse the get/set operation:
Me.Controls("F1LabelCol" & x).Text = My.Settings("F1LabelCol" & x & "Save")
I did some more research and here is how to do it. Now obviously it needs to be a number of of them very similarly named to to be worth it.
For i = 0 To 39
My.Settings("F1LabelCol" & i.ToString & "Save") = Me.Controls("F1LabelCol" & i.ToString).Text
Next

Use combobox in savepath

first of all: Sorry for the not so clear title. I didn't know a better way to descripe my question.
I'm building a application that has to save user-specified data to a sdcard on a plc.
I already found out how to connect to that plc but am still working on the saving part.
For the testing i just used:
ds.WriteXml("C:\" & DateTimePicker1.Text & ".xml")
I think it's possible to change it to \192.168.2.16\SDcard\filename but that's not very flexible.
What i would like to have is the ability to take the value from a combobox and use that as the ip adress.
What is the best way to do this? as i don't think it's a simpe thing like making the savepad
(\" & comboIP.selectedvalue & "\Sdcard\" & DateTimePicker1.Text & ".xml") Unfortunately, the SD card is still on it's way so i can't test it yet..
Thanks in advance!
ds.WriteXml("C:\" & comboIP.Text & "\SDCard\" & DateTimePicker1.Text & ".xml")
That works just fine.
You don't really need the SDCard in hand to test this out.
You can simply create temporary variables before the WriteXML function call, set a breakpoint on them, and ensure that they are the correct values beforehand.
e.g.:
Dim sSelectedIP As String = comboIP.Text
Dim sDateTimePicker As String = DateTimePicker1.Text
Dim sCompleteDirectory As String = "C:\" & sSelectedIP & "\SDCard"
If My.Computer.FileSystem.DirectoryExists(sCompleteDirectory) = False Then
My.Computer.FileSystem.CreateDirectory(sCompleteDirectory)
End If
ds.WriteXml(sCompleteDirectory & "\" & sDateTimePicker & ".xml")

Unable to diligently close the excel process running in memory

I have developed a VB.Net code for retrieving data from excel file .I load this data in one form and update it back in excel after making necessary modifications in data. This complete flow works fine but most of the times I have observed that even if I close the form; the already loaded excel process does not get closed properly.
I tried all possible ways to close it but could not be able to resolve the issue. Find below code which I am using for connecting to excel and let me know if any other approach I may need to follow to resolve this issue.
Note: I do not want to kill the excel process as it will kill other instances of the excel
Dim connectionString As String
connectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & ExcelFilePath & ";
Extended Properties=excel 8.0; Persist Security Info=False"
excelSheetConnection = New ADODB.Connection
If excelSheetConnection.State = 1 Then excelSheetConnection.Close()
excelSheetConnection.Open(connectionString)
objRsExcelSheet = New ADODB.Recordset
If objRsExcelSheet.State = 1 Then objRsExcelSheet.Close()
Try
If TestID = "" Then
objRsExcelSheet.Open("Select * from [" & ActiveSheet & "$]", excelSheetConnection, 1, 1)
Else
objRsExcelSheet.Open("Select Test_ID,Test_Description,Expected_Result,Type,UI_Element,Action,Data,Risk from [" & ActiveSheet & "$] WHERE TEST_Id LIKE '" & TestID & ".%'", excelSheetConnection, 1, 1)
End If
getExcelData = objRsExcelSheet
Catch errObj As System.Runtime.InteropServices.COMException
MsgBox(errObj.Message, , errObj.Source)
Return Nothing
End Try
excelSheetConnection = Nothing
objRsExcelSheet = Nothing
You are using the old VB6 COM interfaces. There isn't any evidence in your code snippet of you ever calling Close() on the RecordSet. You can't call Close() on the Connection since you set it to Nothing.
As written, Excel won't exit until the garbage collector runs and the finalizer thread releases the reference counts on the COM interfaces. Which can take a long time if your program doesn't exit or goes dormant after processing the data. You really should consider uplifting this code to use the classes in System.Data.Oledb so you can properly Dispose() everything when you're done with the objects.
The Q&D solution is to force the finalizer thread to run:
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
Run this after you're done using the RecordSet. It isn't otherwise recommended.