The config file is the preferred location for application configuration data these days. .NET apps will use it themselves to store data in various situations. One such situation that many people may have encountered is when you create a Data Source the wizard offers to store the connection string in the config file. This is so that you can simply edit the config file when the app is installed to customise the connection for that system. One issue here is, what if the connection string contains a password? The config file is just plain text so its visible to the world. The answer is encryption and it's already built into the .NET configuration mechanism.
Try running the attached project and then opening the config file from the bin\Debug folder in VS. Notice that there's a <connectionStrings> section and it contains an item named PrimaryConnectionString. Now, select Tools -> Options from the menu and see that the components of that connection string are displayed as individual fields. Try editing those fields and then clicking OK. You don't actually have to have SQL Server installed to do this but if you want to use the drop-down lists to select a server and database you will need it. Go back to VS and you should be prompted to reload the config file. Do so and note that the values you entered now appear in the stored connection string.
Next, select Tools -> Encrypt Connection Strings from the menu. Go back to VS and reload the config file again and observe the changes. Note that your actual connection string is nowhere to be seen. Now, if you select Tools -> Options again at this point an exception will be thrown. I'm not sure exactly why and i'm not sure how to fix it, but that isn't really a problem anyway. This error only occurs when you start the app with the <connectionStrings> section unencrypted, then encrypt it, then try to read a connection string via My.Settings. This should never occur so the error should never appear.
So, close the app and then run it again. Select Tools -> Options from the menu and observe that the components of the connection string are displayed again, even though they cannot be seen in the config file. There was no extra code needed to accomplish this. The user code is exactly the same to read the connection string whether the config file is encrypted or not. the decryption is handled on-the-fly by the Framework.
Now, edit the connection properties again and click OK. Go back to VS and reload the config file again and observe that the long strings of gibberish have changed. Those strings contain your data in encrypted format. Now, select Tools -> Decrypt Connection Strings from the menu and go back to VS and reload the config file again. Note that your connection string is now visible again and the changes you made are reflected. Go back to the app and select Tools -> Options again and observer that, using the same code again, the connection properties have been read from the unencrypted config file. Tada!
N.B. - I've removed the original attachment, which was a VS 2008 project, and attached a VS 2005 version in its place.
Last edited by jmcilhinney; Oct 26th, 2008 at 05:50 AM.
To continue, that all demonstrates encrypting the <connectionStrings> section. You can encrypt other sections too. In the code for the OptionsDialogue in the project find the Load event handler, which looks like this:
vb.net Code:
Private Sub OptionsDialogue_Load(ByVal sender As Object, _
ByVal e As EventArgs) Handles MyBase.Load
Me.LoadConnectionString()
'Me.LoadAppSettings()
End Sub
and change it to this:
vb.net Code:
Private Sub OptionsDialogue_Load(ByVal sender As Object, _
ByVal e As EventArgs) Handles MyBase.Load
'Me.LoadConnectionString()
Me.LoadAppSettings()
End Sub
Likewise find the okButton.Click event handler and change this part:
vb.net Code:
configSaved = Me.SaveConnectionString
'configSaved = Me.SaveAppSettings
to this:
vb.net Code:
'configSaved = Me.SaveConnectionString
configSaved = Me.SaveAppSettings
You can now perform the same steps as before but note that the connection properties are being retrieved from, and stored to, individual elements within the <appSettings> section. As such, use the Encrypt App Settings and Decrypt App Settings items from the Tools menu to encrypt and decrypt the config file.
Note that in this case you can still read the config file successfully even if encryption was applied after opening the app. Like I said though, this situation should never arise in the real world.
So, speaking of the real world, how would you use this? Well, normally if you want to encrypt the config file then you'd want it encrypted all the time. As such you would use a Setup project to install your app and then you'd include the encryption code in a Custom Action, so the config file is encrypted when the app's installed. because you don't need to make any code changes to read and write an encrypted config file, you can read and write the unencrypted config file while you're developing and then read and write the encrypted config file when the app's deployed without having to care which is which.
Note that what I've shown is for WinForms apps. ASP.NET apps are very similar except that you don't perform the encryption and decryption in code. You encrypt a web.config file using the aspnet_regiis utility at the commandline. That's basically because web apps get installed once only, and it's not going to be the end user who installs it.
hcan you please provide, is there available version for 2005? im receiving this error "the selected file is a solution file, but was created by newer version of this application or cannot be opened."
hcan you please provide, is there available version for 2005? im receiving this error "the selected file is a solution file, but was created by newer version of this application or cannot be opened."
tnx
Go back and read the edit I added to the end of the first post.
Dim builder As New SqlConnectionStringBuilder(My.Settings.PrimaryConnectionString)
...
end sub
i dont understand the line and its produce an error. i change the argument to
Code:
Dim builder As New SqlConnectionStringBuilder("Data Source=ConnectionStringsServer;Initial Catalog=ConnectionStringsDatabase;User ID=ConnectionStringsUserName;Password=ConnectionStringsPassword")
the program run but i was not able to encrypt/decrypt app.config
Last edited by jlbantang; Jul 23rd, 2008 at 11:58 PM.
hi jm. the prog runs but the app.config were not encrypted/decrypted. your code which i replace might have something to do w/ this.
your code:
vb Code:
Dim builder As New SqlConnectionStringBuilder(My.Settings.PrimaryConnectionString)
what was this line for anyway?
replaced with this:
vb Code:
Dim builder As New SqlConnectionStringBuilder("Data Source=ConnectionStringsServer;Initial Catalog=ConnectionStringsDatabase;User ID=ConnectionStringsUserName;Password=ConnectionStringsPassword")
hi jm. the prog runs but the app.config were not encrypted/decrypted. your code which i replace might have something to do w/ this.
your code:
vb Code:
Dim builder As New SqlConnectionStringBuilder(My.Settings.PrimaryConnectionString)
what was this line for anyway?
replaced with this:
vb Code:
Dim builder As New SqlConnectionStringBuilder("Data Source=ConnectionStringsServer;Initial Catalog=ConnectionStringsDatabase;User ID=ConnectionStringsUserName;Password=ConnectionStringsPassword")
That line is to load the stored connection string via My.Settings. Likely you have to actually add the connection string to the settings first. Just impoting the config file is probably not enough. Go to the Settings tab of your project properties and check if there's a connection string listed there. If there isn't then you'll need to add one, so you'll need to delete the existing one from the config file first.
N.B. - This solution was created in VS 2008, so you will not be able to open it in VS 2005, even though it targets .NET 2.0 and all the code will work. If you're using VS 2005 then you have two choices:
1. Edit the SLN and VBPROJ files so they become compatible with VS 2005. Do NOT try this if you don't absolutely know what you're doing.
2. Create a new VS 2005 solution and use Add Existing Item to import the items from my solution. I think you should be able to import everything but, if not, only a small amount of hand-editing will be required.
Thanks jmc. This is seams to be a well done post, but I was wondering if anyone actually been able successfully import the code into VB2005? I have been working on this for hours and I obviously do not have enough VB-brain power to correct all the import issues.
can you post also the vb2008 version of this code ! thanks
The code is exactly the same. I only attached a VS 2005 project because VS 2005 users couldn't open a VS 2008 project. The converse is not the case so you can simply open the attached project as you normally would and say yes when VS offers to upgrade it to VS 2008 format.
Hi Jmc,
Recently found your example and found it fascinating, I wanted to see if I could use certain principles found in your example in a vb.net 4.0, WPF application in order to learn but I seem to be stuck and wondered if you could help me. Now although I am able to save custom connection strings to the YOUR_APP.exe.config file, I am not able to retrieve the saved connection string in the same way you do in your example, instead the default ConnectionStringsPassword etc... values show up instead.
This is the example which I was trying to figure out (Works perfect)
Code:
Private Sub LoadConnectionString()
'Load the current connection string.
Dim builder As New SqlConnectionStringBuilder(My.Settings.PrimaryConnectionString)
'Display individual connection string properties.
Me.serverCombo.Text = builder.DataSource
Me.integratedSecurityOption.Checked = builder.IntegratedSecurity
Me.sqlServerSecurityOption.Checked = Not builder.IntegratedSecurity
Me.userText.Text = builder.UserID
Me.passwordText.Text = builder.Password
Me.databaseCombo.Text = builder.InitialCatalog
End Sub
This is my simplified code that I have built from your example (Which still shows default values that are in the app.config and settings wizard in VS and not the custom values in YOUR_APP.exe.config.)
Code:
Private Sub Loadconstr()
'Load the current connection string
Dim csb As New SqlConnectionStringBuilder(My.Settings.PrimaryConnectionString)
'displays individual constr propertys from the app.config.
Me.start1_Cmbo_InstNam.Text = csb.DataSource
start1_Userid_Cmbox.Text = csb.UserID
start1_cmbo_pass.Text = csb.Password
End Sub
I haven't looked at this code for a long time but my first guess would be that you need to reload the settings from the config file. My.Settings has a Reload method or something like that, so you might try that.
ok I'll give that a shot and let you know. Would I put the my.settings.reload in the first line of the loadconstring sub or elsewhere?
You would put it wherever it's appropriate to reload your settings. After making changes would be the logical option, so that you know immediately that My.Settings contains the most current data.
On second look I found the my.settings.reload under the saveconstr(), I'll post my whole code here, I'd really, really, appreciate it if you could look over it quick and see if anything stands out to you, like I stated before the user inputted connection string values save to YOUR_APP_NAME.exe.config and asks to refresh in VS but the code just wont pull the saved info from YOUR_APP_NAME.exe.config back to the textboxes. It seems to only pull values from app.config or the settings page, under project propertys, it sucks to be this close but yet still so far away.
Code:
Imports System.Data
Imports System.Data.Sql
Imports System.Data.SqlClient
Imports System.Configuration
Class Start1
Private Sub Button6_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button6.Click
Call saveconstr()
End Sub
Private Sub Start1_Loaded(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Loaded
Call Loadconstr()
End Sub
'THIS IS WHERE I HAVE THE PROBLEM, LOADS ONLY DEFAULT VALUES.
Private Sub Loadconstr()
'Load the current connection string
Dim csb As New SqlConnectionStringBuilder(My.Settings.PrimaryConnectionString)
Me.start1_Cmbo_InstNam.Text = csb.DataSource
start1_Userid_Cmbox.Text = csb.UserID
start1_cmbo_pass.Text = csb.Password
End Sub
Private Sub start1_Cmbo_InstNam_DropDownOpened(sender As Object, e As EventArgs) Handles start1_Cmbo_InstNam.DropDownOpened
Dim Servertable As DataTable = SqlDataSourceEnumerator.Instance.GetDataSources()
Dim upperbound As Integer = Servertable.Rows.Count - 1
Dim servernames(upperbound) As String
For index As Integer = 0 To upperbound
If Servertable.Rows(index).IsNull("InstanceName") Then
servernames(index) = CStr(Servertable.Rows(index)("ServerName"))
Else
servernames(0) = String.Format("{0}\{1}", _
Servertable.Rows(0)("ServerName"), _
Servertable.Rows(0)("InstanceName"))
End If
Next
Dim currentservername As String = start1_Cmbo_InstNam.Text
'WPF requires combobox binding to work, hense datacontext and in xaml itemsource.
With start1_Cmbo_InstNam
'.Items.Clear()
.DataContext = servernames
.SelectedItem = currentservername
.Text = currentservername
End With
End Sub
Private Function saveconstr() As Boolean
Dim result As Boolean = False
'WORKS PERFECT, SAVES TO YOUR_APP_NAME.EXE.CONFIG (PROBABLY DOESNT NEED TO BE A FUNCTION FOR MY APPLICATION BUT LEFT AS IS FOR NOW)
Dim config As System.Configuration.Configuration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)
For Each setting As ConnectionStringSettings In config.ConnectionStrings.ConnectionStrings
'find the connectionstring
If setting.Name.Contains("PrimaryConnectionString") Then
'save the new connection string entered by the user.
setting.ConnectionString = getconstr()
'save the changes to the config file
config.Save(ConfigurationSaveMode.Modified)
ConfigurationManager.RefreshSection("connectionStrings")
'force refresh of connectionstring from the config file
My.Settings.Reload()
Exit For
End If
Next setting
Return result
End Function
Private Function getconstr() As String
Dim csb As New SqlConnectionStringBuilder()
'put together a connection string from all user inputs
csb.DataSource = start1_Cmbo_InstNam.Text
csb.IntegratedSecurity = False
csb.UserID = start1_Userid_Cmbox.Text
csb.Password = start1_cmbo_pass.Text
csb.InitialCatalog = ""
Return csb.ConnectionString
End Function
End Class
This is from the app.config file
Code:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
<appSettings>
<add key="ServerName" value="AppSettingsServer" />
<add key="DatabaseName" value="AppSettingsDatabase" />
<add key="OSSecurity" value="False" />
<add key="UserName" value="AppSettingsUserName" />
<add key="Password" value="AppSettingsPassword" />
</appSettings>
<connectionStrings>
<add name="Barcodeapp.My.Settings.PrimaryConnectionString" connectionString="Data Source=ConnectionStringsServer; Initial Catalog=ConnectionStringsDatabase;User ID=ConnectionStringsUserName;Password=ConnectionStringsPassword" />
</connectionStrings>
<system.diagnostics>
<sources>
<!-- This section defines the logging configuration for My.Application.Log -->
<source name="DefaultSource" switchName="DefaultSwitch">
<listeners>
<add name="FileLog"/>
<!-- Uncomment the below section to write to the Application Event Log -->
<!--<add name="EventLog"/>-->
</listeners>
</source>
</sources>
<switches>
<add name="DefaultSwitch" value="Information" />
</switches>
<sharedListeners>
<add name="FileLog"
type="Microsoft.VisualBasic.Logging.FileLogTraceListener, Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
initializeData="FileLogWriter"/>
<!-- Uncomment the below section and replace APPLICATION_NAME with the name of your application to write to the Application Event Log -->
<!--<add name="EventLog" type="System.Diagnostics.EventLogTraceListener" initializeData="APPLICATION_NAME"/> -->
</sharedListeners>
</system.diagnostics>
</configuration>
This is the from the applications .exe.config file that the code saves the values to.
Code:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
<appSettings>
<add key="ServerName" value="AppSettingsServer" />
<add key="DatabaseName" value="AppSettingsDatabase" />
<add key="OSSecurity" value="False" />
<add key="UserName" value="AppSettingsUserName" />
<add key="Password" value="AppSettingsPassword" />
</appSettings>
<connectionStrings>
<add name="Barcodeapp.My.Settings.PrimaryConnectionString" connectionString="Data Source=server1;Initial Catalog=;Integrated Security=False;User ID=user1;Password=pass1" />
</connectionStrings>
<system.diagnostics>
<sources>
<!-- This section defines the logging configuration for My.Application.Log -->
<source name="DefaultSource" switchName="DefaultSwitch">
<listeners>
<add name="FileLog"/>
<!-- Uncomment the below section to write to the Application Event Log -->
<!--<add name="EventLog"/>-->
</listeners>
</source>
</sources>
<switches>
<add name="DefaultSwitch" value="Information" />
</switches>
<sharedListeners>
<add name="FileLog"
type="Microsoft.VisualBasic.Logging.FileLogTraceListener, Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"
initializeData="FileLogWriter"/>
<!-- Uncomment the below section and replace APPLICATION_NAME with the name of your application to write to the Application Event Log -->
<!--<add name="EventLog" type="System.Diagnostics.EventLogTraceListener" initializeData="APPLICATION_NAME"/> -->
</sharedListeners>
</system.diagnostics>
</configuration>
And finally the settings under the apps project propertys
NAME TYPE SCOPE VALUE
PrimaryConnectionString | (connectionstring) | Application | Data Source=ConnectionStringsServer;Initial Catalog=ConnectionStringsDatabase;User ID=ConnectionStringsUserName;Password=ConnectionStringsPassword
I think I need to make something clear, just to be sure that you're not expecting something to happen that is not meant to happen. The app.config file is a source file only. It doesn't exist at run time so the application cannot get anything from it. When you build your project, any appropriate transformations are performed on app.config file and the result is copied to the output folder along with your EXE and renamed to match the EXE name with a ".config" extension. Any changes you make at run time are made to that file. If you close the application, rebuild and then run again, any changes you made will be lost because your config file will have been overwritten with a new copy. Is that maybe what's happening or are you absolutely sure that the config file contains the correct values when the app apparently reads the wrong ones?
Yes were deffinatly on the same page and i posted both the app.config and the Applicationname.exe.config file in the above post to show you that the correct values are indeed being saved to the applicationname.exe.config. In this case server1 for the server instance, user1 for user id and pass1 for password. These values also stay in the applicationname.exe.config file untill new values are entered into the textboxes and the appropriate save subroutine is called.
Also when I use a breakpoint at the end of the loadconstr sub and hover the mouse over the "Primaryconnectionstring" of the line Dim csb As New SqlConnectionStringBuilder(My.Settings.PrimaryConnectionString) it shows the default values, dont know if that helps.
Ok so I finally cracked it, and THINK it was just down to 3 things but not exactly sure if it was one or an accumulation off all 3.
#1) It seems visual studio by default hosts your app in its own process which was causing the YOUR_APP.exe.config to be omitted and the YOUR_APP.vshost.exe.config to be used instead with unstable results, which also may or may not be put into your project folder.
#2) I also changed the setting of "PrimaryConnectionString" to just "PCS" to avoid any typos that may occur from a long designation.
#3) Finally I hit synchronize and found that I had a user file somewhere that was deleted and now its working.
NOTE: I started a new app to debug what exactly was going on and found all I needed to do to get the code to work was step #1 although for my own sanity I always implement shorter names as in #2.
With regards to point 1, I recall reading some time ago what that VS hosting process was supposed to do for you but I can't remember what that was. I do recall that it was not required in the majority of cases, so turning it off should not be an issue. That said, I don't recall having to do that in the demo project I created for this thread.
With regards to point 2, I think that using short names like that that mean nothing to anyone without prior knowledge is a bad idea. In the case of a connection string, the only time that you have to actually type the name without help from Intellisense is when you create it in the first place, so the chance of a typo is basically null. It's more important that someone can look at the name and actually know what the thing is from that alone rather than having to divine what some initials might be for. I'll use initials in cases where a variable exists for a short time and the meaning is obvious, e.g.
Code:
Using ofd As New OpenFileDialog
but PCS would mean nothing to anyone. You might be able to guess that CS was connection string from usage but the P would be a complete mystery.