程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C# 程序員參考--COM Interop 第一部分:C# 客戶端教學文章

C# 程序員參考--COM Interop 第一部分:C# 客戶端教學文章

編輯:C#入門知識

COM Interop 提供對現有 COM 組件的訪問,而不需要修改原始組件。若要將 COM 代碼合並到托管應用程序,請通過使用 COM Interop 實用工具 (TlbImp.exe) 導入相關的 COM 類型。一經導入,COM 類型就可以使用。

此外,COM Interop 還使 COM 開發人員能夠像訪問其他 COM 對象一樣輕松訪問托管對象。同樣,COM Interop 提供了一個專用實用工具 (RegAsm.exe),此工具將托管類型導出到類型庫中,並將托管組件注冊為傳統 COM 組件。

在運行時,公共語言運行庫根據需要在 COM 對象和托管對象之間封送數據。

該教程顯示如何使用 C# 與 COM 對象交互操作。

COM Interop 第二部分:C# 服務器教程講述如何將 C# 服務器與 C++ COM 客戶端一起使用。有關兩個教程的概述,請參見 COM Interop 教程。

教程

C# 使用 .NET Framework 功能執行 COM Interop。C# 支持:

  • 創建 COM 對象。
  • 確定 COM 接口是否由對象實現。
  • 調用 COM 接口上的方法。
  • 實現可由 COM 客戶端調用的對象和接口。

.NET Framework 使用 COM Interop 處理引用計數問題,因此不必調用或實現 AddRefRelease

本教程闡述以下主題:

  • 創建 COM 類包裝
  • 聲明 COM coclass
  • 創建 COM 對象
  • 聲明 COM 接口
  • 使用轉換而不是 QueryInterface
  • 綜述

創建 COM 類包裝

要使 C# 代碼引用 COM 對象和接口,需要在 C# 內部版本中包含 COM 接口的 .NET Framework 定義。完成此操作的最簡單方法是使用 TlbImp.exe(類型庫導入程序),它是一個包括在 .NET Framework SDK 中的命令行工具。TlbImp 將 COM 類型庫轉換為 .NET Framework 元數據,從而有效地創建一個可以從任何托管語言調用的托管包裝。用 TlbImp 創建的 .NET Framework 元數據可以通過 /R 編譯器選項包括在 C# 內部版本中。如果使用 Visual Studio 開發環境,則只需添加對 COM 類型庫的引用,將為您自動完成此轉換。

TlbImp 執行下列轉換:

  • COM coclass 轉換為具有無參數構造函數的 C# 類。
  • COM 結構轉換為具有公共字段的 C# 結構。

檢查 TlbImp 輸出的一種很好的方法是運行 .NET Framework SDK 命令行工具 Ildasm.exe(Microsoft 中間語言反匯編程序)來查看轉換結果。

雖然 TlbImp 是將 COM 定義轉換為 C# 的首選方法,但也不是任何時候都可以使用它(例如,在沒有 COM 定義的類型庫時或者 TlbImp 無法處理類型庫中的定義時,就不能使用該方法)。在這些情況下,另一種方法是使用 C# 屬性在 C# 源代碼中手動定義 COM 定義。創建 C# 源映射後,只需編譯 C# 源代碼就可產生托管包裝。

執行 COM 映射需要理解的主要屬性包括:

  • ComImport,它將類標記為在外部實現的 COM 類。
  • Guid,它用於為類或接口指定通用唯一標識符 (UUID)。
  • InterfaceType,它指定接口是從 IUnknown 還是從 IDispatch 派生。
  • PreserveSig,它指定是否應將本機返回值從 HRESULT 轉換為 .NET Framework 異常。

這些屬性中的每一個都在本教程的某個實際示例的上下文中進行了顯示。

聲明 COM coclass

COM coclass 在 C# 中表示為類。這些類必須具有與其關聯的 ComImport 屬性。下列限制適用於這些類:

  • 類不能從任何其他類繼承。
  • 類不能實現任何接口。
  • 類還必須具有為其設置全局唯一標識符 (GUID) 的 Guid 屬性。

以下示例在 C# 中聲明一個 coclass:

// 



// declare FilgraphManager as a COM coclass 



// 



[ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")] 



class FilgraphManager



{ 



}

C# 編譯器將添加一個無參數構造函數,可以調用此構造函數來創建 COM coclass 的實例。

創建 COM 對象

COM coclass 在 C# 中表示為具有無參數構造函數的類。使用 new 運算符創建該類的實例等效於在 C# 中調用 CoCreateInstance。使用以上定義的類,就可以很容易地實例化此類:

class MainClass 



{



    public static void Main() 



    {



        // 



        // 

[1] [2] [3] [4] [5] 下一頁  

Create an instance of a COM coclass - calls // // CoCreateInstance(E436EBB3-524F-11CE-9F53-0020AF0BA770, // NULL, CLSCTX_ALL, // IID_IUnknown, &f) // // returns null on failure. // FilgraphManager f = new FilgraphManager(); } }

聲明 COM 接口

COM 接口在 C# 中表示為具有 ComImportGuid 屬性的接口。它不能在其基接口列表中包含任何接口,而且必須按照方法在 COM 接口中出現的順序聲明接口成員函數。

在 C# 中聲明的 COM 接口必須包含其基接口的所有成員的聲明,IUnknownIDispatch 的成員除外(.NET Framework 將自動添加這些成員)。從 IDispatch 派生的 COM 接口必須用 InterfaceType 屬性予以標記。

從 C# 代碼調用 COM 接口方法時,公共語言運行庫必須封送與 COM 對象之間傳遞的參數和返回值。對於每個 .NET Framework 類型均有一個默認類型,公共語言運行庫將使用此默認類型在 COM 調用間進行封送處理時封送。例如,C# 字符串值的默認封送處理是封送到本機類型 LPTSTR(指向 TCHAR 字符緩沖區的指針)。可以在 COM 接口的 C# 聲明中使用 MarshalAs 屬性重寫默認封送處理。

在 COM 中,返回成功或失敗的常用方法是返回一個 HRESULT,並在 MIDL 中有一個標記為“retval”、用於方法的實際返回值的 out 參數。在 C#(和 .NET Framework)中,指示已經發生錯誤的標准方法是引發異常。

默認情況下,.NET Framework 為由其調用的 COM 接口方法在兩種異常處理類型之間提供自動映射。

  • 返回值更改為標記為 retval 的參數的簽名(如果方法沒有標記為 retval 的參數,則為 void)。
  • 標記為 retval 的參數從方法的參數列表中剝離。

任何非成功返回值都將導致引發 System.COMException 異常。

此示例顯示用 MIDL 聲明的 COM 接口以及用 C# 聲明的同一接口(注意這些方法使用 COM 錯誤處理方法)。

下面是該接口的原始 MIDL 版本:

[ 



  odl, 



  uuid(56A868B1-0AD4-11CE-B03A-0020AF0BA770), 



  helpstring("IMediaControl interface"), 



  dual, 



  oleautomation 



] 



interface IMediaControl : IDispatch 



{ 



  [id(0x60020000)] 



  HRESULT Run(); 







  [id(0x60020001)] 



  HRESULT Pause(); 







  [id(0x60020002)] 



  HRESULT Stop(); 







  [id(0x60020003)] 



  HRESULT GetState( [in] long msTimeout, [out] long* pfs); 







  [id(0x60020004)] 



  HRESULT RenderFile([in] BSTR strFilename); 







  [id(0x60020005)] 



  HRESULT AddSourceFilter( [in] BSTR strFilename, [out] IDispatch** ppUnk);







  [id(0x60020006), propget] 



  HRESULT FilterCollection([out, retval] IDispatch** ppUnk); 







  [id(0x60020007), propget] 



  HRESULT RegFilterCollection([out, retval] IDispatch** ppUnk); 







  [id(0x60020008)] 



  HRESULT StopWhenReady(); 



};

下面是該接口的 C# 等效版本:

using System.Runtime.InteropServices;







// Declare IMediaControl as a COM interface which 



// derives from the IDispatch interface. 



[Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"),



    InterfaceType(ComInterfaceType.InterfaceIsDual)] 



interface IMediaControl // cannot list any base interfaces here 



{ 



    // Note that the members of IUnknown and Interface are NOT



    // listed here 



    //



    void Run();







    void Pause();



    



    void Stop();







    void GetState( [In] int msTimeout, [Out] out int pfs);







    void RenderFile(



    [In, MarshalAs(UnmanagedType.BStr)] string strFilename);







    void AddSourceFilter(



    [In, MarshalAs(UnmanagedType.BStr)] string strFilename, 



    [Out, MarshalAs(UnmanagedType.Interface)] out object ppUnk);







    [return : MarshalAs(UnmanagedType.Interface)]



    object FilterCollection();







    [return : MarshalAs(UnmanagedType.Interface)]



    object RegFilterCollection();



    



    void StopWhenReady(); 



}

請注意,C# 接口是如何映射錯誤處理情況的。如果 COM 方法返回錯誤,則 C# 一方將引發異常。

若要防止 HRESULT 翻譯為 COMException,請在 C# 聲明中將 PreserveSig(true) 屬性附加到方法。有關詳細信息,請參見 PreserveSigAttribute 類。

上一頁  [1] [2] [3] [4] [5] 下一頁  

使用轉換而不是 QueryInterface

只有在可以訪問 C# coclass 實現的接口時,C# coclass 才會非常有用。在 C++ 中,可以使用 IUnknown 接口上的 QueryInterface 方法定位對象接口。在 C# 中,通過將 COM 對象顯式轉換為所需的 COM 接口,可以做到這一點。如果轉換失敗,則引發無效轉換異常:

// Create an instance of a COM coclass:



FilgraphManager graphManager = new FilgraphManager();







// See if it supports the IMediaControl COM interface. 



// Note that this will throw a System.InvalidCastException if 



// the cast fails. This is equivalent to QueryInterface for 



// COM objects:



IMediaControl mc = (IMediaControl) graphManager;







// Now you call a method on a COM interface: 



mc.Run();

綜述

下面是一個使用 C# 創建 AVI 文件查看器的完整示例。此程序創建 COM coclass 的實例,將其轉換為 COM 接口,然後調用 COM 接口上的方法。

本節中的示例代表兩種方法:

  • 示例 1   使用 TlbImp 創建 .NET Framework 類。
  • 示例 2   編寫手動執行 COM 映射的 C# 代碼。

示例 1:使用 TlbImp

本例顯示如何使用 TlbImp 創建 AVI 查看器。程序從命令行讀取 AVI 文件名,創建 Quartz COM 對象的實例,然後使用 RenderFileRun 方法顯示 AVI 文件。

以下是生成該程序的步驟:

  • 在 TLB 上運行 TlbImp。本示例使用的媒體播放機包含在應位於 Windows 系統目錄中的 Quartz.dll 中。使用以下命令創建 .NET Framework DLL:
    tlbimp c:\winnt\system32\quartz.dll /out:QuartzTypeLib.dll 

    請注意,得到的 DLL 需要命名為 QuartzTypeLib,以便 .NET Framework 可以在運行時正確加載包含類型。

  • 可以使用 Ildasm 工具查看得到的 DLL。例如,若要顯示 QuartzTypeLib.dll 文件的內容,請使用以下命令:
    Ildasm QuartzTypeLib.dll
  • 生成程序時使用 C# 編譯器選項 /R 以包含 QuartzTypeLib.dll 文件。

然後就可以使用此程序顯示影片(用於測試的一個影片示例是駐留在 Windows 目錄中的 Clock.avi)。

// interop1.cs



// compile with: /R:QuartzTypeLib.dll



using System;



class MainClass 



{ 



      /************************************************************ 



      Abstract: This method collects the file name of an AVI to 



      show then creates an instance of the Quartz COM object.



      To show the AVI, the program calls RenderFile and Run on 



      IMediaControl. Quartz uses its own thread and window to 



      display the AVI.The main thread blocks on a ReadLine until 



      the user presses ENTER.



            Input Parameters: the location of the AVI file it is 



            going to display



            Returns: void



      **************************************************************/ 



      public static void Main(string[] args) 



      { 



            // Check to see if the user passed in a filename 



            if (args.Length != 1)



            { 



                  DisplayUsage();



                  return;



            } 



 



            if (args[0] == "/?")



            { 



                  DisplayUsage();



                  return;



            } 



 



            string filename = args[0]; 



 



            // Check to see if the file exists



            if (!System.IO.File.Exists(filename))



            {



                  Console.WriteLine("File " + filename + " not found.");



                  DisplayUsage();



                  return;



            }



    



            // Create instance of Quartz



            // (Calls CoCreateInstance(E436EBB3-524F-11CE-9F53-0020AF0BA770,



            // NULL, CLSCTX_ALL, IID_IUnknown, &graphManager).): 



 



            try



            {



                  QuartzTy

上一頁  [1] [2] [3] [4] [5] 下一頁  

peLib.FilgraphManager graphManager = new QuartzTypeLib.FilgraphManager(); // QueryInterface for the IMediaControl interface: QuartzTypeLib.IMediaControl mc = (QuartzTypeLib.IMediaControl)graphManager; // Call some methods on a COM interface // Pass in file to RenderFile method on COM object. mc.RenderFile(filename); // Show file. mc.Run(); } catch(Exception ex) { Console.WriteLine("Unexpected COM exception: " + ex.Message); } // Wait for completion. Console.WriteLine("Press Enter to continue."); Console.ReadLine(); } private static void DisplayUsage() { // User did not provide enough parameters. // Display usage: Console.WriteLine("VideoPlayer: Plays AVI files."); Console.WriteLine("Usage: VIDEOPLAYER.EXE filename"); Console.WriteLine("where filename is the full path and"); Console.WriteLine("file name of the AVI to display."); } }

運行示例

若要顯示影片示例 Clock.avi,請使用以下命令:

interop1 %windir%\clock.avi

當您按 ENTER 鍵後,屏幕上將顯示影片。

示例 2:C# 代碼方法

本示例使用與示例 1 相同的 Main 方法,但它只是使用 C# 映射媒體播放機 COM 對象,而不是運行 TlbImp。

// interop2.cs



using System;



using System.Runtime.InteropServices;



 



namespace QuartzTypeLib 



{



      // Declare IMediaControl as a COM interface which 



      // derives from IDispatch interface:



      [Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"), 



      InterfaceType(ComInterfaceType.InterfaceIsDual)] 



      interface IMediaControl   // Cannot list any base interfaces here 



      { 



            // Note that IUnknown Interface members are NOT listed here:



 







            void Run(); 



            void Pause();



 



            void Stop();



 



            void GetState( [In] int msTimeout, [Out] out int pfs);



 



            void RenderFile(



                  [In, MarshalAs(UnmanagedType.BStr)] string strFilename);



 



            void AddSourceFilter( 



                  [In, MarshalAs(UnmanagedType.BStr)] string strFilename, 



                  [Out, MarshalAs(UnmanagedType.Interface)]



                  out object ppUnk);



 



            [return: MarshalAs(UnmanagedType.Interface)] 



            object FilterCollection();



 



            [return: MarshalAs(UnmanagedType.Interface)] 



            object RegFilterCollection();



            



            void StopWhenReady(); 



      }



      // Declare FilgraphManager as a COM coclass:



      [ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")] 



      class FilgraphManager   // Cannot have a base class or



            // interface list here.



      { 



            // Cannot have any members here 



            // NOTE that the C# compiler will add a default constructor



            // for you (no parameters).



      }



}



 



class MainClass 



{ 



      /********************************************************** 



      Abstract: This method collects the file name of an AVI to 



      show then creates an instance of the Quartz COM object.



      To show the AVI, the program calls RenderFile and Run on 



      IMediaControl. Quartz uses its own thread and window to 



      display the AVI.The main thread blocks on a ReadLine until 



   

上一頁  [1] [2] [3] [4] [5] 下一頁  

the user presses ENTER. Input Parameters: the location of the AVI file it is going to display Returns: void *************************************************************/ public static void Main(string[] args) { // Check to see if the user passed in a filename: if (args.Length != 1) { DisplayUsage(); return; } if (args[0] == "/?") { DisplayUsage(); return; } String filename = args[0]; // Check to see if the file exists if (!System.IO.File.Exists(filename)) { Console.WriteLine("File " + filename + " not found."); DisplayUsage(); return; } // Create instance of Quartz // (Calls CoCreateInstance(E436EBB3-524F-11CE-9F53-0020AF0BA770, // NULL, CLSCTX_ALL, IID_IUnknown, // &graphManager).): try { QuartzTypeLib.FilgraphManager graphManager = new QuartzTypeLib.FilgraphManager(); // QueryInterface for the IMediaControl interface: QuartzTypeLib.IMediaControl mc = (QuartzTypeLib.IMediaControl)graphManager; // Call some methods on a COM interface. // Pass in file to RenderFile method on COM object. mc.RenderFile(filename); // Show file. mc.Run(); } catch(Exception ex) { Console.WriteLine("Unexpected COM exception: " + ex.Message); } // Wait for completion. Console.WriteLine("Press Enter to continue."); Console.ReadLine(); } private static void DisplayUsage() { // User did not provide enough parameters. // Display usage. Console.WriteLine("VideoPlayer: Plays AVI files."); Console.WriteLine("Usage: VIDEOPLAYER.EXE filename"); Console.WriteLine("where filename is the full path and"); Console.WriteLine("file name of the AVI to display."); } }

運行示例

若要顯示影片示例 Clock.avi,請使用以下命令:

interop2 %windir%\clock.avi

當您按 ENTER 鍵後,屏幕上將顯示影片。

 

上一頁  [1] [2] [3] [4] [5] 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved