使用VS2005的 ClickOnce 技術實現按需下載組件
預設情況下,首次運行 ClickOnce 應用程式時,會下載該應用程式中包含的所有程式集。但是一些特殊的情境我們可能不希望它這麼幹,而是希望按照一定規測或者需要用到某個組件的時候再下載。
比如我們可能會有下面一些應用情境的需求:
1、我開發的這個用戶端程式是要收費的。但是免費使用者也可以使用部分功能。我在技術實現上把收費使用者使用的功能封裝到了A.dll 組件了,我希望免費使用者根本無法獲得A.dll,只有收費使用者才能獲得A.dll,並載入A.dll中的收費功能。
2、我整個應用程式非常大,我不希望我每次更新,使用者都需要把所有應用程式都下載下來,使用者應該只需要下載他用到的功能組件。
下面我們就來實現一個簡單的按需下載的例子,我們完全可以在這個例子基礎上,實現上述提到的應用情境1。
示範步驟:
一、建立一個 類庫 Project
定義類庫輸出應用程式名稱為:OnDemandAssembly,即這個類庫編譯後產生的檔案名稱為 OnDemandAssembly.dll
這個類庫中有如下代碼,我們用這段代碼來類比上面提到情境中的一些需要按需載入的功能或者是收費的功能,我們將在主程式中調用這個功能:
using System;
namespace OnDemandAssembly
{
public class DynamicClass
{
public string Message
{
get
{
return "郭紅俊測試ClickOnce按需載入功能。";
}
}
}
}
二、建立一個 Window 應用程式
我們將在這個Window 應用程式中載入上述類庫。
為了便於我們示範這個程式,請確保這個Window程式具備以下功能:
1、有一個 textbox 控制項,這個控制項在 Window 的OnLoad 事件中,把應用程式的目錄顯示在這裡,方便我們去監控是否上述 OnDemandAssembly.dll 組件被載入了。
代碼如下:
private void Form1_Load(object sender, EventArgs e)
{
this.textBox1.Text = Application.StartupPath;
}
2、由一個 Button 按鈕,點擊這個按鈕後,我們把 OnDemandAssembly.dll 組件中的 Message 資訊顯示出來,代碼如下:
private void button1_Click(object sender, EventArgs e)
{
DynamicClass o = new DynamicClass();
MessageBox.Show(o.Message);
}
Window程式關於ClickOnce設定比較特殊的地方:
我們來配置ClickOnce發布的一些特殊參數,確保可以按需下載需要的組件
本文中沒有詳細描述ClickOnce設定的各個步驟和參數,如果你對ClickOnce不是很熟悉,建議你首先看一些ClickOnce的入門文章再來看本文。
比如:http://blog.oracle.com.cn/155011/viewspace_3603.html 這裡提供的ClickOnce 文章(這裡是提供了一個word壓縮檔下載,文章在壓縮的Word檔案中)
選擇我們的Window程式,在右鍵菜單中選擇屬性,在屬性頁面中選擇發布(Publish)標籤頁。
然後點擊 Application File 按鈕,我們來設定,需要發布的檔案。如:
上述按鈕開啟的視窗如下:
我們在開啟的視窗中,設定 OnDemandAssembly.DLL 檔案的 發布狀態為 Include,
並在 Download Group 中為OnDemandAssembly.DLL 檔案建立的一個下載組,我們這裡把這個新的下載組命名為 DemandAssembly01 。
說明:
預設情況下,我們用ClickOnce 第一次安裝,或者升級程式的時候,系統只下載 Required 組的檔案,其他組的檔案系統不下載,需要我們自己編碼來下載。
其他ClickOnce的設定跟我們平常使用的時候一樣來設定。本文忽略這部分。
我們這時候發布這個程式,我們下載安裝後,就會發現OnDemandAssembly.DLL 檔案不在安裝目錄下,點擊這個程式的按鈕,就會報異常,找不到需要的應用程式集
OnDemandAssembly.dll 組件。
下面我們來編碼實現,如果應用程式找不到 OnDemandAssembly.dll 組件,就從網上下載這個組件的功能
1、視窗的建構函式中增加 AssemblyResolve 事件的處理邏輯
public Form1()
{
InitializeComponent();
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
}
說明:AppDomain.AssemblyResolve 事件 在對程式集的解析失敗時發生。
2、下面我們編碼實現 AssemblyResolve 事件的處理邏輯
using System.Reflection;
using System.Deployment.Application;
// 如果我們有多個檔案都需要按需下載的話,每個檔案影射到那個下載分組,就是這個實體來記錄的
Dictionary<String, String> DllMapping = new Dictionary<String, String>();
public Form1()
{
InitializeComponent();
DllMapping["OnDemandAssembly"] = "DemandAssembly01";
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
Assembly newAssembly = null;
// 是 ClickOnce 部署方式
if (ApplicationDeployment.IsNetworkDeployed)
{
ApplicationDeployment deploy = ApplicationDeployment.CurrentDeployment;
// Get the DLL name from the Name argument.
string[] nameParts = args.Name.Split(',');
string dllName = nameParts[0];
string downloadGroupName = DllMapping[dllName];
// 下載所需要的檔案
try
{
deploy.DownloadFileGroup(downloadGroupName);
}
catch (DeploymentException de)
{
MessageBox.Show("Downloading file group failed. Group name: " + downloadGroupName + "; DLL name: " + args.Name);
throw (de);
}
// 載入組件到應用程式集
// Load the assembly.
// Assembly.Load() doesn't work here, as the previous failure to load the assembly
// is cached by the CLR. LoadFrom() is not recommended. Use LoadFile() instead.
try
{
newAssembly = Assembly.LoadFile(Application.StartupPath + @"/" + dllName + ".dll");
}
catch (Exception e)
{
throw (e);
}
}
else
{
//Major error - not running under ClickOnce, but missing assembly. Don't know how to recover.
throw (new Exception("Cannot load assemblies dynamically - application is not deployed using ClickOnce."));
}
return (newAssembly);
}
一些問題說明:
Q:上述代碼中,如果我們這個組件OnDemandAssembly.dll 有最新版本了,並且發布了,但是用戶端還是一個老的版本的話,這個邏輯我們沒有處理呀?
A:問題這個問題,就是代表你對 ClickOnce 的原理還是不懂,OnDemandAssembly.dll 有最新版本,那你就必須再重新發布一個版本的ClickOnce代碼,這時候發布的版本號碼就不一樣了。用戶端下不同版本號碼的ClickOnce 程式是存在不同目錄下的(當然伺服器也是一樣)。
系統檢查到你的新版本程式發布後,會為新的版本號碼建立目錄,這個目錄下如果你沒有用過OnDemandAssembly.dll 的功能,是不會有OnDemandAssembly.dll 組件的,繼而上述問題是可以不用考慮的。
參考資料:
MSDN:Downloading Assemblies On Demand with the ClickOnce Deployment API
使用VS2005的 ClickOnce 技術實現按需下載組件
預設情況下,首次運行 ClickOnce 應用程式時,會下載該應用程式中包含的所有程式集。但是一些特殊的情境我們可能不希望它這麼幹,而是希望按照一定規測或者需要用到某個組件的時候再下載。
比如我們可能會有下面一些應用情境的需求:
1、我開發的這個用戶端程式是要收費的。但是免費使用者也可以使用部分功能。我在技術實現上把收費使用者使用的功能封裝到了A.dll 組件了,我希望免費使用者根本無法獲得A.dll,只有收費使用者才能獲得A.dll,並載入A.dll中的收費功能。
2、我整個應用程式非常大,我不希望我每次更新,使用者都需要把所有應用程式都下載下來,使用者應該只需要下載他用到的功能組件。
下面我們就來實現一個簡單的按需下載的例子,我們完全可以在這個例子基礎上,實現上述提到的應用情境1。
示範步驟:
一、建立一個 類庫 Project
定義類庫輸出應用程式名稱為:OnDemandAssembly,即這個類庫編譯後產生的檔案名稱為 OnDemandAssembly.dll
這個類庫中有如下代碼,我們用這段代碼來類比上面提到情境中的一些需要按需載入的功能或者是收費的功能,我們將在主程式中調用這個功能:
using System;
namespace OnDemandAssembly
{
public class DynamicClass
{
public string Message
{
get
{
return "郭紅俊測試ClickOnce按需載入功能。";
}
}
}
}
二、建立一個 Window 應用程式
我們將在這個Window 應用程式中載入上述類庫。
為了便於我們示範這個程式,請確保這個Window程式具備以下功能:
1、有一個 textbox 控制項,這個控制項在 Window 的OnLoad 事件中,把應用程式的目錄顯示在這裡,方便我們去監控是否上述 OnDemandAssembly.dll 組件被載入了。
代碼如下:
private void Form1_Load(object sender, EventArgs e)
{
this.textBox1.Text = Application.StartupPath;
}
2、由一個 Button 按鈕,點擊這個按鈕後,我們把 OnDemandAssembly.dll 組件中的 Message 資訊顯示出來,代碼如下:
private void button1_Click(object sender, EventArgs e)
{
DynamicClass o = new DynamicClass();
MessageBox.Show(o.Message);
}
Window程式關於ClickOnce設定比較特殊的地方:
我們來配置ClickOnce發布的一些特殊參數,確保可以按需下載需要的組件
本文中沒有詳細描述ClickOnce設定的各個步驟和參數,如果你對ClickOnce不是很熟悉,建議你首先看一些ClickOnce的入門文章再來看本文。
比如:http://blog.oracle.com.cn/155011/viewspace_3603.html 這裡提供的ClickOnce 文章(這裡是提供了一個word壓縮檔下載,文章在壓縮的Word檔案中)
選擇我們的Window程式,在右鍵菜單中選擇屬性,在屬性頁面中選擇發布(Publish)標籤頁。
然後點擊 Application File 按鈕,我們來設定,需要發布的檔案。如:
上述按鈕開啟的視窗如下:
我們在開啟的視窗中,設定 OnDemandAssembly.DLL 檔案的 發布狀態為 Include,
並在 Download Group 中為OnDemandAssembly.DLL 檔案建立的一個下載組,我們這裡把這個新的下載組命名為 DemandAssembly01 。
說明:
預設情況下,我們用ClickOnce 第一次安裝,或者升級程式的時候,系統只下載 Required 組的檔案,其他組的檔案系統不下載,需要我們自己編碼來下載。
其他ClickOnce的設定跟我們平常使用的時候一樣來設定。本文忽略這部分。
我們這時候發布這個程式,我們下載安裝後,就會發現OnDemandAssembly.DLL 檔案不在安裝目錄下,點擊這個程式的按鈕,就會報異常,找不到需要的應用程式集
OnDemandAssembly.dll 組件。
下面我們來編碼實現,如果應用程式找不到 OnDemandAssembly.dll 組件,就從網上下載這個組件的功能
1、視窗的建構函式中增加 AssemblyResolve 事件的處理邏輯
public Form1()
{
InitializeComponent();
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
}
說明:AppDomain.AssemblyResolve 事件 在對程式集的解析失敗時發生。
2、下面我們編碼實現 AssemblyResolve 事件的處理邏輯
using System.Reflection;
using System.Deployment.Application;
// 如果我們有多個檔案都需要按需下載的話,每個檔案影射到那個下載分組,就是這個實體來記錄的
Dictionary<String, String> DllMapping = new Dictionary<String, String>();
public Form1()
{
InitializeComponent();
DllMapping["OnDemandAssembly"] = "DemandAssembly01";
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
Assembly newAssembly = null;
// 是 ClickOnce 部署方式
if (ApplicationDeployment.IsNetworkDeployed)
{
ApplicationDeployment deploy = ApplicationDeployment.CurrentDeployment;
// Get the DLL name from the Name argument.
string[] nameParts = args.Name.Split(',');
string dllName = nameParts[0];
string downloadGroupName = DllMapping[dllName];
// 下載所需要的檔案
try
{
deploy.DownloadFileGroup(downloadGroupName);
}
catch (DeploymentException de)
{
MessageBox.Show("Downloading file group failed. Group name: " + downloadGroupName + "; DLL name: " + args.Name);
throw (de);
}
// 載入組件到應用程式集
// Load the assembly.
// Assembly.Load() doesn't work here, as the previous failure to load the assembly
// is cached by the CLR. LoadFrom() is not recommended. Use LoadFile() instead.
try
{
newAssembly = Assembly.LoadFile(Application.StartupPath + @"/" + dllName + ".dll");
}
catch (Exception e)
{
throw (e);
}
}
else
{
//Major error - not running under ClickOnce, but missing assembly. Don't know how to recover.
throw (new Exception("Cannot load assemblies dynamically - application is not deployed using ClickOnce."));
}
return (newAssembly);
}
一些問題說明:
Q:上述代碼中,如果我們這個組件OnDemandAssembly.dll 有最新版本了,並且發布了,但是用戶端還是一個老的版本的話,這個邏輯我們沒有處理呀?
A:問題這個問題,就是代表你對 ClickOnce 的原理還是不懂,OnDemandAssembly.dll 有最新版本,那你就必須再重新發布一個版本的ClickOnce代碼,這時候發布的版本號碼就不一樣了。用戶端下不同版本號碼的ClickOnce 程式是存在不同目錄下的(當然伺服器也是一樣)。
系統檢查到你的新版本程式發布後,會為新的版本號碼建立目錄,這個目錄下如果你沒有用過OnDemandAssembly.dll 的功能,是不會有OnDemandAssembly.dll 組件的,繼而上述問題是可以不用考慮的。
參考資料:
MSDN:Downloading Assemblies On Demand with the ClickOnce Deployment API