Keep My.Settings Always VB.Net - vb.net

I have an application and I need to always have the my.settings saved. I notice that they get cleared whenever you change the files location, or run it on a different computer. Are there any ways to prevent this. It also happens when the application updates. Thanks.

Here is how you can retain your settings thru an upgrade:
(you also need to define a Project - Properties - Setting settings of 'ApplicationVersion' as string; you can start it off with an initial value of "not yet set")
Private Sub SetSettingsVersion()
Dim a As System.Reflection.Assembly = System.Reflection.Assembly.GetExecutingAssembly()
Dim appVersion As Version = a.GetName().Version
Dim appVersionString As String = appVersion.ToString
If My.Settings.ApplicationVersion <> appVersion.ToString Then
My.Settings.Upgrade()
My.Settings.ApplicationVersion = appVersionString
End If
End Sub

Related

How to detect if a software is installed and its installation path

I need to check, with my VB.net software, if a program has been installed before the execution of my software. I've looked around and I've found that I can make this by inspecting the Registry Editor in Windows. But how can I detect the installation path of this software? I mean, in my case the software is installed on the drive D and not on the C one so, if I map the C path, I'll receive an error. How could I get the sure installation path of it?
Hoping to have been as clearer as possible, thanks all are gonna answer me.
Best regards
Here are two examples of how to check a couple of things.
First, this is how one of my tools checks to see what version of itself is already installed. This isn't exactly what you're looking for, but don't worry - I'll get to that in just a minute:
Public Function GetSolutionVersion(MyApp As String)
Dim uninstallKey As String = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
Using rk As RegistryKey = Registry.LocalMachine.OpenSubKey(uninstallKey)
For Each skName In rk.GetSubKeyNames
Dim name As String = Registry.LocalMachine.OpenSubKey(uninstallKey).OpenSubKey(skName).GetValue("DisplayName")
If Not name Is Nothing Then
If name.ToString.ToLower = MyApp.ToLower Then
Try
Dim displayversion As String = Registry.LocalMachine.OpenSubKey(uninstallKey).OpenSubKey(skName).GetValue("DisplayVersion")
Return displayversion
Catch ex As Exception
Return ""
End Try
End If
End If
Next
End Using
Return ""
End Function
If you actually browse some of the entries in Uninstall, you'll see that some of them - but definitely not all - have an InstallLocation key that tells you right where to find the program. It depends on their particular installer. You may also be able to figure out the path by looking at the UninstallPath or UninstallString keys; again, it depends their installer.
As a second example, let's say we want to see if Firefox is installed - and where:
Private Sub CheckFirefox()
'Dim readValue = My.Computer.Registry.GetValue("HKEY_CURRENT_USER\Software\MyApp", "Name", Nothing)
'Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\firefox.exe
Dim HasFirefox as Boolean = False
Try
Dim regvalue = My.Computer.Registry.GetValue("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\firefox.exe", "", Nothing)
If regvalue <> Nothing Then
'Found firefox!
FirefoxFullPath = regvalue
HasFirefox = File.Exists(FirefoxFullPath)
If HasFirefox = False Then
MsgBox($"WARNING! Registry says Firefox is at ({regvalue}), but no such file was found")
Else
MsgBox($"Firefox found at {FirefoxFullPath}")
End If
Else
MsgBox("Firefox path not found in registry")
HasFirefox = False
End If
If HasFirefox = True Then
'Do something...
End If
Catch ex As Exception
MsgBox($"Unable to find Firefox in registry because an error occurred: {ex.ToString}",, "Error")
HasFirefox = False
End Try
End Sub
Knowing the right place to look in the Registry and how to get the right key values is the hardest part of it - hope this helps. However, not every piece of software is going to be listed in App Paths - it depends on whether they went through the registration process.
Finally, if none of that works, think about how the user is going to launch this particular program. If they're going to use either a desktop shortcut or menu entry, you could scan for those and retrieve its properties.

How do I select specific variables based on checkbox state as I iterate through a For Each

I'm working on a project that requires I iterate through a list of controls on a tabpage to find all of the checkboxes. Then depending on the state of the box (checked or unchecked) select individual variables (filenames) to then perform either a batch rename or delete of files on the filesystem (cb.checked = perform action).
I have managed to create the "for each" for the iteration of the controls (thanks google) but I'm struggling to figure out how to pick the variables. They are all named differently, obviously, as are the checkboxes. Also the checkboxes are statically assigned to the form/tabpage. Here's what I have at the moment.
Public Sub delBut_code(ByRef fname As String)
If (Sanity = 1) Then
For Each cb As Control In Form1.Controls
If TypeOf cb Is CheckBox AndAlso DirectCast(cb,
CheckBox).Checked Then
If My.Computer.FileSystem.FileExists(fname) Then
My.Computer.FileSystem.DeleteFile(fname)
End If
End If
Next
MessageBox.Show("All Actions Completed Successfully")
Else
MessageBox.Show("Please select a File To Delete")
End If
End Sub
and here is an example of some of the variables:
Dim castle As String = selPath & "\zm_castle_loadingmovie.txt"
Dim factory As String = selPath &
"\zm_factory_load_factoryloadingmovie.txt"
Dim island As String = selPath & "\zm_island_loadingmovie.txt"
N.B selpath collects a user entered folder path and can be ignored here
I would really appreciate any pointers.
First, you can do much better with the loop:
Public Sub delBut_code(ByRef fname As String)
If Sanity <> 1 Then
MessageBox.Show("Please select a File To Delete")
Exit Sub
End If
Dim checked = Form1.Controls.OfType(Of CheckBox)().Where(Function(c) c.Checked)
For Each box As CheckBox in checked
Try
'A file not existing is only one reason among many this could fail,
' so it needs to be in a Try/Catch block.
' And once you're using a Try/Catch block anyway,
' the FileExists() check becomes a slow and unnecessary extra trip to the disk.
My.Computer.FileSystem.DeleteFile(fname)
Catch
'Do something here to let the user know it failed for this file
End Try
Next
MessageBox.Show("All Actions Completed")
End Sub
But now you need to know how have the right value in that fname variable. There's not enough information in the question for us to fully answer this, but we can give some suggestions. There a number of ways you could do this:
Set the Tag property in the Checkboxes when you build the string variables. Then fname becomes DirectCast(box.Tag, String).
Inherit a custom control from CheckBox to use instead of a normal Checkbox that has an additional String property for the file name. Set this property when you build the string variables.
Name your string variables in a way that you can derive the string variable name from the CheckBox variable name, and then use a Switch to pick the right string variable from each box.Name.
Keep a Dictionary(Of CheckBox, String) that maps the Checkboxes to the right string values.
But without knowing more context of the application, I hesitate to recommend any of these over the others as best for your situation.

Using Filewatcher for a progress bar with subdirectories, Wont Update Properly

I'm trying to copy Files from a local Computer to a Network device. I'm trying to get a Progress Bar working for the File copy, and got it working for a Single Directory with no Subdirectory:
Private Sub CopyPictures()
Try
If Not Directory.Exists(DestinationPath) Then
My.Computer.FileSystem.CreateDirectory(DestinationPath)
End If
Dim counterLocalFiles = My.Computer.FileSystem.GetFiles(SourcePath)
UpdateProgressBarMaximum1(CInt(counterLocalFiles.Count))
UpdateLabelText2(CStr(counterLocalFiles.Count)) 'is a label which shows copied X files of Label2 Files
fsw1 = New IO.FileSystemWatcher(DestinationPath)
fsw1.EnableRaisingEvents = True
My.Computer.FileSystem.CopyDirectory(SourcePath, DestinationPath)
GetSettingsFromFile()
Catch Exec As System.IO.IOException
Dim dr As DialogResult = MessageBox.Show("Some Random Error Code", "Exception Title", MessageBoxButtons.OKCancel)
If (Not DialogResult.OK = dr) Then
Exit Sub
Return
End If
End Try
End Sub
Private Sub fsw1_Created(sender As Object, e As FileSystemEventArgs) Handles fsw1.Created
Dim counterRemoteFiles = My.Computer.FileSystem.GetFiles(DestinationPath)
UpdateProgressBar1(CInt(counterRemoteFiles.Count))
UpdateLabelText1(CStr(counterRemoteFiles.Count))
End Sub
The Update ObjectX Subs are just invoke Functions since the CopyPictures is raised by a backgroundworker as well looking all like this one for example
Private Sub UpdateProgressBar1(Value As Int32)
If ProgressBar1.InvokeRequired Then
ProgressBar1.Invoke(New Action(Of Integer)(AddressOf UpdateProgressBar1), Value)
Else
'We are on the UI thread so update the control.
ProgressBar1.Value = Value
End If
End Sub
This code works perfectly fine for me, but I have to deal with SubDirectories which contain the Images, and the names of the subs are random so i cant predetermine them so I came up with slight changes:
The Counter is looking now like this:
Dim counterLocalFiles = System.IO.Directory.GetFiles(SourcePath, "*.jpg*", SearchOption.AllDirectories).Length
UpdateProgressBarMaximum1(CInt(counterLocalFiles))
UpdateLabelText2(CStr(counterLocalFiles))
And this:
Dim counterRemoteFiles = IO.Directory.GetFiles(DestinationPath, "*.jpg", SearchOption.AllDirectories).Length
UpdateProgressBar1(CInt(counterRemoteFiles))
UpdateLabelText1(CStr(counterRemoteFiles))
And I added:
fsw1.IncludeSubdirectories = True
Now the weired Problems started: It would properly count the file in the source Directory setting label2 to the correct amount of files in all subdirectories and then start copying. It would NOT update the Progressbar though in real time. It just updated it once when it was done with the first directory and just adding the amount of files to it which it contained. After that it completly stoppedd nored the second directory and didn't add that at all to the progressbar. What am I doing wrong here? I hope my english is fine, If you have any question or If I was not clear enough, please let me know. Thank you
You don't have an event consumer that triggers your progressbar update routine - you call it once when your filesystemwatcher is instantiated.
You need to declare an event that handles the copy event and fires off your progress update code. Because Filesystemwatcher cannot monitor network drives, you may want to declare an event that fires off your progress update method when the counterRemoteFiles count increments.
Turns out I just made a mistake with correctly putting the
fsw1.IncludeSubdirectories = True
I was setting it to true in the Form Editor instead of doing it in the code. Once i actually put that in the code after initialising the fsw, it would work just fine

VB 2013 Application Out of Memory

I'm new to VB but recently created my first working app :) Anyway it just compresses files and little bit more. The latest thing that I added was a marquee style progress bar to animate while the operation was in progress and stop when it ends and the user can do the next zip operation. The progress bar wasn't updating, so I used a background worker to do the actual task while the button click just did the animation. Since then I've notcied serious degredation in the app. It struggles to load. I even got an out of memory error. Not sure if the background worker is related, but I thought I'd mention as it was the last update. Has anyone experienced anything similar? If I can provide and specific info, please ask me for it! Many thanks.
UPDATE: So I understand that I'm not using the BGWorker correctly. I will change that. But I found even with that removed, I still had issues. So I created a new form and started adding in bits of my code one by one. Anyway, I fell at the first hurdle with my form load sub. So I added that in slowly. I found that when ever I have any statements that load a variable from settings for persistent settings that the app falls over. Below is my code. Can anyone see what's up?????
UPDATE: I've found that if I load from settings the memory useage shoots up. I tried this too with saving settins on form closed. Below is the error received. The same out of memory occurs when trying to load settings too. I never experienced this on the first form I created. So perhaps I have missed some settings on the second, because the implementation in the code hasn't changed.
System.Configuration.ConfigurationErrorsException: Failed to save settings: An error occurred executing the configuration section handler for userSettings/Backup_Tool.My.MySettings. ---> System.Configuration.ConfigurationErrorsException: An error occurred executing the configuration section handler for userSettings/Backup_Tool.My.MySettings. ---> System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown
This is when I added in the code below:
Private Sub Form1_Closed(sender As Object, e As EventArgs) Handles MyBase.FormClosed
' TAB PAGE 1.
' Save controls to settings.
My.Settings.StartPathTextBox1 = StartPathTextBox1.Text
My.Settings.ZipPathTextBox1 = ZipPathTextBox1.Text
My.Settings.CopyPathTextBox1 = CopyPathTextBox1.Text
My.Settings.ZipSelectCheckBox1 = ZipSelectCheckBox1.Checked
My.Settings.CopySelectCheckBox1 = CopySelectCheckBox1.Checked
For Each s As String In StartNameListBox1.Items()
My.Settings.StartNameListBoxItems1.Add(s)
Next
For Each s As String In StartNameListBox1.SelectedItems()
My.Settings.StartNameListBoxSelectedItems1.Add(s)
Next
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' FORM 1.
' Initialise specialised string collections.
If My.Settings.StartNameListBoxItems1 Is Nothing Then
My.Settings.StartNameListBoxItems1 = _
New System.Collections.Specialized.StringCollection
End If
If My.Settings.StartNameListBoxSelectedItems1 Is Nothing Then
My.Settings.StartNameListBoxSelectedItems1 = _
New System.Collections.Specialized.StringCollection
End If
' TAB PAGE 1.
' Restore controls from saved settings.
StartPathTextBox1.Text() = My.Settings.StartPathTextBox1
ZipPathTextBox1.Text() = My.Settings.ZipPathTextBox1
CopyPathTextBox1.Text() = My.Settings.CopyPathTextBox1
ZipSelectCheckBox1.Checked = My.Settings.ZipSelectCheckBox1
CopySelectCheckBox1.Checked = My.Settings.CopySelectCheckBox1
For Each s As String In My.Settings.StartNameListBoxItems1()
StartNameListBox1.Items.Add(s)
Next
For Each s As String In My.Settings.StartNameListBoxSelectedItems1()
StartNameListBox1.SelectedItems.Add(s)
Next
' Decide controls initial states.
If StartNameListBox1.SelectedItems.Count = 0 Then
ZipSelectCheckBox1.Enabled = False
RunButton1.Enabled = False
End If
If ZipSelectCheckBox1.Checked = False Then
ZipPathTextBox1.Enabled = False
ZipBrowseButton1.Enabled = False
End If
If ZipPathTextBox1.Text = String.Empty Then
CopySelectCheckBox1.Enabled = False
End If
If CopySelectCheckBox1.Checked = False Then
CopyPathTextBox1.Enabled = False
CopyBrowseButton1.Enabled = False
End If
End Sub
It appears to be that you are only ever adding the current selections to the Settings collections. You might well clear the ListBox when they make new selections, but you do not do the same thing with the Settings Collections like My.Settings.StartNameListBoxItems1:
For Each s As String In StartNameListBox1.Items()
My.Settings.StartNameListBoxItems1.Add(s)
Next
The collection will have all the items in it from all the other times it has ever run already and you are now going to add more to it. Eventually you will have many, many, many items in it.
My.Settings.StartNameListBoxItems1.Clear ' REMOVE ALL OLD ITEMS
' Save just the current items to the collection
For Each s As String In StartNameListBox1.Items()
My.Settings.StartNameListBoxItems1.Add(s)
Next
use .Clear on both Collections

Download URL Contents Directly into String (VB6) WITHOUT Saving to Disk

Basically, I want to download the contents of a particular URL (basically, just HTML codes in the form of a String) into my VB6 String variable. However, there are some conditions.
I know about the URLDownloadToFile Function - however, this requires that you save the downloaded file/HTML onto a file location on disk before you can read it into a String variable, this is not an option for me and I do not want to do this.
The other thing is, if I need to use an external library, it must already come with all versions of Windows from XP and onwards, I cannot use a control or library that I am required to ship, package and distribute even if it is free, this is not an option and I do not want to do this. So, I cannot use the MSINET.OCX (Internet Transfer) Control's .OpenURL() function (which simply returns contents into a String), as it does not come with Windows.
Is there a way to be able to do this with the Windows API, URLMON or something else that is pre-loaded into or comes with Windows, or a way to do it in VB6 (SP6) entirely?
If so, I would appreciate direction, because even after one hour of googling, the only examples I've found are references to URLDownloadToFile (which requires saving on disk before being ale to place into a String) and MsInet.OpenURL (which requires that I ship and distribute MSINET.OCX, which I cannot and don't want to do).
Surely there has got to be an elegant way to be able to do this? I can do it in VB.NET without an issue, but obviously don't have the luxury of the .NET framework in VB6 - any ideas?
Update:
I have found this: http://www.freevbcode.com/ShowCode.asp?ID=1252
however it says that the displayed function may not return the entire
page and links to a Microsoft bug report or kb article explaining
this. Also, I understand this is based off wininet.dll - and I'm
wondering which versions of Windows does WinInet.dll come packaged
with? Windows XP & beyond? Does it come with Windows 7 and/or Windows
8?
This is how I did it with VB6 a few years ago:
Private Function GetHTMLSource(ByVal sURL As String) As String
Dim xmlHttp As Object
Set xmlHttp = CreateObject("MSXML2.XmlHttp")
xmlHttp.Open "GET", sURL, False
xmlHttp.send
GetHTMLSource = xmlHttp.responseText
Set xmlHttp = Nothing
End Function
If you want to do this with pure VB, and no IE, then you can take advantage of a little-used features of the VB UserControl - async properties.
Create a new UserControl, and call it something like UrlDownloader. Set the InvisibleAtRuntime property to True. Add the following code to it:
Option Explicit
Private Const m_ksProp_Data As String = "Data"
Private m_bAsync As Boolean
Private m_sURL As String
Public Event AsyncReadProgress(ByRef the_abytData() As Byte)
Public Event AsyncReadComplete(ByRef the_abytData() As Byte)
Public Property Let Async(ByVal the_bValue As Boolean)
m_bAsync = the_bValue
End Property
Public Property Get Async() As Boolean
Async = m_bAsync
End Property
Public Property Let URL(ByVal the_sValue As String)
m_sURL = the_sValue
End Property
Public Property Get URL() As String
URL = m_sURL
End Property
Public Sub Download()
UserControl.AsyncRead m_sURL, vbAsyncTypeByteArray, m_ksProp_Data, IIf(m_bAsync, 0&, vbAsyncReadSynchronousDownload)
End Sub
Private Sub UserControl_AsyncReadComplete(AsyncProp As AsyncProperty)
If AsyncProp.PropertyName = m_ksProp_Data Then
RaiseEvent AsyncReadComplete(AsyncProp.Value)
End If
End Sub
Private Sub UserControl_AsyncReadProgress(AsyncProp As AsyncProperty)
If AsyncProp.PropertyName = m_ksProp_Data Then
Select Case AsyncProp.StatusCode
Case vbAsyncStatusCodeBeginDownloadData, vbAsyncStatusCodeDownloadingData, vbAsyncStatusCodeEndDownloadData
RaiseEvent AsyncReadProgress(AsyncProp.Value)
End Select
End If
End Sub
To use this control, stick it on a form and use the following code:
Option Explicit
Private Sub Command1_Click()
XDownload1.Async = False
XDownload1.URL = "http://www.google.co.uk"
XDownload1.Download
End Sub
Private Sub XDownload1_AsyncReadProgress(the_abytData() As Byte)
Debug.Print StrConv(the_abytData(), vbUnicode)
End Sub
Suffice to say, you can customise this to your hearts content. It can tell (using the AyncProp object) whether the file is cached, and other useful information. It even has a special mode in which you can download GIF, JPG and BMP files and return them as a StdPicture object!
One alternative is using Internet Explorer.
Dim ex As InternetExplorer
Dim hd As HTMLDocument
Dim s As String
Set ex = New InternetExplorer
With ex
.Navigate "http://donttrack.us/"
.Visible = 1
Set hd = .Document
s = hd.body.innerText ' assuming you just want the text
's = hd.body.innerHTML ' if you want the HTML
End With
EDIT: For the above early binding to work you need to set references to "Microsoft Internet Controls" and "Microsoft HTML Object Library" (Tools > References). You could also use late binding, but to be honest, I forget what the proper class names are; maybe someone smart will edit this answer :-)