本文转自:http://blog.csdn.net/seamonkey/archive/2007/11/28/1905983.aspx
这几天忙于ASP.NET2.0安装打包,在网上找遍所有信息却不能完全满足自己的需要。现总经经验出下:
1、站点: 如果新建默认的Web安装项目,那它将创建的默认网站下的一个虚拟应用程序目录而不是一个新的站点。故我们只有创建新的安装项目,而不是Web安装项目。然后通过安装类进行自定义操作,创建新站如下图:
2、创建新的安项目之后,在(文件系统编辑器)里的应用程序文件夹里,添加ASP.net2.0的项目输出(还有第二种方法是用ASP.NET2.0发布网站到一个文件夹下面,再把这个文件夹下面所有文件复制粘贴过来,这样生成的安装文件,在安装之后就没有源代码文件了,我自己就是这样做的)如下图:
图1
下图的MyPojectSetup项目下已有了Web项目的输出,以及一个SQL脚本(SQL脚本是通SQL2005生成的,将在安装类库里要使用的)
图2文件系统左边界面,添加了Web项目输出
2.2添加完项目输出之后,需要设置安装界面。我们的要求是第一、建一个新站点,所以需要所安装的IIS服务器地址,以及新站点的端口。第二、需要数据库的地址,新建的数据库名称,以及访问数据库的用户名和密码两项(需要有创库权限的)。
如图1在文件系统编辑器右边,选择用户界面,然后看到如下:
在启动选择单击右击菜单,添加对话框A,并在对话框A上单击右键=》上移到安装文件夹的上面:
再次设置右边属性,文本框A是站点信息输入如下信息,其中Edit1Property是一个需要传入安装类的参数。
按照以上方式再添加文本框B并移到文本框A的下面,如下图所示
注意:如上所示安装项目基本的事情已经做完了,但还有最后一个步骤没有做,那就是自定义操作,也就安装的重中之重的事情,安装类库的创建,如3点所示。创建安装类库之后就需要把它的输出来添加到安装项目里如同Web项目,然后设置自定义活动为这个项目就可以,详情在下面介绍。
3、需要创建一个安装类库,里面把Class1.cs删除,再添加一个新的安装文件。安装项目所有的自定义都是在这里用编码完成的(包括数据库生成,虚拟站点创建,IIS属性修改,Web.Config文件修改)。也就是说,安装项目是个外壳,通过创建一些界面接收用户参数,然后利用这个安装类库,提供的功能,进行自己的操作。
4、安装程序类新建之后,双击进入代码状态,用override重载Install函数如下所示:
using
System;
using
System.Collections.Generic;
using
System.ComponentModel;
using
System.Configuration.Install;
namespace
MyProjectInstall
...
{
[RunInstaller(true)]
public partial class InstallerMySample : Installer
...{
public InstallerMySample()
...{
InitializeComponent();
}
public override void Install(System.Collections.IDictionary stateSaver)
...{
}
}
}
安装数据库的代码如下,其中用了两种方法,一种是用SQL命令方式创建数据库,另一种是调用osql命令执行脚本,创建数据表结构等,最后用命令追加一条记录.
数据库操作
#region 数据库操作
protected void AddDBTable()
...{
try
...{
//创建数据库
ExcuteSQL("master", string.Format("CREATE DATABASE {0}", this._dataBaseName));
//调用osql执行脚本
ExcuteScript();
//添加原始数据
ExcuteSQL(_dataBaseName, "INSERT INTO GV_SystemAdmin(SystemAdminUser,SystemAdminPass,SystemAdminName,SystemAdminMemo) VALUES ('admin', 'admin', 'Administrator' ,'系统默认超级用户')");
}
catch (Exception ex)
...{
throw new ApplicationException(ex.Message);
}
}
//此方法在本例中未用到,因为最后只要追加一条记录,
//但如果有多条可以写成一个脚本,以资源的形式嵌入到这个项目
//然后用如下的方法读取,然后调用SQL命令执行
private string GetSQLScript(string fileName)
...{
try
...{
//得到当前程序集
Assembly asm = Assembly.GetExecutingAssembly();
//资源文件
Stream strm =
asm.GetManifestResourceStream(asm.GetName().Name + "." + fileName);
//读取嵌入文件内容,文本文件内容必须为Unicode
StreamReader reader = new StreamReader(strm);
return reader.ReadToEnd();
}
catch
...{
return String.Empty;
}
}
private void ExcuteSQL(string dataBaseName, string SQL)
...{
SqlConnection con =
new SqlConnection(string.Format("user id={0};password={1};Initial Catalog={2};Data Source={3};", _username, _saPassword, dataBaseName, _servername));
SqlCommand cmd = new SqlCommand(SQL, con);
cmd.Connection.Open();
cmd.Connection.ChangeDatabase(dataBaseName);
try
...{
cmd.ExecuteNonQuery();
}
finally
...{
//最后总要关闭数据库
cmd.Connection.Close();
}
}
private void ExcuteScript()
...{
try
...{
Process sqlProcess = new Process();
//调用osql必须在目标机,也就是安装的机子上要有安装SQLServer服务器
//不然找不到这个命令
sqlProcess.StartInfo.FileName = "osql.exe";
//如下所指的SQL脚本文件是打包打安装项目的文件名,
//targetPath就是在安装界面用户指定的安装目录
sqlProcess.StartInfo.Arguments = string.Format("-U {0} -P{1} -d {2} -S {3} -i {4}VideoMeetingCreateSQL2000.sql",
_username, _saPassword, _dataBaseName, _servername, _targetPath);
sqlProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
sqlProcess.Start();
sqlProcess.WaitForExit();
sqlProcess.Close();
}
catch (Exception ex)
...{
throw ex;
}
}
#endregion
修改Web.Config配置的代码如下:
WriteWebConfig 修改web.config的连接数据库的字符串
#region WriteWebConfig 修改web.config的连接数据库的字符串
private bool WriteWebConfig()
...{
System.IO.FileInfo FileInfo = new System.IO.FileInfo(this.Context.Parameters["targetdir"] + "/web.config");
if (!FileInfo.Exists)
...{
throw new InstallException("Missing config file :" + this.Context.Parameters["targetdir"] + "/web.config");
}
System.Xml.XmlDocument xmlDocument = new System.Xml.XmlDocument();
xmlDocument.Load(FileInfo.FullName);
bool FoundIt = false;
foreach (System.Xml.XmlNode Node in xmlDocument["configuration"]["connectionStrings"])
...{
if (Node.Name == "add")
...{
if (Node.Attributes.GetNamedItem("name").Value == "MonitorConnectionString")
...{
Node.Attributes.GetNamedItem("connectionString").Value = String.Format("Data Source={0};database={1};User ID={2};Password={3}", _servername, _dataBaseName, _username, _saPassword);
FoundIt = true;
}
}
}
if (!FoundIt)
...{
throw new InstallException("Error when writing the config file: web.config");
}
xmlDocument.Save(FileInfo.FullName);
return FoundIt;
}
#endregion
创建IIS站点的代码如下(注我也是从网找到的,其中CreateNewWebsit这个方法中我加了IIS参数设置的代码,也就是在创建IIS时一并设置了):
注意这一句:string fileName = Environment.GetEnvironmentVariable("windir") + @"\Microsoft.NET\Framework\v2.0.50727\ASPnet_regiis.exe";原因是当你不管是手动还是自动创建一个新站点,在它的属性页ASP.NET设置里都会有1.1版和2.0版这个选项,默认是1.1,现在我要部署2.0所以在默认情况下就不能够一步到位,安装完就可以访问执行,而要去设置成2.0才行.所以要调用FrameworkV2.0下的regiis.exe得新注册一下我们指定的虚拟目录,使它是2.0.
using
System;
using
System.DirectoryServices;
using
System.Collections;
using
System.Text.RegularExpressions;
using
System.Text;
using
System.Runtime;
using
System.Diagnostics;
/**/
/**
* @author 吴海燕
* @email [email protected]
* 2004-6-25 第一版
*/
namespace
Wuhy.ToolBox
...
{
/**//// <summary>
/// 这个类是静态类。用来实现管理IIS的基本操作。
/// 管理IIS有两种方式,一是ADSI,一是WMI。由于系统限制的原因,只好选择使用ADSI实现功能。
/// 这是一个遗憾。只有等到只有使用IIS 6的时候,才有可能使用WMI来管理系统
/// 不过有一个问题就是,我现在也觉得这样的一个方法在本地执行会比较的好。最好不要远程执行。
/// 因为那样需要占用相当数量的带宽,即使要远程执行,也是推荐在同一个网段里面执行
/// </summary>
public class IISAdminLib
...{
UserName,Password,HostName的定义#region UserName,Password,HostName的定义
public static string HostName
...{
get
...{
return hostName;
}
set
...{
hostName = value;
}
}
public static string UserName
...{
get
...{
return userName;
}
set
...{
userName = value;
}
}
public static string Password
...{
get
...{
return password;
}
set
...{
if (UserName.Length <= 1)
...{
throw new ArgumentException("还没有指定好用户名。请先指定用户名");
}
password = value;
}
}
public static void RemoteConfig(string hostName, string userName, string password)
...{
HostName = hostName;
UserName = userName;
Password = password;
}
private static string hostName = "localhost";
private static string userName;
private static string password;
#endregion
根据路径构造Entry的方法#region 根据路径构造Entry的方法
/**//// <summary>
/// 根据是否有用户名来判断是否是远程服务器。
/// 然后再构造出不同的DirectoryEntry出来
/// </summary>
/// <param name="entPath">DirectoryEntry的路径</param>
/// <returns>返回的是DirectoryEntry实例</returns>
public static DirectoryEntry GetDirectoryEntry(string entPath)
...{
DirectoryEntry ent;
if (UserName == null)
...{
ent = new DirectoryEntry(entPath);
}
else
...{
// ent = new DirectoryEntry(entPath, HostName+"\"+UserName, Password, AuthenticationTypes.Secure);
ent = new DirectoryEntry(entPath, UserName, Password, AuthenticationTypes.Secure);
}
return ent;
}
#endregion
添加,删除网站的方法#region 添加,删除网站的方法
/**//// <summary>
/// 创建一个新的网站。根据传过来的信息进行配置
/// </summary>
/// <param name="siteInfo">存储的是新网站的信息</param>
public static void CreateNewWebSite(NewWebSiteInfo siteInfo)
...{
if (!EnsureNewSiteEnavaible(siteInfo.BindString))
...{
throw new Exception("已经有了这样的网站了。" + Environment.NewLine + siteInfo.BindString);
}
string entPath = String.Format("IIS://{0}/w3svc", HostName);
DirectoryEntry rootEntry = GetDirectoryEntry(entPath);
string newSiteNum = GetNewWebSiteID();
DirectoryEntry newSiteEntry = rootEntry.Children.Add(newSiteNum, "IIsWebServer");
newSiteEntry.CommitChanges();
//Hashtable ahs1 = new Hashtable();
//foreach (string a1 in newSiteEntry.Properties.PropertyNames)
//{
// ahs1.Add(a1, newSiteEntry.Properties[a1].Value);
//}
newSiteEntry.Properties["ServerBindings"].Value = siteInfo.BindString;
newSiteEntry.Properties["ServerComment"].Value = siteInfo.CommentOfWebSite;
newSiteEntry.CommitChanges();
DirectoryEntry vdEntry = newSiteEntry.Children.Add("root", "IIsWebVirtualDir");
vdEntry.CommitChanges();
vdEntry.Properties["Path"].Value = siteInfo.WebPath;
vdEntry.Invoke("AppCreate", true);//创建应用程序
vdEntry.Properties["AccessRead"][0] = true; //设置读取权限
vdEntry.Properties["DefaultDoc"][0] = "default.htm";//设置默认文档
vdEntry.Properties["AppFriendlyName"][0] = "VideoMeeting"; //应用程序名称
vdEntry.Properties["AccessScript"][0] = true;//执行权限
vdEntry.Properties["AuthFlags"][0] = 1;//0表示不允许匿名访问,1表示就可以3为基本身份验证,7为windows继承身份验证
//Hashtable ahs = new Hashtable();
//ArrayList list = new ArrayList();
//foreach (string a in vdEntry.Properties.PropertyNames)
//{
// list.Add(a);
// ahs.Add(a, vdEntry.Properties[a].Value);
//}
vdEntry.CommitChanges();
//启动ASPnet_iis.exe程序
string fileName = Environment.GetEnvironmentVariable("windir") + @"Microsoft.NETFrameworkv2.0.50727ASPnet_regiis.exe";
ProcessStartInfo startInfo = new ProcessStartInfo(fileName);
//处理目录路径
string path = vdEntry.Path.ToUpper();
int index = path.IndexOf("W3SVC");
path = path.Remove(0, index);
//启动ASPnet_iis.exe程序,刷新教本映射
startInfo.Arguments = "-s " + path;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
string errors = process.StandardError.ReadToEnd();
if (errors != string.Empty)
...{
throw new Exception(errors);
}
}
/**//// <summary>
/// 删除一个网站。根据网站名称删除。
/// </summary>
/// <param name="siteName">网站名称</param>
public static void DeleteWebSiteByName(string siteName)
...{
string siteNum = GetWebSiteNum(siteName);
string siteEntPath = String.Format("IIS://{0}/w3svc/{1}", HostName, siteNum);
DirectoryEntry siteEntry = GetDirectoryEntry(siteEntPath);
string rootPath = String.Format("IIS://{0}/w3svc", HostName);
DirectoryEntry rootEntry = GetDirectoryEntry(rootPath);
rootEntry.Children.Remove(siteEntry);
rootEntry.CommitChanges();
}
#endregion
Start和Stop网站的方法#region Start和Stop网站的方法
public static void StartWebSite(string siteName)
...{
string siteNum = GetWebSiteNum(siteName);
string siteEntPath = String.Format("IIS://{0}/w3svc/{1}", HostName, siteNum);
DirectoryEntry siteEntry = GetDirectoryEntry(siteEntPath);
siteEntry.Invoke("Start", new object[] ...{ });
}
public static void StopWebSite(string siteName)
...{
string siteNum = GetWebSiteNum(siteName);
string siteEntPath = String.Format("IIS://{0}/w3svc/{1}", HostName, siteNum);
DirectoryEntry siteEntry = GetDirectoryEntry(siteEntPath);
siteEntry.Invoke("Stop", new object[] ...{ });
}
#endregion
确认网站是否相同#region 确认网站是否相同
/**//// <summary>
/// 确定一个新的网站与现有的网站没有相同的。
/// 这样防止将非法的数据存放到IIS里面去
/// </summary>
/// <param name="bindStr">网站邦定信息</param>
/// <returns>真为可以创建,假为不可以创建</returns>
public static bool EnsureNewSiteEnavaible(string bindStr)
...{
string entPath = String.Format("IIS://{0}/w3svc", HostName);
DirectoryEntry ent = GetDirectoryEntry(entPath);
foreach (DirectoryEntry child in ent.Children)
...{
if (child.SchemaClassName == "IIsWebServer")
...{
if (child.Properties["ServerBindings"].Value != null)
...{
if (child.Properties["ServerBindings"].Value.ToString() == bindStr)
...{
return false;
}
}
}
}
return true;
}
#endregion
获取一个网站编号的方法#region 获取一个网站编号的方法
/**//// <summary>
/// 获取一个网站的编号。根据网站的ServerBindings或者ServerComment来确定网站编号
/// </summary>
/// <param name="siteName"></param>
/// <returns>返回网站的编号</returns>
/// <exception cref="NotFoundWebSiteException">表示没有找到网站</exception>
public static string GetWebSiteNum(string siteName)
...{
Regex regex = new Regex(siteName);
string tmpStr;
string entPath = String.Format("IIS://{0}/w3svc", HostName);
DirectoryEntry ent = GetDirectoryEntry(entPath);
foreach (DirectoryEntry child in ent.Children)
...{
if (child.SchemaClassName == "IIsWebServer")
...{
if (child.Properties["ServerBindings"].Value != null)
...{
tmpStr = child.Properties["ServerBindings"].Value.ToString();
if (regex.Match(tmpStr).Success)
...{
return child.Name;
}
}
if (child.Properties["ServerComment"].Value != null)
...{
tmpStr = child.Properties["ServerComment"].Value.ToString();
if (regex.Match(tmpStr).Success)
...{
return child.Name;
}
}
}
}
throw new Exception("没有找到我们想要的站点" + siteName);
}
#endregion
获取新网站id的方法#region 获取新网站id的方法
/**//// <summary>
/// 获取网站系统里面可以使用的最小的ID。
/// 这是因为每个网站都需要有一个唯一的编号,而且这个编号越小越好。
/// 这里面的算法经过了测试是没有问题的。
/// </summary>
/// <returns>最小的id</returns>
public static string GetNewWebSiteID()
...{
ArrayList list = new ArrayList();
string tmpStr;
string entPath = String.Format("IIS://{0}/w3svc", HostName);
DirectoryEntry ent = GetDirectoryEntry(entPath);
foreach (DirectoryEntry child in ent.Children)
...{
if (child.SchemaClassName == "IIsWebServer")
...{
tmpStr = child.Name.ToString();
list.Add(Convert.ToInt32(tmpStr));
}
}
list.Sort();
int i = 1;
foreach (int j in list)
...{
if (i == j)
...{
i++;
}
}
return i.ToString();
}
#endregion
}
新网站信息结构体#region 新网站信息结构体
public struct NewWebSiteInfo
...{
private string hostIP; // The Hosts IP Address
private string portNum; // The New Web Sites Port.generally is "80"
private string descOfWebSite; // 网站表示。一般为网站的网站名。例如"www.dns.com.cn"
private string commentOfWebSite;// 网站注释。一般也为网站的网站名。
private string webPath; // 网站的主目录。例如"e: mp"
public NewWebSiteInfo(string hostIP, string portNum, string descOfWebSite, string commentOfWebSite, string webPath)
...{
this.hostIP = hostIP;
this.portNum = portNum;
this.descOfWebSite = descOfWebSite;
this.commentOfWebSite = commentOfWebSite;
this.webPath = webPath;
}
public string BindString
...{
get
...{
return String.Format("{0}:{1}:{2}", hostIP, portNum, descOfWebSite);
}
}
public string CommentOfWebSite
...{
get
...{
return commentOfWebSite;
}
}
public string WebPath
...{
get
...{
return webPath;
}
}
}
#endregion
}
最后重载的InStall函数如:
public
override
void
Install(IDictionary stateSaver)
...
{
//数据库安装程序入口
_saPassword = this.Context.Parameters["pwd"];
_dataBaseName = this.Context.Parameters["dbname"];
_targetPath = this.Context.Parameters["targetdir"];
_servername = this.Context.Parameters["server"];
_username = this.Context.Parameters["user"];
iis = this.Context.Parameters["iis"];
port = this.Context.Parameters["port"];
//添加数据库
AddDBTable();
//注册新站点
Wuhy.ToolBox.NewWebSiteInfo siteInfo = new Wuhy.ToolBox.NewWebSiteInfo(string.Empty, port, "", "VideoMeeting", _targetPath);
Wuhy.ToolBox.IISAdminLib.HostName = iis;
Wuhy.ToolBox.IISAdminLib.UserName = null;
Wuhy.ToolBox.IISAdminLib.CreateNewWebSite(siteInfo);
Wuhy.ToolBox.IISAdminLib.StartWebSite(siteInfo.BindString);
//修改Web.Config文件
if (!WriteWebConfig())
...{
throw new ApplicationException("设置数据库连接字符串时出现错误");
}
}
到此安装项目已经完成,生成并安装就可以调试了.可惜这里好像不能上传项目.