C# 互通性入門系列(四):在C# 中調用COM組件

來源:互聯網
上載者:User

C#互操作系列文章:

  1. C#互通性入門系列(一):C#中互通性介紹
  2. C#互通性入門系列(二):使用平台叫用調用Win32 函數
  3. C# 互通性入門系列(三):平台叫用中的資料封送處理
  4. C#互通性入門系列(四):在C# 中調用COM組件

 

本專題概要:

  • 引言
  • 如何在C#中調用COM組件——訪問Office 互操作對象
  • 在C# 中調用COM組件的實現原理剖析
  • 錯誤處理
  • 小結

 

一、引言

  COM(Component Object Modele,元件物件模型)是微軟以前推崇的一個開發技術,所以現在微軟的很多產品都用到了COM組件,如Office,IE 等。然而如果.NET 平台下的程式想訪問COM組件的方式來實現某個功能怎麼辦呢? 正是由於開發人員有這個需求,所以微軟在.NET FrameWork中為COM和Managed 程式碼之間進行互操作提供了支援,這種互通性的技術就是COM Interop。 但是COM Interop(COm互操作)這項技術,不僅支援在Managed 程式碼中使用COM對象,並且也支援在COM中使用託管對象,本專題只針對在.NET中調用COM對象來介紹,由於COM技術現在用的不多,所以如何在COM中使用託管對象將不會在本系列中做出介紹,如果有需要的朋友可以參看MSDN的相關連結: http://msdn.microsoft.com/zh-cn/library/3y76b69k(v=vs.100).aspx。

下面就從一個具體的執行個體來看看在.NET 中是如何調用COM組件的。

二、如何在C#中調用COM組件——訪問Office 互操作對象

  因為Office產品中使用了很多COM組件,下面就示範通過調用Office中的COM對象來建立Word文檔並儲存建立的文檔到檔案目錄下的例子(在建立的控制台程式裡添加”Microsoft.Office.Interop.Word 14.0.0.0 “ 這個引用,14.0.0.0版本是對應於Office 2010的一個Interop 組件, 12.0.0.0版本則是對應於Office 2007的Interop 組件,如果你電腦中只安裝了Office 2007的話,就只能找到 12.0.0.0的版本的,如果安裝了Office 2010的話,就可以同時找到這兩個版本。)。具體代碼如下:

using System;// 添加額外的命名空間using Microsoft.Office.Interop.Word;namespace COM互通性{    class Program    {        static void Main(string[] args)        {            // 調用COM對象來建立Word文檔            CreateWordDocument();        }        private static void CreateWordDocument()        {            // 啟動Word並使Word可見            Application wordApp = new Application() { Visible = true };            // 建立Word文檔            wordApp.Documents.Add();            Document wordDoc = wordApp.ActiveDocument;            Paragraph para = wordDoc.Paragraphs.Add();            para.Range.Text = "歡迎你,進入Learning Hard部落格";            // 儲存文檔            object filename = @"D:\learninghard.doc";            wordDoc.SaveAs2(filename);            // 關閉Word            wordDoc.Close();            wordApp.Application.Quit();        }    }}

運行結果為:

此時在所指定的檔案目錄中就可以看到你剛才建立的Word文檔了。通過COM互操作的技術我們可以Office的自動化操作。

三、在C# 中調用COM組件的實現原理剖析

  通過上面的例子,大家可以看出在.NET 中調用COM組件是非常方便和簡單的,以至於我們根本不能明白它背後的原理的,下面就介紹在Managed 程式碼中調用COM組件的實現原理和需要的步驟。

要運行上面的程式必須添加一個Interop 組件——“Microsoft.Office.Interop.Word 14.0.0.0”,大家可以通過下面的步驟來添加這個程式集:

  •  請在“方案總管”中,右擊“引用”檔案夾,然後單擊“添加引用”。 
  • “.NET”選項卡上,選中最新版本的 Microsoft.Office.Interop.Word。  例如,“Microsoft.Office.Interop.Excel 14.0.0.0”。  單擊“確定”。

  通過上面添加引用的步驟可以看出,Microsoft.Office.Interop.Word.dll 是一個.NET程式集,而不是COM組件,這時候朋友們肯定有這樣的疑問——不是調用COM組件的嗎?怎麼在Managed 程式碼中調用.NET 程式集的?這樣怎麼能算是在.NET下調用COM組件的示範了? 然而事實是—— Microsoft.Office.Interop.Word.dll 確實是一個.NET程式集,並且它也叫做COM組件的Interop 組件,這個程式集中包含了COM組件中定義的類型的中繼資料, Managed 程式碼通過調用Interop 組件中公開的介面或對象來間接地調用COM對象和介面的。由於Managed 程式碼中不能直接使用COM對象和介面,所以Managed 程式碼對COM對象的調用時是通過CLR的 COM Interop 層作為代理完成的,這個代理就是RCW(即Runtime Callable Wrapper,運行時可調用封裝),所以對COM對象的調用,都是通過RCW來完成的,RCW做的工作主要有啟用COM對象和在Managed 程式碼和Unmanaged 程式碼之間進行資料封送處理(從這裡可以看出,RCW就是 .NET平台和COM組件之間的一個代理,微軟的很多技術都使用了代理的,例如WCF技術——我們在代碼中建立的對象其實只是服務的一個代理,通過代理對象來訪問真真的對象的服務,即方法。講到代理的技術,C#中的委託也是代理的一種實現,此時又想到了23中設計模式中的——代理模式,然而生活中也不乏代理的例子,租房中介,Proxy 伺服器等)。下面通過一個圖來示範下 在.NET中調用COM組件的原理:

  關於通過Tlblmp.exe工具來產生Interop 組件步驟,這裡我就不多詳細訴說了,大家可以參考MSDN中這個工具詳細使用說明 :http://msdn.microsoft.com/zh-cn/library/tt0cf3sx(v=VS.80).aspx 。

  然而我們也可以使用Visual Studio中內建的支援來完成為COM類型庫建立Interop 組件的工作,我們只需要在VS中為.NET 項目添加對應的COM組件的引用,此時VS就會自動將COM類型庫中的COM類型庫轉化為程式集中的中繼資料,並在項目的Bin目錄下產生對於的Interop 組件,所以在VS中添加COM引用,其實最後程式中引用的是Interop 組件,然後通過RCW來對COM組件進行調用。 然而對於Office中的Microsoft.Office.Interop.Wordd.dll,這個程式集也是Interop 組件,但是它又是主要 Interop 組件,即PIA(Primary Interop Assemblies)。主要 Interop 組件是一個由供應商提供的唯一的程式集,為了產生主要 Interop 組件,可以在使用TlbImp命令是開啟 /primary 選項。看到這裡,朋友們肯定有這樣的疑問:PIA與普通程式集到底有什麼區別呢?——區別就是PIA除了包含了COM組件定義的資料類型外,還包含了一些特殊的資訊,如公開金鑰,COM類型庫的提供者等資訊。然而 為什麼需要主要 Interop 組件的呢 ? 對於這個問題的答案就是——主要 Interop 組件可以協助我們解決部署程式時,引用Interop 組件版本不一致的問題。(如果開發人員會為一個COM組件類型庫產生多個Interop 組件,項目中引用的Interop 組件版本與部署時的Interop 組件版本不一致的問題,有了Interop 組件時,我們可以直接引用官方提供主要 Interop 組件。)

四、錯誤處理

 知道了如何調用COM組件之後,大家或許會問:如果調用COM對象的方法失敗時怎麼去擷取失敗的資訊呢?對於這個疑問,錯誤的處理的方法和我們平常Managed 程式碼中的處理方式是一樣的,下面就具體看看是如何擷取錯誤資訊的,下面這段代碼的功能是——開啟一個現有的Word文檔並插入相應的文本,當指定的Word文檔不存在時,此時就會出現調用COM對象的Open方法失敗的情況,具體代碼如下:

using System;using Microsoft.Office.Interop.Word;using System.IO;using System.Runtime.InteropServices;namespace COM互操作中的錯誤處理{    class Program    {        static void Main(string[] args)        {           // 開啟存在的文檔插入文本            string wordPath = @"D:\test.docx";            OpenWordDocument(wordPath);            Console.Read();        }        // 向現有文檔插入文本        private static void OpenWordDocument(string wordPath)        {            // 啟動Word 應用程式            Application wordApp = new Application() { Visible = true };            Document wordDoc=null;            try            {                // 如果文檔不存在時,就會出現調用COM對象失敗的情況                // 開啟Word文檔                wordDoc = wordApp.Documents.Open(wordPath);                // 向Word中插入文本                Range wordRange = wordDoc.Range(0, 0);                wordRange.Text = "這是插入的文本";                // 儲存文檔                wordDoc.Save();            }            catch(Exception ex)            {                          // 獲得異常相對應的HRESULT值                // 因為COM中根據方法返回的HRESULT來判斷調用是否成功的                int HResult = Marshal.GetHRForException(ex);                // 設定控制台的前景色彩,即輸出文本的顏色                Console.ForegroundColor = ConsoleColor.Red;                // 下面把HRESULT值以16進位輸出                Console.WriteLine("調用拋出異常,異常類型為:{0}, HRESULT= 0x{1:x}", ex.GetType().Name, HResult);                Console.WriteLine("異常資訊為:" + ex.Message.Replace('\r', ' '));            }            finally            {                // 關閉文檔並                if (wordDoc != null)                {                    wordDoc.Close();                }                // 退出Word程式                wordApp.Quit();            }        }    }}

如果我們D盤中不存在一個 test.docx文檔時,此時代碼就會進入catch塊,輸出異常資訊,運行結果為:

  從上面的結果我們看到了一個 HRESULT值,這個值真是COM代碼中返回返回的。在COM中,COM方法通過返回 HRESULT 來報告錯誤;.NET 方法則通過引發異常來報告錯誤,為了方便地在Managed 程式碼中獲得COM代碼中出現的錯誤和異常資訊,CLR提供了兩者之間的轉換,每一個代表錯誤發生的HRESULT都會被映射到.NET Framework中的一個異常類,對於具體的映射關係可以參考MSDN中 的文章: http://msdn.microsoft.com/zh-cn/library/9ztbc5s1(VS.80).aspx ,我這裡就不具體用表格列出來的。 如果某個HRESULR不能被映射到等效的.NET Framework 異常類時,那麼就會被映射到COMException異常類, 我們可以通過 Marshal類的GetHRForException方法來獲得異常類對應的HRESULT值(該方法的使用在上面代碼中已經貼出)

五、小結

  關於在.NET中調用COM組件的介紹就到這裡的,即使我們在.NET中調用COM對象的方法是非常的簡單和方便,但是理解CLR 為我們背後完成的工作到底有哪些和理解Managed 程式碼中調用COM組件原理也是相當有必要的。因為理解了調用的原理之後,當我們出現問題的時候就可以很快找到解決方案並解決它,不會覺得無從下手,這樣就可以協助我們提供解決問題的能力。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.