WEB项目(B/S系统)打包安装程序总结
使用VS 自带的打包工具,制作webform安装项目
开发环境:VS 2008 ,Ms sqlserver 2008(必须先安装),IIS(必须先安装)
操作系统:Windows 7
开发语言:C#
项目名称:中心站
概要:
打包安装程序的制作选择性还很多的,有installshield,wise installer,inno setup这几个做打包安装项目都是很强大的,要搞出界面大气个性化的安装,还是得用这几个才行,我这篇说 的是.NET自带的安装项目打包WEB系统的,.NET打包安装项目虽然傻傻的,也比较丑,但是搭建安装项目快速也能满足总体的要求,如果没有足够的时间研究重量级的安装工具,短时间内 用.NET自带的安装项目也是一个良好的选择。
使用vs2013打包安装时,使用visual studio installer projects ,下载地址
网上有很多关于用.NET打包WEB项目的文章,大体是那样的,但不是很全面,有个别细节并没有涉及,先说下这个安装打包项目的主要功能:
安装类的说明
一、安装前系统检测主要检测SQL SERVER是否安装,站点名称和端口号是否重复等,framework自动检测没有的话先安装,本身自带就有此功能,IIS是必须要求的,不然没法创建站点。
二、项目文件安装到指定文件夹并根据安装时的数据库配置修改webconfig连接
三、附加数据库:打包时可以把mdf文件放到WEB项目文件夹下,生成安装文件时就会自动打包进去,安装时执行附加即可。
四、注册COM组件(如果项目中涉及到COM的话)
五、创建WEB站点
六、创建虚拟目录
这里的虚拟目前是根据打包的项目需要进行的,因为有的WEB项目结构比较复杂,比如一个系统除了本身项目的发布文件,还有另外的独立特殊功能的项目作为虚拟目录的形式一起运行, 类似于插拔式的插件一样,需要的话直接拷贝到站点下,设置虚拟目录,系统菜单做好链接指向其页面就可以了。
七、创建桌面快捷方式
比如可以把系统的登录页面或其它起始页作为安装时指定的快捷方式
八、项目卸载
项目的卸载主要包括删除项目文件,卸载附加的数据库,删除站点及桌面快捷方式等
安装类代码说明
安装WEB项目的前提条件是需要在用户电脑上先安装SQL SERVER数据库(或其它数据库)和IIS信息管理器。
1)安装项目主要涉及的是安装类里面的操作,包括安装和卸载的方法重写等,安装时需要记录用户通过安装界面输入的信息,这里我们可以用XML文件作为记录配置文件,主要是在卸载的时候需要用到用户输入的相关信息,数据库连接串的记录操作如下:
/// <summary>
/// 获取数据库登录连接字符串
/// </summary>
/// <param name="databasename">数据库名称</param>
/// <returns></returns>
private string GetConnectionString(string databasename)
{
string ConnStr = "server=" + Context.Parameters["server"].ToString() + ";database=" + (string.IsNullOrEmpty(databasename) ? "master" : databasename) + ";uid=" + Context.Parameters["user"].ToString() + ";pwd=" + Context.Parameters["pwd"].ToString();
if (string.IsNullOrEmpty(databasename)) //将连接串写入XML文件,供卸载操作时读取
{
dbpath = Path.Combine(this.Context.Parameters["installdir"].ToString(), "dbconfig.xml");
OperateXML.UpdateXMLNode(dbpath, "ConnString", ConnStr);
}
else
{
dbpath = Path.Combine(this.Context.Parameters["installdir"].ToString(), "dbconfig.xml");
OperateXML.UpdateXMLNode(dbpath, "DbName", databasename);
}
return ConnStr;
}
2)安装检测
/// <summary>
/// 判断是否安装了SQL SERVER
/// </summary>
/// <returns></returns>
private bool ExistSqlServerService()
{
bool Exist = false;
ServiceController[] service = ServiceController.GetServices();
for (int i = 0; i < service.Length; i++)
{
if (service[i].ServiceName.Length > 5 && service[i].ServiceName.Substring(0, 5) == "MSSQL") //if (service[i].ServiceName == "MSSQLSERVER")
{
Exist = true;
break;
}
}
return Exist;
}
这里是根据服务名称判断的,如果是SQLSERVER EXPRESS版的话,服务名是MSSQL$EXPRESS,其它的数据库实例也是MSSQL$+实例名
/// <summary>
/// 检测IIS及版本号
/// </summary>
/// <returns></returns>
public string GetIISVerstion()
{
RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWAREMicrosoftINetStp");
if (key == null)
return string.Empty;
else
return Convert.ToString(key.GetValue("MajorVersion")) + "." + Convert.ToString(key.GetValue("MinorVersion"));
}
string entPath = String.Format("IIS://{0}/w3svc", "localhost");
/// <summary>
/// 端口号是否重复
/// </summary>
/// <returns></returns>
private bool IsExistSitePort()
{
bool exist = false;
DirectoryEntry ent = GetDirectoryEntry(entPath);
foreach (DirectoryEntry child in ent.Children)
{
if (child.SchemaClassName == "IIsWebServer")
{
if (child.Properties["ServerBindings"].Value != null && child.Properties["ServerBindings"].Value.ToString().Split(':').Length > 1)
{
if (child.Properties["ServerBindings"].Value.ToString().Split(':')[1] == Context.Parameters["siteport"].ToString())
{
exist = true;
break;
}
}
}
}
return exist;
}
child.Properties["ServerBindings"].Value 是绑定属性,其实就是下面这个图里的属性
但是有的站点可能会分配两个端口,child.Properties["ServerBindings"].Value 就是 System.Object[] 字符
/// <summary>
/// 站点名称是否存在
/// </summary>
/// <returns></returns>
private bool IsExistSiteName(string sitename)
{
bool exist = false;
using (DirectoryEntry root = new DirectoryEntry(entPath))
{
foreach (DirectoryEntry Child in root.Children)
{
if (Child.SchemaClassName == "IIsWebServer")
{
string WName = Child.Properties["ServerComment"].Value.ToString();
if (sitename == WName)
{
exist = true;
break;
}
}
}
root.Close();
}
return exist;
}
3)安装并复制文件
复制文件的操作在OnBeforeInstall方法中就已经完成,在该方法中可以替换数据库连接字符串操作等。
protected override void OnBeforeInstall(IDictionary savedState)
{
if (ExistSqlServerService())
{
if (IsExistSitePort())
{
MessageBox.Show("站点端口号重复", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (IsExistSiteName(Context.Parameters["sitename"].ToString()))
{
MessageBox.Show("站点名称重复", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
base.OnBeforeInstall(savedState);
unRAR("Message.rar"); //解压文件
unRAR("MoreUpload.rar");
/*
* 设置webconfig连接字符串
*/
string webconfigpath = Path.Combine(this.Context.Parameters["installdir"].ToString(), "Web.config");
string webcofnigstring = File.ReadAllText(webconfigpath).Replace("#constring#", GetConnectionString(Context.Parameters["dbname"].ToString()));
webcofnigstring = webcofnigstring.Replace("#siteport#", Context.Parameters["siteport"].ToString());
webcofnigstring = webcofnigstring.Replace("#comConn#", "Provider=SQLOLEDB.1;Persist Security Info=False;User ID=" + Context.Parameters["user"].ToString() + ";Password=" + Context.Parameters["pwd"].ToString() + ";Initial Cat alog=" + Context.Parameters["dbname"].ToString() + ";Data Source=" + Context.Parameters["server"].ToString());
File.WriteAllText(webconfigpath, webcofnigstring);
////安装IIS
//if (string.Empty == GetIISVerstion())
//{
// IISInstall(this.Context.Parameters["installdir"].ToString() + "IIS6.0_XPSP3", this.Context.Parameters["installdir"].ToString() + "iis.txt");
//}
}
else
{
MessageBox.Show("检测到您的电脑没有安装SQL SERVER,无法继续安装此产品", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
this.Rollback(savedState);
}
}
上面代码中解压了两个文件,主要是在安装完创建站点时还需要创建虚拟目录。之所以要先压缩放到WEB项目里,是因为生成安装程序时会检测项目的生成是否成功(是否可以不让它检 测成功呢? 还不清楚),一般只要我们把发布好的项目COPY到新建项目下就可以,但是独立的项目文件夹有自己独立的bin目录和webconfig文件,是不能混合在一起生成成功的,但 是又需要一次完全安装,那么可以先压缩做为WEB项目下的文件,待复制完后再解压。
4)安装
/// <summary>
/// 安装
/// </summary>
/// <param name="stateSaver"></param>
public override void Install(IDictionary stateSaver)
{
if (ExistSqlServerService())
{
if (IsExistSitePort())
{
MessageBox.Show("站点端口号重复", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
if (IsExistSiteName(Context.Parameters["sitename"].ToString()))
{
MessageBox.Show("站点名称重复", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
base.Install(stateSaver);
WriteBat();
RegisterCom();
string connectionString = GetConnectionString(null);
try
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = "sp_attach_db '" + Context.Parameters["dbname"].ToString() + "','" + Context.Parameters["installdir"].ToString() + "App_Data/Db_Data.MDF','"
+ Context.Parameters["installdir"].ToString() + "App_Data/Db_Data_Log.LDF'";
ExecuteSQL(connection, sql);
connection.Close();
}
}
catch (Exception ex)
{
MessageBox.Show("数据库安装失败!
数据库配置有误,请正确配置信息!
" + ex.Message, "出错!");
this.Rollback(stateSaver);
return;
}
try
{
CreateDeskTopShortcut();
NewWebSiteInfo siteInfo = new NewWebSiteInfo(string.Empty, Context.Parameters["siteport"].ToString(), "", Context.Parameters["sitename"].ToString(), Context.Parameters["installdir"].ToString());
CreateNewWebSite(siteInfo);
StartWebSite(siteInfo.BindString);
}
catch//(Exception ex)
{
//MessageBox.Show("创建站点失败!
" + ex.Message, "出错!");
//this.Rollback(stateSaver);
CreateVirWebSite("Message", Context.Parameters["installdir"].ToString() + "Message");
CreateVirWebSite("MoreUpload", Context.Parameters["installdir"].ToString() + "MoreUpload");
}
}
else
{
MessageBox.Show("检测到您的电脑没有安装SQL SERVER,无法继续安装此产品", "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
this.Rollback(stateSaver);
}
}
创建好网站后调用CreateVirWebSite方法创建虚拟目录。
5)安装时用户参数接收
参数的名称在添加安装项目的视图-》用户界面里填写,
参数的传递在视图——》自定义操作里面设置,如下图:
在CustomActionData里输入所有需要传递到安装类的参数 /dbname=[DBNAME] /server=[SERVER] /user=[UID] /pwd=[PWD] /sitename=[SITENAME] /siteport=[SITEPORT] /startpage=[STARTPAGE] /shortcutname=[SHORTCUTNAME] /installdir="[TARGETDIR]" 注意这个格式是固定的,尤其安装路径的参数格式installdir="[TARGETDIR]"这么写
同时要说明的是在卸载的CustomActionData属性只需要给出installdir参数,即使给了其它的参数,卸载时也是读取不到的,只能读取到初始的默认值,如果安装过程中用户重新修 改了就不行了,安装文件时选择的路径始终能正确读取到,正因为如此在安装时需要把录入的相关信息以XML配置文件的形式记录下来,这样才能保证正确无误的卸载。
6)卸载
因为解压产生的文件卸载时不会自动删除,需要单独编写代码删除,这些文件是安装过程中产生的次生文件,它只会自动删除原本WEB项目打包进去的原来文件,至于另外产生 的文件是不会自动删除的。对于解压后的文件进行操作,这里也有个一个必须要注意的地方,就是路径问题,解压产生的次生文件,安装时会读取不到解压后的文件夹路径,事实上文件已经复制解压也完成了,但就是读取不到,对应这种情况办法是建立一个和压缩包一样的空文件夹放到WEB项目里,如果解压后的文件里需要替换webconfig连接串,可以在文件夹里放入Web.xml文件,并设置好要替换的地方,前面说了多个webconfig生成会不成功,这里可以先取名xml,解压后替换连接串再修改文件名OK了。
/// <summary
/// 卸载
/// </summary>
/// <param name="savedState"></param>
public override void Uninstall(IDictionary savedState)
{
DeleteWebSite(); //删除安装文件前先删除站点,因删除站点时需读取安装文件的配置信息
dbpath = Path.Combine(this.Context.Parameters["installdir"].ToString(), "dbconfig.xml");
string connectionString = OperateXML.GetXmlNodeValue(dbpath, "ConnString");
string dbName = OperateXML.GetXmlNodeValue(dbpath, "DbName");
base.Uninstall(savedState);
try
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = "if exists(select 1 from master..sysdatabases where name= '" + dbName + "') drop database " + dbName;
ExecuteSQL(connection, sql);
connection.Close();
}
if (Directory.Exists(Context.Parameters["installdir"].ToString() + "Message"))
{
Directory.Delete(Context.Parameters["installdir"].ToString() + "Message", true);//删除解压产生的文件
}
if (Directory.Exists(Context.Parameters["installdir"].ToString() + "MoreUpload"))
{
Directory.Delete(Context.Parameters["installdir"].ToString() + "MoreUpload", true);
}
}
catch (Exception ex)
{
MessageBox.Show("卸载失败!
" + ex.Message, "信息提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
this.Rollback(savedState);
return;
}
}
好了,安装类说明已经差不多了,现在按照VS里实际操作步骤来讲解下
步骤:
第一步:打开开发环境VS2008,新建项目,选择其他项目类型,再选择"web安装项目",输入名称及选择安装路径;(其实选择安装项目而非web安装项目也没有问题)
第二步:进入文件系统选项卡,选择应用程序文件夹,在中间的空白区域右键选择"添加文件",添加项目相关文件
注:添加的文件最好是发布的文件,.cs的文件不要包含在内(如果文件夹太多,可直接复制,并粘帖到文件系统对应的文件夹下)
第三步:设置系统必备,右键选择安装项目,进入属性页中,单击"系统必备"按钮,进入系统必备对话框;勾选"创建用于安装系统必备组件的安装程序",在安装系统必备组件列表中,选择
1)、Windows Installer 3.1(必选)
2)、.NET Framework 3.5 (可选)参考最后说明
3)、Crystal Report Basic for Visual Studio2008(x86,x64) (可选) 项目中用到了水晶报表就需要勾选此项
重要一点:勾选"从与我的应用程序相同的位置下载系统必备组件(D)",其实意思就是说你勾选后,生成安装项目时,在你安装项目的路径下,会有你在系统必备组件列表中勾选的组件.(系统自动完成,这一点还不错,不需要你自己去下载组件)
第四步:更改安装程序属性,右键选择安装项目属性,可以设置项目作者及名称,其他属性信息可以根据实际情况进行设置.
第五步: 创建安装程序类
在解决方案资源管理器中,新建一个类库项目【DBInstaller】,删除Class1.cs,新建一个安装程序类[DBInstaller.cs],具体安装类的代码在文章尾附上
第六步: 创建自定义安装对话框
在刚新建的安装项目上右键,【视图】->【用户界面】:
在用户界面中,右键【启动】-【添加对话框】-选择【文本框(A)】-确定。
然后右键这个文本框(A),将其上移到欢迎使用下面:(这点很重要)
右键选择【属性】,参考下图的信息填写:
备注:如果还有一些自定义的属性要写,可以添加文件框,....
添加许可协议(可选)在word里先创建好协议书的rtf文件,在这里浏览引用就行
在刚新建的安装项目上右键,【视图】->【自定义操作】:
右键【自定义操作界面】的【安装】节点,【添加自定义操作】,弹出的对话框。
在查找范围里选择应用程序文件夹,再点击右侧的【添加输出(O)…】,选择刚新建的安装程序类项目,默认还是主输出,确定。此时:
10.右键这个【主输出来自DBInstasller(活动)】,进入属性界面,在【CustomActionData】属性里输入下面的内容:
/dbname=[DBNAME] /server=[SERVER] /user=[UID] /pwd=[PWD] /sitename=[SITENAME] /siteport=[SITEPORT] /startpage=[STARTPAGE] /shortcutname=[SHORTCUTNAME] /installdir="[TARGETDIR]"
说明:其中前四个方括号中的大写字母,为上面第6步图中输入的四个EditProPerty属性,需要对应好。最后一个targetdir的值的意思是安装后文件的目录路径。
特别提醒:前面各元素之间个"/XXX=[XXX]"后面 ,都有一个空格的
备注:这个很重要,复制过去后最好手动去掉空格后再添一个空格,注意最后TARGETDIR 写法不同, 不要更改,否则程序里这些设置的参数读取不到值,安装会失败
至此安装程序就弄好了,生成下安装项目就会生成安装文件
注1:在生成中如果出现应将 qedit.dll排除错误,可以在安装项目-检测到的依赖项里把qedit.dll排除即可
注2:在安装过程中出现 "Error 1001. 在初始化安装时发生异常 system.badImageFormatException 未能加载文件或程序集 *** 生成此程序集的运行时比当前加载的运行时新,无法加载此程序集"错误,
解决办法:首先确保安装类的输出目标为 any cpu,安装项目为x86,本人在出现此错误时是因为数据库安装类 DBInstall类的.net框架的版本为4.0,安装项目的版本为aspnetversion 4.0(即4.0)的框架,把 DBInstall类的.net框架的版本改为2.0后重新生成的安装文件安装即可正常。