2009年8月12日 星期三

在應用程式中動態的載入Assembly完成自動更新佈署1-1

RunPC 128期:利用.NET技術實現零接觸佈署(二)

利用.NET技術實現零接觸佈署的第一篇文章中,我們說明了如何利用.NET的NTD技術和如何設定程式碼存取安全(Code Access Security,CAS)以及它在.NET安全性上扮演的角色,我們也舉了二個例子加以說明,其一經由URL執行.NET應用程式,利用Download Cache機制佈署、更新程式,讓.NET的NTD應用系統俱備了Web (Thin Clinet)與Windows (Rich Client)應用程式的優點;其二建立一個相似於早期ActiveX Document的NTD應用程式內嵌於Internet Explorer中,並使用Client Script與該.NET控制項互動。
接下來我們會為各位介紹.NET的動態載入組件(Assembly)完成自動更新與佈署作法,此種作法與上篇之差異在於,上篇中針對一應用程式來作更新且完全依賴.NET提供的機制來完成,而本篇可細分至針對Assembly來作更新,甚可加入系統權限角色功能等相關判來完成本身自訂的特殊需求;該篇文章依序會為各位介紹:定義在System命名空間(namespace)下的Type類別、定義於System.Reflection命名空間下的反射機制(Reflection)服務、用來作晚期繫結(Late Binding)的Activator類別…等相關技術來完成動態載入組件自動更新的功能。

System.Type類別
Type代表型別的宣告,它提供了許多方法成員可用來找尋項目後的細節,而許多定義在System.Reflection命名空間裡的項目都用到抽象System.Type類別;換句話說Type 是 System.Reflection 功能的根,也是存取中繼資料 (Metadata) 的主要方法,使用 Type 的成員可以很簡易的取得型別宣告的資訊,如(表一)所示,而Reflection和CLR型別系統請參考(圖一)。

(表一)Type類別成員


(圖一)Reflection和CLR型別系統

取得Type物件的方式與Type的使用
Type類別是個抽象類別,是故您不可以利用『New』關鍵字來建立Type的實體物件,以下筆者提供您三種方式取得Type物件。

1、 GetType運算子:傳回指定型別的 Type 物件。
Dim t As Type=GetType(Integer)
‘註:Integer為型別名稱
2、 靜態Type.GetType方法:取得具有指定名稱的Type物件。
Dim t As Type = Type.GetType("System.Int32")
‘注意:此處"System.Int32"字串參數大小有別。
3、 Object.GetType方法:取得目前執行個體的Type物件。
Dim str As New String("NewString")
Dim t As Type = str.GetType

除非必要否則我們並不建議您使用第三種方式來取得Type物件,因為此處您必須先建立一個物件執行個體,方能取得您所要的Type物件;當然在取得一個Type物件的參考後接下來讓我們用個很簡單的對應關係圖來說明執行時期的型別資訊。

(圖二) 執行時期的型別資訊對應關係圖


System.Reflection命名空間的探索
在探索System.Reflection命名空間首要談的就是Assembly類別,我們可以經由使用該型別動態的載入組件、在執行時期叫用(Invoke)類別成員(稱之為『晚期繫結』),和取得組件本身的屬性資訊。
列舉類別的成員
接著讓我們利用隻簡單的範例程式來瀏灠類別定義的資訊。首先,我們建立一個新的[Visual Basic Windows應用程式專案],取名為[DynamicAssembly],再於Form1程式碼視窗中輸入下面這段程式碼,用於灠瀏組件中所有成員,最後於表單載入事件中呼叫WalkAssembly函式,並傳入[DynamicAssembly]組件名稱(註1),執行後即可在輸出視窗中看見如[圖三]的內容列出本身組件的所有成員型別、名稱…等。
註1:您可於方案總管中找到該DynamicAssembly專案,點選右鍵選[屬性],即可叫出該專案的屬性頁,內有一組件名稱項目可正確找出該專案的組件名稱。

Public Sub WalkAssembly(ByVal assemblyName As String)
Dim Asm As [Assembly] = System.Reflection.Assembly.Load(assemblyName)
For Each m As [Module] In Asm.GetModules
For Each t As Type In m.GetTypes
For Each mi As MemberInfo In t.GetMembers
Console.WriteLine("{0}->{1}.{2}",mi.MemberType,t, mi.Name)
Next
Next
Next
End Sub

程式碼說明如下:
 Dim Asm As [Assembly] = System.Reflection.Assembly.Load(assemblyName)
此處我們呼叫Assembly的靜態方法Load,再傳入一組件顯示名稱(註2),則可傳回一Assembly的實體物件,再使用三個For迴圈一一找出相關的成員。
註2:組件的顯示名稱可能會包含易記名外,還可以選擇指定它的地區設定、版本號碼、公鑰值、和強式名稱…等。如:Name (,Loc=CultureInfo)(,Ver=Major.Minor.Build.Revision)(,SN=StrongName)。
 [Asm.GetModules]:透過Asm組件的GetModules方法取出在該組件內的所有模組,此範例只有一個模組名稱為『DynamicAssembly.exe』,您可使用Console.WriteLine(m.Name)來取得。
 [m.GetTypes]:透過m模組物件的GetTypes方法取出該模組內所有型別,此處亦只有一個型別名稱為『Form1』,可使用Console.WriteLine(t.Name)取得;但若您在此專案內加入圖二的Person類別,則此處會多一型別『Person』。
 [t.GetMembers]:透過t型別物件的GetTypes方法取出該型別內的所有成員。
 [mi.MemberType]:找出mi成員型態,如:Method、Property、Constructor、Event…等。
 [t]:相當於t.ToString為型別的名稱,此處為『DynamicAssembly.Form1』。
 [mi.Name]:即為成員的名稱。

(圖三)組件成員輸出列表


列舉方法的參數
在得知如何列舉類別成員後,接下來讓我們取出圖二Person類別內Save方法的參數成員,請參考下面程式碼,而輸出如圖四:

Private Sub btnListMethods_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnListMethods.Click
Dim PType As Type = Asm.GetType("DynamicAssembly.Person")
Dim saveMi As MethodInfo = PType.GetMethod("Save")
Dim savePars() As ParameterInfo = saveMi.GetParameters
Console.WriteLine("{0}方法參數成員有{1}個", saveMi.Name, savePars.Length)
Dim par As ParameterInfo
For Each par In savePars
Console.WriteLine("參數名稱:{0}的型別是{1},位於第{2}個", par.Name, par.ParameterType, par.Position)
Next
End Sub
End Class

程式碼說明如下:
 Dim PType As Type = Asm.GetType("DynamicAssembly.Person")
經由Assembly物件的GetType得到命名空間DynamicAssembly下的Person型別。
 PType.GetMethod("Save"):經由型別的GetMethod方法得到指定的方法成員。
 saveMi.GetParameters:經由MethodInfo的GetParameters方法得到參數集合。

(圖四)

至此我們了解如何使用定義在System.Reflection命名空間的核心項目,且利用它取得執行期間取得相關的資訊,若您曾使用過.NET Framework提供的工具:ILDasm.exe,我們已完成它提供的大部份瀏灠組件的功能,差別只在ILDasm.exe是顯示樹狀結構,而我們是呈現在輸出視窗中。

沒有留言:

加入書籤: MyShare HemiDemi Baidu Google Bookmarks Yahoo! My Web PChome Del.icio.us Digg technorati furludn bookmark 其他更多書籤

BOOKS:New and Upcoming