使用反射動態調用ActiveX控件ide
袁永福 2018-3-2函數
■■■■問題描述:工具
目前的基於.NET平臺的軟件研發中仍然存在大量的對COM及ActiveX控件的調用。使用C#調用ActiveX控件時通常是使用vs.net工具的添加COM引用時自動生成的互操做性程序集。這種方法操做簡單,能保證必定的性能。但會產生額外的程序文件,不利於應用軟件的簡潔部署。並且當開發環境和運行環境使用的ActiveX控件的版本不一致[袁永福原創]時還容易出錯。性能
筆者長期從事基於.NET平臺的通用產品類軟件研發。[袁永福原創]產品類軟件要求部署簡潔,爲此筆者都會將多個工程編譯生成的多個.NET程序集文件合併成一個.NET程序集文件來作到簡潔部署。ui
實踐中發現自動生成的互操做性程序集沒法合併。另外產品類軟件應該能適應各類複雜的開發和生產環境,甚至ActiveX控件的CLSID 都有可能變化。this
例如,對於COM類庫「HebcaFormSealLib」,VS.NET會自動生成程序集文件「AxInterop.HebcaFormSealLib.dll」、「Interop.HebcaFormSealLib.dll」。這些程序集文件沒法進行程序集合並,並且對於32位或64位的工程項目類型敏感,容易致使錯誤。.net
■■■■技術改進:orm
所以筆者不採用這種自動生成的互操做性程序集。轉而採用自定義的反射來調用ActiveX控件。對象
後期綁定ActiveX 控件主要知識點爲System.Windows.Forms.AxHost類型和Type.InvokeMember方法。blog
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事件中建立控件並添加到窗體上,其代碼以下
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); }
這樣無需使用自動生成的COM接口程序集便可調用ActiveX控件,大幅提升程序的通用性,並且對於32位和64位的項目類型不敏感。方便部署和更新。
不過這樣因爲採用後期綁定而帶來必定的性能[袁永福原創]問題,所以對於性能敏感而又頻繁調用ActiveX控件的場景下須要謹慎採用這種模式。
■■■■小結:
在.net開發中調用舊的ActiveX控件是不少開發場景中不得不作的事情。在本文中,筆者介紹了在C#中調用ActiveX控件的標準模式,並提出了一種[袁永福原創]改良模式來提升程序的通用性。爲操做ActiveX控件的.NET程序開發提供了一種新的技術手段。