忙乎了一天,终于将VS 2005下应用程序配置文件加解密及多项目共用一个配置文件的问题搞定。
实际要求是:因为准备编写一个数学试题库管理系统,自然考虑到要将数据库连接字符串保存到配置文件中,C#中当然是保存在App.Config文件。如果你的项目名称是TestLib,生成的可执行文件是TestLib.EXE,配置文件就是TestLib.EXE.Config。因为数据库连接的用户名和密码都是放在配置文件中,于是考虑到要将连接字符串加密。同时,为了方便部署,希望编写一个程序,能够按照要求修改连接字符串,并将其保存。这个程序要求必须是一个单独的EXE文件,仅在部署时才用到它。用户平时使用题库时并不需要用它。
先解决数据库连接字符串加解密的问题。
从“菩提树下的杨过.Net”的博客中找到了“再谈web.config/app.config敏感数据加/解密的二种方法”的文章(http://www.cnblogs.com/yjmyzz/archive/2008/08/22/1274395.html)
加密的方法如下:
//加密web.Config中的指定节
private void ProtectSection(string sectionName)
{
Configuration config = WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
ConfigurationSection section = config.GetSection(sectionName);
if (section != null && !section.SectionInformation.IsProtected)
{
section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
config.Save();
}
}
//解密web.Config中的指定节
private void UnProtectSection(string sectionName)
{
Configuration config = WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
ConfigurationSection section = config.GetSection(sectionName);
if (section != null && section.SectionInformation.IsProtected)
{
section.SectionInformation.UnprotectSection();
config.Save();
}
}
虽然是加解密ASP.NET中的Web.Config文件,但只要将
Configuration config = WebConfigurationManager.OpenWebConfiguration(Request.ApplicationPath);
改为:
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
就可以了。
加密前的配置文件为:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="MathConnStr" connectionString="Data Source=s10.0.13.100;Initial Catalog=Maths;User ID=Maths;password=5975238"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
加密后的配置文件为:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings configProtectionProvider="DataProtectionConfigurationProvider">
<EncryptedData>
<CipherData>
<CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAnnUA3X3xO0G6yCdMO0YNYwQAAAACAAAAAAADZgAAqAAAABAAAAAhKw7WeiJ2xsjZ1uraDXgDAAAAAASAAACgAAAAEAAAAEKF+hGsklR2pcTePGmtB2igAQAAJDN2SLtXGNKojOGq9ukpFTJg0YrNlgEfaosx7Rxm9spCgKqolicr59L6mthOBeUaVxlx3Rk3YACr7IrWGDj6Pbu4macRf5V5TWSxUv+7dqyljS8uvUuEOLfC25Og4sYqyPsYk/dAnOFAFGbRhDGqlpsvGApOGzgb5vUorzqVVvvK38jdQU/klgqLcDJ5K+WC7GPTcuAfRrCcnvkHA2wPDb+XQGF9Y7ErmoQLazPlBOkIE6qyXQ/6xVEcIc/7FMX3KSgzxTGcITFHBxby/ZIFQ15uauHaxXR1v+gm22pG8o3lLZ1pw/7q5sBpR2wNdlNnl1ExB/t9PK6OFAITV2vOGL8tlx+Gl32+A3EsGGz7GKX3rdpKquugNuBytF3TYYl10k2Z1U/vLMgeUMhG0Ntdb4JJll4VbFmIS9AyBvxM5sO9ZbIbuYavH60BJ2Z1S+ZH1Hm37qjrq0yZLZUetf6RrvDKtkiJcmii6cX3pOt6Ecgdj2vitdN/OJva3JJDhAJOcC49lTwOH2ZZ29tfyuMB/8H/+avchr2e5MwLar2nZ3sUAAAAYczvUuIIqdw7NjlyFowywgYrxRQ=</CipherValue>
</CipherData>
</EncryptedData>
</connectionStrings>
</configuration>
加解密数据库连接字符串很快就解决了,接下来要解决编写另外的小程序来修改这个连接字符串。开始考虑到直接做在试题库的应用程序中。
从“我们的家园”博客上找到了修改数据库连接字符串(实际上可以是配置文件中的任何一节)的文章“读取并修改App.config文件(转载)”(http://www.cnblogs.com/xshy3412/archive/2007/11/24/971374.html),他也是转载的哟,从哪儿转载我就不知道了。这里面提供了一个修改数据库连接字符串的方法:
///<summary>
///依据连接串名字connectionName返回数据连接字符串
///</summary>
///<param name="connectionName"></param>
///<returns></returns>
private static string GetConnectionStringsConfig(string connectionName)
{
string connectionString =
ConfigurationManager.ConnectionStrings[connectionName].ConnectionString.ToString();
Console.WriteLine(connectionString);
return connectionString;
}
///<summary>
///更新连接字符串
///</summary>
///<param name="newName">连接字符串名称</param>
///<param name="newConString">连接字符串内容</param>
///<param name="newProviderName">数据提供程序名称</param>
private static void UpdateConnectionStringsConfig(string newName,
string newConString,
string newProviderName)
{
bool isModified = false; //记录该连接串是否已经存在
//如果要更改的连接串已经存在
if (ConfigurationManager.ConnectionStrings[newName] != null)
{
isModified = true;
}
//新建一个连接字符串实例
ConnectionStringSettings mySettings =
new ConnectionStringSettings(newName, newConString, newProviderName);
// 打开可执行的配置文件*.exe.config
Configuration config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// 如果连接串已存在,首先删除它
if (isModified)
{
config.ConnectionStrings.ConnectionStrings.Remove(newName);
}
// 将新的连接串添加到配置文件中.
config.ConnectionStrings.ConnectionStrings.Add(mySettings);
// 保存对配置文件所作的更改
config.Save(ConfigurationSaveMode.Modified);
// 强制重新载入配置文件的ConnectionStrings配置节
ConfigurationManager.RefreshSection("ConnectionStrings");
}
有了这个,很快就实现了在应用程序中修改配置文件中已经加密的连接字符串。但考虑到将这个功能放在应用程序中,用户可能随便使用,于是考虑将其另外作为一个可执行文件,这样,用户就不会随便到可执行程序文件夹下执行这个程序了。
联想到在一个解决方案中创建多个项目,并在主项目中添加对DbConfig项目的引用,DbConfig项目仍然作为Windows应用程序输出,可以在主程序输出目录中包含DbConfig项目的可执行文件。(作为类库项目只包含Dll文件)。
但很快发现,DbCofnig.Exe文件只能读取和修改DbCofnig.Exe.Config文件的内容,你把他改成TestLib.Exe,是能读取TestLib.Exe.Config文件的内容。但主项目TestLib的可执行文件也是TestLib.Exe呀。不可取。
怎么能做到多项目都能读取同一个Cofnig文件呢?考虑到ConfigurationManager.OpenExeConfiguration方法的另一个版本:
ConfigurationManager.OpenExeConfiguration(string exePath),它可以指定具体哪个文件。结合Application.ExecutablePath可以获得可执行文件对应的Config文件。但仍然不行,DbConfig.Exe仍然只能读取DbConfig.Exe.Config文件。
那就使用Application.StartupPath+@"\Db.config",只让它读取指定的Db.Config文件。
调试了n遍,仍然是“未将对象引用设置到对象的实例”。
郁闷ing...,再到网上去找!
在“若我为上帝,谁为众生”的博客上,找到了这篇文章“再谈额外的配置文件读取和ConfigrationManager.OpenExeConfiguration(exePath)的误导性错误”(http://www.cnblogs.com/telephoner/archive/2008/07/15/1243260.html).
晕!居然是微软的Bug。
----------------------------
首先说ConfigrationManager.OpenExeConfiguration(exePath)的问题:
1.此处是一个bug,exePath在msdn的说明中是要读取的配置文件的路径,而在生成System.Configuration.Configuration a = System.Configuration.ConfigurationManager.OpenExeConfiguration(exepath)的时候,实际在生成的a对象中,可以看到它的FilePath属性,让人大吃一惊,它显示的路径是原exePath路径+.config.
所以a对象中永远为空,因为你的X.config是存在的,但它读取的却是X.config.config.
这是否是我们的理解有误而非Bug呢?
不,在试验在exePath中填写.config前面的部分后,提示错误,无法读取该配置文件,即它的ConfigurationManager.OpenExeConfiguration()方法要求exepath必须是一个.config文件!!!
2.这个Bug可能的原因
我猜想,它原来的读取的方法和类都是针对的配置文件的形式为:可执行文件文件名+.exe+.config的形式,或者是Machine.config.而这时不需要识别.config的,而当在.net 2.0里面时候,添加的这个读取额外配置文件的方法,由于一些失误,仍然是匹配的原来的老方法,导致它在读取是自动加上了.config.
---------------------------------------------------------------------------------------------
解决方法
我参考了<Configuration类在网页实现对web.config的修改>(http://tech.sina.com.cn/s/2008-06-30/1013712947.shtml)的方法
即真假config文件的方法:
1.先在项目的bin/debug目录下建立一个额外的config文件,即X.config;
2.在同目录下建立一个X.config.config文件;
3.所有信息都在X.config.config内,即appSettings节点等;
4.然后读写都可以了.
-----------------------------------------------------
于是按照他所提供的方法来,在可执行文件的文件夹下同时存放Db.Config和Db.Config.Config,果然可以,并且将Db.Config文件必须要有,哪怕是个空的都中,删掉则不行。
OK!
2008年12月15日晚22:00