網(wǎng)站完成后,需要部署到目標機器上,方法有很多,直接把文件Copy到目標機器上,執行SQL腳本,配置IIS,這樣可以做到;也可以使用InstallShield這樣到專(zhuān)業(yè)制作軟件來(lái)打包。本篇文章是使用VS2005自帶到安裝部署工具來(lái)制作exe安裝文件。
涉及到到內容包括:使用安裝部署工具,建立數據庫,配置IIS,文件操作,注冊表等。參考了網(wǎng)上關(guān)于使用VS制作安裝程序的文章。環(huán)境:Windows2003 + VS2005 + SQL2005。
注:本文只是為了實(shí)現,沒(méi)有強調方法,變量等的寫(xiě)法標準。
使用VS2005制作安裝程序的時(shí)候,有多種選擇,如下圖:

我想一般常用的可能就最上面的兩種。當使用Web安裝項目時(shí),執行制作好的msi安裝文件時(shí),會(huì )發(fā)現實(shí)際上是添加虛擬目錄而不是添加網(wǎng)站,而且無(wú)法選擇安裝目錄,當然,可以自己寫(xiě)腳本來(lái)對IIS進(jìn)行修改。不過(guò)在這篇文章里,將不采用此模式。本篇文章用的是使用“安裝項目”,就和打包WinForm程序一樣,把Web文件部署到目標機器上,然后重寫(xiě)install方法,配置IIS,執行SQL腳本,修改web.config文件等。
在開(kāi)始整個(gè)過(guò)程之前,需要先了解一下“預編譯”的概念,MSDN上的說(shuō)明是:默認情況下,在用戶(hù)首次請求資源(如網(wǎng)站的一個(gè)頁(yè))時(shí),將動(dòng)態(tài)編譯 ASP.NET 網(wǎng)頁(yè)和代碼文件。第一次編譯頁(yè)和代碼文件之后,會(huì )緩存編譯后的資源,這樣將大大提高隨后對同一頁(yè)提出的請求的效率。請查閱MSDN“預編譯”相關(guān)文檔。
準備一個(gè)文件DBSQL.txt,在里面包含連接數據庫后需要執行的SQL腳本,在本例中使用了簡(jiǎn)單的一個(gè)Create Tabel的SQL。
好了,現在開(kāi)干,吼吼~~~~~~~~~~
打開(kāi)VS2005,打開(kāi)一個(gè)網(wǎng)站項目,這里是打開(kāi)的本地localhost,如圖:

新加一個(gè)頁(yè)面,隨便取個(gè)名字(本文中為ClientCallback.aspx),然后寫(xiě)一個(gè)簡(jiǎn)單的按鈕事件即可,這不是本文的重點(diǎn),由你隨意處理^_^ 再添加web配置文件即web.config,在下添加
此文件到時(shí)候是需要發(fā)布到目標機器上的。(關(guān)于連接數據庫字符串,VS2005里面有專(zhuān)門(mén)的connectionStrings,本文未使用,可查閱幫助)
點(diǎn)擊生成網(wǎng)站,訪(fǎng)問(wèn)頁(yè)面,ok!
選擇菜單生成=》發(fā)布網(wǎng)站:

點(diǎn)擊確定后,網(wǎng)站已經(jīng)生成,這些就是需要部署到目標機器上的所有文件。
點(diǎn)擊菜單文件=》添加=》新建項目,添加安裝部署項目,如圖:

添加安裝項目類(lèi)庫:

刪除默認的Class1.cs,新加一個(gè)安裝程序類(lèi)文件,名為MyInstaller.cs:

在此項目中,添加對System.EnterpriseServices和System.DirectoryServices的引用,在操作IIS的時(shí)候,需要用到。在文件中添加:
using System;
using System.IO;
using System.DirectoryServices;
using System.Reflection;
using System.Data;
using System.Data.SqlClient;
using System.Configuration.Install;
using System.Management;
using System.Collections;
using Microsoft.Win32;
using System.Collections.Specialized;如果編譯的時(shí)候出錯,請添加相關(guān)引用。
修改文件如下圖:

注意要手動(dòng)添加Installer!
將DBSQL.txt文件放到此項目中,在屬性中設置為“嵌入的資源”

回到MyWebSetup項目,點(diǎn)擊查看屬性,可以設置安裝文件到顯示相關(guān)信息,如圖

選擇文件系統,如圖:

然后:

(在文件系統中,在屬性里面可以設置安裝程序默認到安裝路徑)
添加一個(gè)叫bin文件夾并添加在生成網(wǎng)站時(shí)bin目錄下的dll文件,如圖:

在應用程序文件夾下在生成網(wǎng)站時(shí)目錄下的文件,添加后如圖:

選擇“用戶(hù)界面編輯器”,添加兩個(gè)文本框A和B,A將作為安裝新站點(diǎn)后的IIS設置,B將作為數據庫操作時(shí)的參數設置,調整位置后如下:

調整A的屬性,這里只選擇了顯示兩個(gè)輸入框,屬性分別為IISSERVER和PORT,值為localhost和9998,其他的你可以自己調整。如圖:

文本框B的設置如下:

OK,文本框設置完畢。當然,你還可以選擇其他的多種文本框,如協(xié)議什么的。
打開(kāi)自定義操作面板:


然后:

在CustomActionData中輸入:
/dbname=[DBNAME] /server=[DBSERVERNAME] /user=[USERNAME] /pwd=[PASSWORD] /iis=[IISSERVER] /port=[PORT] /targetdir="[TARGETDIR]""
這些參數就是文本框A和B上的輸入框的值,在安裝過(guò)程中可以獲得,然后進(jìn)行處理。
至此,基本的安裝文件已經(jīng)制作完畢。進(jìn)行生成,然后點(diǎn)擊安裝,可以看到文件已經(jīng)復制到了相應到目錄。接下來(lái)就要接收參數對IIS和數據庫進(jìn)行處理。
打開(kāi)SetupClassLibrary項目下的MyInstaller.Designer.cs,修改此文件。
申明幾個(gè)變量:
private System.Data.SqlClient.SqlConnection sqlConn;
private System.Data.SqlClient.SqlCommand Command;
private string DBName;
private string ServerName;
private string AdminName;
private string AdminPwd;
private string iis;
private string port;
private string dir;
public static string VirDirSchemaName = "IIsWebVirtualDir";
private string _target;
private DirectoryEntry _iisServer;
private ManagementScope _scope;
private ConnectionOptions _connection;
連接數據庫服務(wù)器到方法:
#region ConnectDatabase 連接數據庫
private bool ConnectDatabase()
{
if (Command.Connection.State != ConnectionState.Open)
{
try
{
Command.Connection.Open();
}
catch(Exception e)
{
return false;
}
}
return true;
}
#endregion
如果不能正確連接數據庫服務(wù)器,請檢查你的連接字符串,或者將連接字符串寫(xiě)入文件查看。不好意思,我不知道如何對這種安裝部署程序進(jìn)行debug,sorry咯!
讀取SQL文件的方法:
#region GetSql 從文件中讀取SQL,在讀取包含SQL腳本的文件時(shí)需要用到,參考自MSDN
private string GetSql(string Name)
{
try
{
Assembly Asm = Assembly.GetExecutingAssembly();
Stream strm = Asm.GetManifestResourceStream(Asm.GetName().Name + "." + Name);
StreamReader reader = new StreamReader(strm);
return reader.ReadToEnd();
}
catch (Exception getException)
{
throw new ApplicationException(getException.Message);
}
}
#endregion
可以將此需要執行的SQL腳本放在此文本中
執行SQL語(yǔ)句的方法:
#region ExecuteSql 執行SQL語(yǔ)句,參考自MSDN
private void ExecuteSql(string DataBaseName, string sqlstring)
{
Command = new System.Data.SqlClient.SqlCommand(sqlstring, sqlConn);
if (ConnectDatabase())
{
try
{
Command.Connection.ChangeDatabase(DataBaseName);
Command.ExecuteNonQuery();
}
finally
{
Command.Connection.Close();
}
}
}
#endregion
創(chuàng )建數據庫及數據庫表:
#region CreateDBAndTable 創(chuàng )建數據庫及數據庫表,參考自MSDN
protected bool CreateDBAndTable(string DBName)
{
bool Restult = false;
try
{
ExecuteSql("master", "USE MASTER IF EXISTS (SELECT NAME FROM SYSDATABASES WHERE NAME='" + DBName + "') DROP DATABASE " + DBName);
ExecuteSql("master", "CREATE DATABASE " + DBName);
ExecuteSql(DBName, GetSql("DBSQL.txt"));
Restult = true;
}
Catch
{
}
return Restult;
}
#endregion
從備份文件恢復數據庫及數據庫表
#region RestoreDB 從備份文件恢復數據庫及數據庫表
///
/// 從備份文件恢復數據庫及數據庫表
///
/// 數據庫名
/// 配件中數據庫腳本資源的名稱(chēng)
///
protected bool RestoreDB(string DBName)
{
dir = this.Context.Parameters["targetdir"];
bool Restult = false;
string MSQL = "RESTORE DATABASE " + DBName +
" FROM DISK = '" + dir + @"data.bak' " +
" WITH MOVE 'Test' TO '" + @"c:"" + DBName + ".mdf', " +
" MOVE 'Test_log' TO '" + @"c:"" + DBName + ".ldf' ";
try
{
ExecuteSql("master", "USE MASTER IF EXISTS (SELECT NAME FROM SYSDATABASES WHERE NAME='" + DBName + "') DROP DATABASE " + DBName);
ExecuteSql("master", MSQL);
Restult = true;
}
finally
{
// 刪除備份文件
try
{
File.Delete(dir + @"data.bak");
}
catch
{
}
}
return Restult;
}
#endregion
這里可以到注冊表讀取SQL Server的安裝路徑,把恢復后的數據庫文件放到data目錄地下。在本例中,只是實(shí)現了恢復,并未進(jìn)行標準的操作。其中Test和Test_log時(shí)備份時(shí)數據庫的文件信息。如果想要從備份文件中恢復,請把文件包含到項目里并且設置和DBSQL.txt一樣,嵌入到程序里。最后執行刪除。不過(guò)我想應該有辦法不把文件先安裝到目標機器上,而是有方法想讀取DBSQL.txt文件一樣,直接恢復數據庫,不過(guò)確實(shí)沒(méi)想到辦法,失??!
網(wǎng)站安裝好后,需要設置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"]["appSettings"])
{
if (Node.Name == "add")
{
if (Node.Attributes.GetNamedItem("key").Value == "ConnectionString")
{
Node.Attributes.GetNamedItem("value").Value = String.Format("Persist Security Info=False;Data Source={0};database={1};User ID={2};Password={3};Packet Size=4096;Pooling=true;Max Pool Size=100;Min Pool Size=1", ServerName, DBName, AdminName, AdminPwd);
FoundIt = true;
}
}
}
if (!FoundIt)
{
throw new InstallException("Error when writing the config file: web.config");
}
xmlDocument.Save(FileInfo.FullName);
return FoundIt;
}
#endregion
#region WriteRegistryKey 寫(xiě)注冊表。安裝部署中,直接有一個(gè)注冊表編輯器,可以在那里面設置。
private void WriteRegistryKey()
{
// 寫(xiě)注冊表
RegistryKey hklm = Registry.LocalMachine;
RegistryKey cqfeng = hklm.OpenSubKey("SOFTWARE", true);
RegistryKey F = cqfeng.CreateSubKey("cqfeng");
F.SetValue("FilePath", "kkkk");
}
#endregion
操作IIS,建立網(wǎng)站等??蓞⒖迹?/span>
用VS2005制作網(wǎng)頁(yè)對IIS進(jìn)行操作
#region Connect 連接IIS服務(wù)器
public bool Connect()
{
if (iis == null)
return false;
try
{
_iisServer = new DirectoryEntry("IIS://" + iis + "/W3SVC/1");
_target = iis;
_connection = new ConnectionOptions();
_scope = new ManagementScope(@"""" + iis + @""root"MicrosoftIISV2", _connection);
_scope.Connect();
}
catch
{
return false;
}
return IsConnected();
}
public bool IsConnected()
{
if (_target == null || _connection == null || _scope == null) return false;
return _scope.IsConnected;
}
#endregion
#region IsWebSiteExists 判斷網(wǎng)站是否已經(jīng)存在
public bool IsWebSiteExists(string serverID)
{
try
{
string siteName = "W3SVC/" + serverID;
ManagementObjectSearcher searcher = new ManagementObjectSearcher(_scope, new ObjectQuery("SELECT * FROM IIsWebServer"), null);
ManagementObjectCollection webSites = searcher.Get();
foreach (ManagementObject webSite in webSites)
{
if ((string)webSite.Properties["Name"].Value == siteName)
return true;
}
return false;
}
catch
{
return false;
}
}
#endregion
#region GetNextOpenID 獲得一個(gè)新的ServerID
private int GetNextOpenID()
{
DirectoryEntry iisComputer = new DirectoryEntry("IIS://localhost/w3svc");
int nextID = 0;
foreach (DirectoryEntry iisWebServer in iisComputer.Children)
{
string sname = iisWebServer.Name;
try
{
int name = int.Parse(sname);
if (name > nextID)
{
nextID = name;
}
}
catch
{
}
}
return ++nextID;
}
#endregion
#region CreateWebsite 添加網(wǎng)站
public string CreateWebSite(string serverID, string serverComment, string defaultVrootPath, string HostName, string IP, string Port)
{
try
{
ManagementObject oW3SVC = new ManagementObject(_scope, new ManagementPath(@"IIsWebService='W3SVC'"), null);
if (IsWebSiteExists(serverID))
{
return "Site Already Exists...";
}
ManagementBaseObject inputParameters = oW3SVC.GetMethodParameters("CreateNewSite");
ManagementBaseObject[] serverBinding = new ManagementBaseObject[1];
serverBinding[0] = CreateServerBinding(HostName, IP, Port);
inputParameters["ServerComment"] = serverComment;
inputParameters["ServerBindings"] = serverBinding;
inputParameters["PathOfRootVirtualDir"] = defaultVrootPath;
inputParameters["ServerId"] = serverID;
ManagementBaseObject outParameter = null;
outParameter = oW3SVC.InvokeMethod("CreateNewSite", inputParameters, null);
// 啟動(dòng)網(wǎng)站
string serverName = "W3SVC/" + serverID;
ManagementObject webSite = new ManagementObject(_scope, new ManagementPath(@"IIsWebServer='" + serverName + "'"), null);
webSite.InvokeMethod("Start", null);
return (string)outParameter.Properties["ReturnValue"].Value;
}
catch (Exception ex)
{
return ex.Message;
}
}
public ManagementObject CreateServerBinding(string HostName, string IP, string Port)
{
try
{
ManagementClass classBinding = new ManagementClass(_scope, new ManagementPath("ServerBinding"), null);
ManagementObject serverBinding = classBinding.CreateInstance();
serverBinding.Properties["Hostname"].Value = HostName;
serverBinding.Properties["IP"].Value = IP;
serverBinding.Properties["Port"].Value = Port;
serverBinding.Put();
return serverBinding;
}
catch
{
return null;
}
}
#endregion
好了,準備工作已經(jīng)做完,現在開(kāi)始寫(xiě)最重要的Install方法了
整個(gè)方法寫(xiě)完后如下:
#region Install 安裝
///
/// 安裝數據庫
///
///
public override void Install(IDictionary stateSaver)
{
base.Install(stateSaver);
dir = this.Context.Parameters["dir"];
DBName = this.Context.Parameters["DBNAME"].ToString();
ServerName = this.Context.Parameters["server"].ToString();
AdminName = this.Context.Parameters["user"].ToString();
AdminPwd = this.Context.Parameters["pwd"].ToString();
iis = this.Context.Parameters["iis"].ToString(); ;
port = this.Context.Parameters["port"].ToString();
//寫(xiě)入獲取的安裝程序中的變量,此段代碼為調試用可以不添加
this.sqlConn.ConnectionString = "Packet size=4096;User ID=" + AdminName + ";Data Source=" + ServerName + ";Password=" + AdminPwd + ";Persist Security Info=False;Integrated Security=false";
// 執行SQL 安裝數據庫 可選擇時(shí)恢復或者時(shí)直接創(chuàng )建
if(!CreateDBAndTable(DBName))
{
throw new ApplicationException("創(chuàng )建數據庫時(shí)出現嚴重錯誤!");
}
// 從備份數據庫文件恢復數據庫
/*
if (!RestoreDB(DBName))
{
throw new ApplicationException("恢復數據庫時(shí)出現嚴重錯誤!");
}
*/
// 添加網(wǎng)站
Connect();
//string serverID = GetNextOpenID().ToString();
//string serverComment = websitenName;
// 下面的信息為測試,可以自己編寫(xiě)文本框來(lái)接收用戶(hù)輸入信息
string serverID = "5555";
string serverComment = "cqfeng";
string defaultVrootPath = this.Context.Parameters["targetdir"];
if (defaultVrootPath.EndsWith(@"""))
{
defaultVrootPath = defaultVrootPath.Substring(0, defaultVrootPath.Length-1);
}
string HostName = "";
string IP = "";
string Port = port;
string sReturn = CreateWebSite(serverID, serverComment, defaultVrootPath, HostName, IP, Port);
// 修改web.config
if (!WriteWebConfig())
{
throw new ApplicationException("設置數據庫連接字符串時(shí)出現錯誤");
}
// 寫(xiě)注冊表
WriteRegistryKey();
}
#endregion
刪除時(shí)的方法。在本文中未詳細操作,比如刪除站點(diǎn),刪除數據庫等。如果需要,請你自己補足
#region Uninstall 刪除
public override void Uninstall(IDictionary savedState)
{
if (savedState == null)
{
throw new ApplicationException("未能卸載!");
}
else
{
base.Uninstall(savedState);
}
}
#endregion
編譯,然后選擇安裝,如圖:

第一圖:

第二圖:

第三圖:

抱歉,我不知道在這里怎么使登錄密碼框輸入時(shí)顯示為*號
第四圖:

第五圖:

安裝:

安裝完成:

安裝后的IIS

安裝目錄:

安裝后的數據庫:

至此,一個(gè)簡(jiǎn)單的部署web程序的exe文件已經(jīng)完成,當然,省略了很多東西,比如,對安裝機器的判斷(IIS版本,Framework版本,SQL Server版本等),IIS站點(diǎn)屬性等設置(默認頁(yè)面,訪(fǎng)問(wèn)權限,執行權限等),卸載程序時(shí)應該刪除的東西等等。這些東西,在MSDN里面可以查找到相關(guān)說(shuō)明,如果你需要,只有辛苦一下了,嘿嘿。
相信有了這些,自己用WinForm來(lái)寫(xiě)安裝程序也時(shí)可以了哈。
問(wèn)題:安裝的時(shí)候輸入密碼時(shí)無(wú)法顯示為*號;安裝文件夾中出現編譯后的安裝類(lèi)編譯后的dll文件。哪位大哥對這些比較熟悉的,指點(diǎn)指點(diǎn)……………..
寫(xiě)得不是很仔細,如果有問(wèn)題或者錯誤,請你自己多調試一下!
聯(lián)系客服