■■■■前言web
目前的基於.NET平臺的軟件研發中仍然存在大量的對COM及ActiveX控件的調用。使用C#調用ActiveX控件時通常是使用vs.net工具自動生成的互操做性程序集。這種方法操做簡單,能保證必定的性能。但會產生額外的程序文件,不利於應用軟件的簡潔部署,還容易產生和ActiveX控件版本相關的錯誤。本文就提出使用反射技術動態調用ActiveX控件的方式來解決這些問題。ide
■■■■問題描述:函數
目前的基於.NET平臺的軟件研發中仍然存在大量的對COM及ActiveX控件的調用。使用C#調用ActiveX控件時通常是使用vs.net工具的添加COM引用時自動生成的互操做性程序集。這種方法操做簡單,能保證必定的性能。但會產生額外的程序文件,不利於應用軟件的簡潔部署。並且當開發環境和運行環境使用的ActiveX控件的版本不一致[袁永福原創]時還容易出錯。工具
筆者長期從事基於.NET平臺的通用產品類軟件研發。[袁永福原創]產品類軟件要求部署簡潔,爲此筆者都會將多個工程編譯生成的多個.NET程序集文件合併成一個.NET程序集文件來作到簡潔部署。性能
實踐中發現自動生成的互操做性程序集沒法合併。另外產品類軟件應該能適應各類複雜的開發和生產環境,甚至ActiveX控件的CLSID 都有可能變化。ui
例如,對於COM類庫「HebcaFormSealLib」,VS.NET會自動生成程序集文件「AxInterop.HebcaFormSealLib.dll」、「Interop.HebcaFormSealLib.dll」。這些程序集文件沒法進行程序集合並,並且對於32位或64位的工程項目類型敏感,容易致使錯誤。this
■■■■技術改進:spa
所以筆者不採用這種自動生成的互操做性程序集。轉而採用自定義的反射來調用ActiveX控件。.net
後期綁定ActiveX 控件主要知識點爲System.Windows.Forms.AxHost類型和Type.InvokeMember方法。orm
AxHost類型是從System.Windows.Forms.Control類型[袁永福原創]派生出來的,專門用於承載ActiveX控件。它是一個抽象類,有一個受保護的構造函數,函數參數是一個guid格式的ActiveX控件的CLSID字符串。還有一個GetOcx內部方法用於建立ActiveX控件的對象實例,它是一個COM對象引用。
建立了這個COM對象引用後就能夠調用Type.InvokerMember方法來動態的調用指定名稱的方法和屬性。
■■■■範例:
筆者最近在使用某電子簽名的ActiveX控件來實現文檔簽名的功能。筆者寫出如下接口代碼
[System.Runtime.InteropServices.ComVisible(false)]
public class DCHebeiCAControl : System.Windows.Forms.AxHost
{
public DCHebeiCAControl()
: base("{e4ee564c-0845-4404-91ee-0c206113333f}")
{ }
public object _ocx = null;
protected override void AttachInterfaces()
{
this._ocx = base.GetOcx();
}
private void CheckOCX()
{
if (this._ocx == null)
{
throw new System.NullReferenceException("_ocx");
}
}
public VersionType GetBaseVersionType()
{
this.CheckOCX();
VersionType result = (VersionType)this._ocx.GetType().InvokeMember(
"GetBaseVersionType",
BindingFlags.InvokeMethod,
null,
this._ocx,
new object[] { });
return result;
}
public string GetCert(string sealSN)
{
this.CheckOCX();
string result = (string)this._ocx.GetType().InvokeMember(
"GetCert",
BindingFlags.InvokeMethod,
null,
this._ocx,
new object[] { sealSN });
return result;
}
public string GetClientDetailVersionInfo()
{
this.CheckOCX();
string result = (string)this._ocx.GetType().InvokeMember(
"GetClientDetailVersionInfo",
BindingFlags.InvokeMethod,
null,
this._ocx,
new object[] { });
return result;
}
public int GetClientVersion()
{
this.CheckOCX();
int result = (int)this._ocx.GetType().InvokeMember(
"GetClientVersion",
BindingFlags.InvokeMethod,
null,
this._ocx,
new object[] { });
return result;
}
public string GetClientVersionInfo()
{
this.CheckOCX();
string result = (string)this._ocx.GetType().InvokeMember(
"GetClientVersionInfo",
BindingFlags.InvokeMethod,
null,
this._ocx,
new object[] { });
return result;
}
public object GetConfig(string argName)
{
this.CheckOCX();
object result = (object)this._ocx.GetType().InvokeMember(
"GetConfig",
BindingFlags.InvokeMethod,
null,
this._ocx,
new object[] { argName });
return result;
}
// ----------- 封裝其他接口 -----------------------------
}//classDCHebeiCAControl
上述代碼中各個功能函數內部代碼結構簡單[袁永福原創],之間有很大的類似性,所以徹底能夠編寫一個代碼生成器來自動生成上述代碼。
完成自定義的控件後,筆者再建立一個WinForm 窗體,在其Load事件中建立控件並添加到窗體上,其代碼以下
這樣無需使用自動生成的COM接口程序集便可調用ActiveX控件,大幅提升程序
private DCHebeiCAControl _Control = null;
private void frmTest_Load(object sender, EventArgs e)
{
this._Control = new DCHebeiCAControl();
this._Control.Size = new Size(200, 200);
this._Control.Location = new Point(0, 0);
this.Controls.Add(this._Control);
}
的通用性,並且對於32位和64位的項目類型不敏感。方便部署和更新。
不過這樣因爲採用後期綁定而帶來必定的性能[袁永福原創]問題,所以對於性能敏感而又頻繁調用ActiveX控件的場景下須要謹慎採用這種模式。
■■■■小結:
在.net開發中調用舊的ActiveX控件是不少開發場景中不得不作的事情。在本文中,筆者介紹了在C#中調用ActiveX控件的標準模式,並提出了一種[袁永福原創]改良模式來提升程序的通用性。爲操做ActiveX控件的.NET程序開發提供了一種新的技術手段。