c#自動更新+安裝程序的制作 (轉) http://blog.csdn.net/myhuli120/article/details/6927588
一、自動更新的實現
讓客戶端實現自動更新,通常做法是在客戶端部署一個單獨的自動更新程序。主程序啟動后,訪問服務端,檢查配置文件是 否有更新版本,有更新版本就啟動更新程序,由更新負責下載更新版本,並更新客戶端程序,流程如下:
1)版本判斷:
客戶端和服務端都部署同一個版本文件,客戶端登陸時發送驗證給服務端判斷版本是否一致。
Version.xml代碼
<iq xmlns="http://www.dynastech.com/xmtp" from="*@domcool.local/updater" to="*@domcool.local/updater" type="get"
id="508f3e88-4bb0-4585-a5c6-cc41ef57fef3"> <query xmlns="http://www.dynastech.com/xmtp/disco#update" version="20090922" lastUpdateTime="2009-09-22"
fileUrl="http://172.0.0.1/UCCompanion/UCCompanionSetup(0922).zip <"> x xmlns="http://www.dynastech.com/xmtp/item"> </x> </query> </iq>
版本文件主要比較服務端Version.xml文件和客戶端Version.xml文件中Version(版本號)是否一致,如果服務端Version屬性
大於客戶端的Version屬性,則通過服務端的fileUrl屬性獲取新版本的下載地址。供更新程序使用。
2)刪除原有更新包
所有客戶端更新文件均下載到C:\Documents and Settings\當前用戶名\Local Settings\Temp 文件夾內,當客戶端運行后首先判
斷是否有新更新包需要下載,如果沒有則判斷該臨時文件夾內是否有舊有安裝文件,如果存在,則刪除舊有安裝文件。
private void RemoveOldSetupFile()
{
try { string temp = System.Environment.GetEnvironmentVariable("TEMP"); string folder = new DirectoryInfo(temp).FullName; if (File.Exists(folder + @"\" + setupName + ".exe")) { File.Delete(folder + @"\" + setupName + ".exe"); } if (File.Exists(folder + @"\" + setupName + ".msi")) { File.Delete(folder + @"\" + setupName + ".msi"); } } catch { } }
備注:關於獲取系統特殊文件夾的方法見博客http://www.cnblogs.com/thornfield_he/archive/2009/09/22/1571719.html
3)啟動下載程序
下載程序和客戶端程序是相互獨立的,可以通過客戶端開啟新線程啟動下載程序。下載程序在文件下載結束后可以關掉客戶端程序, 並開啟新線程啟動安裝程序進行安裝。
private void Update()
{
if (ShouldUpdate(query.Version, this.version)) { MessageBox.Show("請更新客戶端文件到版本[" + query.Version + "]", "更新提示", MessageBoxButtons.OK,
MessageBoxIcon.Asterisk);
System.Diagnostics.Process.Start(Application.StartupPath + @"\AutoUpdater.exe", query.FileUrl); } else { RemoveOldSetupFile(); } } private bool ShouldUpdate(string serverVersion, string localVersion) { if (!string.IsNullOrEmpty(serverVersion) && !string.IsNullOrEmpty(localVersion)) { return serverVersion.CompareTo(localVersion) > 0; } return true; }
調用AutoUpdater.exe文件時需要傳入文件下載地址。
System.Diagnostics.Process.Start(Application.StartupPath + @"\AutoUpdater.exe", query.FileUrl);
4)下載程序代碼
下載程序界面
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Net; using System.IO; using System.Threading; using System.Diagnostics; namespace AutoUpdater { public partial class MainForm : Form { private WebClient client; private string URl; private string fileName; private string path; private const string applicationFile = "Setup"; public MainForm(string url) { InitializeComponent(); this.URl = url; client = new WebClient(); client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged); client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted); client.Proxy = WebRequest.DefaultWebProxy; client.Proxy.Credentials = new NetworkCredential(); this.Hide(); //Thread thread = new Thread(UpdateFile); //Thread.Sleep(15000); //thread.Start(); UpdateFile(); } public MainForm() { InitializeComponent(); } /// <summary> /// 下載完成調用 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) { label1.Text = "文件接收完成"; UnZip(); RunUpdate(); } /// <summary> /// 下載進度條 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { this.progressBar1.Value = e.ProgressPercentage; } /// <summary> /// 開始下載 /// </summary> private void StartDownload() { fileName = URl.Substring(URl.LastIndexOf("/") + 1, URl.Length - URl.LastIndexOf("/") - 1); path = GetTempFolder(); try { WebRequest myre = WebRequest.Create(URl); } catch (Exception ex) { MessageBox.Show(ex.Message, "Error"); } try { label1.Text = "開始下載文件..."; client.DownloadFileAsync(new Uri(URl), path + @"\" + fileName); } catch (WebException exp) { label1.Text = exp.Message; } } /// <summary> /// 解壓壓縮包,格式必須是*.zip,否則不能解壓 /// 因為是調用Windows內部api進行解壓,只能夠識別zip壓縮包 /// 必須添加C:\WINDOWS\system32\shell32.dll的引用 /// </summary> private void UnZip() { try { Shell32.ShellClass sc = new Shell32.ShellClass(); Shell32.Folder SrcFolder = sc.NameSpace(this.path + @"\" + this.fileName); Shell32.Folder DestFolder = sc.NameSpace(this.path); Shell32.FolderItems items = SrcFolder.Items(); DestFolder.CopyHere(items, 20); } catch (Exception ex) { MessageBox.Show(ex.Message); } } /// <summary> /// 獲取下載文件夾地址及解壓文件存放地址 /// 此地址默認為C:\Documents and Settings\當前用戶名\Local Settings\Temp 文件夾 /// </summary> /// <returns></returns> private string GetTempFolder() { string folder = System.Environment.GetEnvironmentVariable("TEMP"); return new DirectoryInfo(folder).FullName; } /// <summary> /// 開始下載文件 /// </summary> private void UpdateFile() { this.Hide(); //如果臨時文件夾存在setup安裝文件,就直接調用安裝文件 if (File.Exists(GetTempFolder() + @"\" + applicationFile + ".exe") && File.Exists(GetTempFolder() +
@"\" + applicationFile + ".msi")) { label1.Text = "開始下載文件..."; this.progressBar1.Value = this.progressBar1.Maximum; label1.Text = "文件接收完成"; RunUpdate(); } //如果臨時文件夾不存在setup安裝文件,就從網絡下載 else { RemoveSetupFile(); StartDownload(); } } /// <summary> /// 清除舊有已下載的安裝文件 /// </summary> private static void RemoveSetupFile() { try { string temp = System.Environment.GetEnvironmentVariable("TEMP"); string folder = new DirectoryInfo(temp).FullName; if (File.Exists(folder + @"\" + applicationFile + ".exe")) { File.Delete(folder + @"\" + applicationFile + ".exe"); } if (File.Exists(folder + @"\" + applicationFile + ".msi")) { File.Delete(folder + @"\" + applicationFile + ".msi"); } } catch { } } /// <summary> /// 下載完畢,開始執行更新程序 /// </summary> private void RunUpdate() { try { foreach (Process p in Process.GetProcesses()) { if (p.ProcessName.ToLower().StartsWith("uccompanion")) { if (MessageBox.Show("UCCompanion正在運行,是否關閉當前程序安裝更新?", "安裝UCCompanion",
MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { p.Kill(); Process.Start(GetTempFolder() + @"\" + applicationFile + ".exe"); } else { MessageBox.Show("UCCompanion下載完成,將在下次啟動時提醒更新!"); } } } } catch (Exception ex) { MessageBox.Show(ex.Message); } finally { this.Close(); } } /// <summary> /// 重載WindProc判斷點擊關閉按鈕(X)時,隱藏程序界面 /// </summary> /// <param name="msg"></param> protected override void WndProc(ref Message msg) { const int WM_SYSCOMMAND = 0x0112; const int SC_CLOSE = 0xF060; if (msg.Msg == WM_SYSCOMMAND && ((int)msg.WParam == SC_CLOSE)) { this.Hide(); return; } base.WndProc(ref msg); } /// <summary> /// 雙擊圖標彈出界面 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void icon_notify_MouseDoubleClick(object sender, MouseEventArgs e) { this.Show(); this.WindowState = FormWindowState.Normal; } /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void MainForm_SizeChanged(object sender, EventArgs e) { if (this.WindowState == FormWindowState.Minimized) { this.Hide(); } } private void MainForm_Load(object sender, EventArgs e) { this.Hide(); } } static class Program { /// <summary> /// 啟動,接收傳入網址作為參數 /// </summary> /// <param name="agr"></param> [STAThread] static void Main(string[] agr) { if (agr.Length == 1 && agr[0].StartsWith(@"http://")) { MainForm form = new MainForm(agr[0]); Application.Run(form); } } } }
將AutoUpdater項目生成的文件添加到客戶端文件中,在客戶端的Update()方法里調用updater,實現更新文件的下載。
以上就已經實現了自動更新功能,下面將討論文件安裝包的制作。
二、安裝包的制作
1)創建安裝項目
2)鼠標右擊Setup項目選擇>視圖,可以看到制作安裝包常見的視圖有以下幾個
最常用的視圖有“文件系統”,“用戶界面”和“啟動條件”。
3)指定安裝屬性
鼠標左鍵單擊項目名稱,記住是左鍵單擊,然后點擊屬性標簽,注意:不是右擊的屬性
a.需要注意的是Version屬性,每次版本更新時Version值必須后面的版本大於前面的版本。每次更改Version值時Projectcode會更改一次。
其中你修改安裝項目的版本號時,比如從v1.00 到1.01,在你再次生成項目的時候,會提示你是否允許修改ProductCode,選擇"是", 程序會自動修改ProductCode,選擇否將保持相同的ProductCode,即不能自動卸載舊的版本.
b.在以后版本中要確認和以前的版本兩個版本有不同的ProductCode和相同的UpgradeCode
c.manufacturer屬性指定制造商名稱。
d.detectnewerinstalledversion屬性選擇為true,
e.removepreviousversions選擇為true
鼠標左鍵單擊項目名稱,此次是右鍵單擊,然后點擊屬性,彈出屬性頁,選擇“系統必備”。
在打開的系統必備頁中,選中如下中的選擇項,這個很重要!!!!!1!!!!!選上以后,在生成的安裝文件包中 包含.netframework組件.(這個選項默認是沒有選中的)。
4)文件系統視圖
文件系統視圖左側根目錄樹下有3個子節點。
a.應用程序文件夾:將所有待打包的應用程序的可執行文件和相應的類庫和組件拖動到該目錄下。該目錄可以創建子 目錄,項目安裝完畢以后的文件夾結構會和該目錄下結構一致。
如圖:
然后右擊左邊的"應用程序文件夾"打開屬性對話框,修改文件釋放路徑,[ProgramFilesFolder][Manufacturer]\[ProductName]。 安裝程序默認安裝目錄會是"c:\programm file\制造商名稱\安裝解決方案名稱";
b.用戶的“程序”菜單和用戶桌面:用於在開始菜單創建文件快捷方式
在應用程序文件夾中將需要生成的快捷方式的文件添加快捷方式並拖動到用戶的“程序”菜單和用戶桌面
c.添加文件卸載功能
在添加你的應用程序項目的時候,多添加一個msiexec.exe進去,這個文件在c:\windows\system32文件夾下。
為其在程序菜單添加一個快捷方式,把他的名字改成"Uninstall.exe",指定Icon快捷方式顯示的圖標。然后下面我們 要的做的就是查找這個部署項目的ProductCode了,鼠標左鍵單擊項目名稱,記住是左鍵單擊,然后點擊屬性標簽,注意: 不是右擊的屬性,這個區別很大,這時你就可以看到ProductCode了
然后打開你創建的那個卸載程序的快捷方式的屬性對話框,在Aguements屬性中輸入"/x {ProductCode}"
5)用戶界面視圖
在“歡迎使用”后,“安裝文件夾”前添加“許可協議”對話框。
licensefile選擇協議,協議的格式為rtf。
6)啟動條件視圖
為啟動安裝程序制定最低framework要求。
7)實現安裝、卸載過程中的其他額外的操作。比如安裝結束后啟動程序,卸載程序后同時刪除網絡下載打安裝包等功能。
a.新建一個空的項目InstallCompenent,步驟為:解決方案->右鍵添加->新建項目->選擇"空項目"-> 輸入名稱"InstallCompenent"->確定,完成項目的添加.
b.在InstallCompenent項目中右鍵->添加->新建項->選擇安裝程序類->輸入名稱"Installer",完成installer類的添加.
修改代碼為:
using System;
using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Configuration.Install; using System.Reflection; using System.IO; namespace InstallCompenent { [RunInstaller(true)] public partial class UccompanionInstaller : Installer { private const string zipPacket = "UCCompanionSetup(0918).zip"; /// <summary> /// 應用程序入口 /// </summary> public static void Main() { } /// <summary> /// 構造函數 /// </summary> public UccompanionInstaller() { InitializeComponent(); } /// <summary> /// 重寫安裝完成后函數 /// 實現安裝完成后自動啟動已安裝的程序 /// </summary> /// <param name="savedState"></param> protected override void OnAfterInstall(IDictionary savedState) { base.OnAfterInstall(savedState); Assembly asm = Assembly.GetExecutingAssembly(); string path = asm.Location.Remove(asm.Location.LastIndexOf("\\")) + "\\"; System.Diagnostics.Process.Start(path + "\\UCCompanion.exe"); } /// <summary> /// 重寫安裝過程方法 /// </summary> /// <param name="stateSaver"></param> public override void Install(IDictionary stateSaver) { base.Install(stateSaver); } /// <summary> /// 重寫安裝之前方法 /// </summary> /// <param name="savedState"></param> protected override void OnBeforeInstall(IDictionary savedState) { base.OnBeforeInstall(savedState); } /// <summary> /// 重寫卸載方法 /// 卸載程序后也刪除程序的安裝包 /// </summary> /// <param name="savedState"></param> public override void Uninstall(IDictionary savedState) { string temp = System.Environment.GetEnvironmentVariable("TEMP"); string folder = new DirectoryInfo(temp).FullName; if (File.Exists(folder + @"\setup.exe")) { File.Delete(folder + @"\setup.exe"); } if (File.Exists(folder + @"\setup.msi")) { File.Delete(folder + @"\setup.msi"); } if (File.Exists(folder + @"\"+zipPacket)) { File.Delete(folder + @"\"+zipPacket); } base.Uninstall(savedState); } /// <summary> /// 重寫回滾方法 /// </summary> /// <param name="savedState"></param> public override void Rollback(IDictionary savedState) { base.Rollback(savedState); } } }
c.在安裝項目中右鍵->添加項目輸出->選擇"項目"->InstallCompenent.完成主輸出項目的添加.
d.打開自定義操作編輯器,在安裝->右鍵->添加自定義操作->選擇"應用程序文件夾"->選擇"主輸出來自InstallCompenent",完成添加.
好了,點擊“生成解決方案”,即可以生成帶有卸載功能的安裝程序了。