C#介面轉換

來源:互聯網
上載者:User

標籤:使用   檔案   os   資料   art   io   

C#中不僅支援.Net 平台,而且支援COM平台.為了支援 COM和.Net,C# 包含一種稱為屬性的獨特語言特性.一個屬性實際上就是一個 C# 類,它通過修飾原始碼來提供元資訊.屬性使 C# 能夠支援特定的技術,如 COM 和 .Net,而不會干擾語言規範本身.C# 提供將COM介面轉換為 C#介面的屬性類.另一些屬性類將 COM類轉換為C# 類.執行這些轉換不需要任何 IDL 或類工廠.
    現在部署的任何COM 組件都可以在介面轉換中使用.通常情況下,所需的調整是完全自動進行的.
    特別是,可以使用運行時可調用封裝 ( RCW ) 從 .NET 架構訪問 COM 組件.此封裝將 COM 組件提供的 COM 介面轉換為與 .NET 架構相容的介面.對於 OLE 自動化介面,RCW 可以從類型庫中自動產生;對於非 OLE 自動化介面,開發人員可以編寫自訂 RCW,手動將 COM 介面提供的類型映射為與 .NET 架構相容的類型.
    使用ComImport引用COM組件
COM Interop 提供對現有 COM 組件的訪問,而不需要修改原始組件.使用ComImport引用COM組件常包括下面 幾個方面的問題:
1、建立 COM 物件.
2、確定 COM 介面是否由對象實現.
3、調用 COM 介面上的方法.
4、實現可由 COM 用戶端調用的對象和介面.
    建立 COM 類別封裝
    要使 C# 代碼引用COM 物件和介面,需要在 C# 中包含 COM 介面的定義.完成此操作的最簡單方法是使用 TlbImp.exe(類型庫匯入程式),它是一個包括在 .NET 架構 SDK 中的命令列工具.TlbImp 將 COM 類別型庫轉換為 .NET 架構中繼資料,從而有效地建立一個可以從任何託管語言調用的託管封裝.用 TlbImp 建立的 .NET 架構中繼資料可以通過 /R 編譯器選項包括在 C# 組建中.如果使用 Visual Studio 開發環境,則只需添加對 COM 類別型庫的引用,將為您自動完成此轉換.
TlbImp 執行下列轉換:
1、COM coclass 轉換為具有無參數建構函式的 C# 類.
2、COM 結構轉換為具有公用欄位的 C# 結構.
    檢查 TlbImp 輸出的一種很好的方法是運行 .NET 架構 SDK 命令列工具 Ildasm.exe(Microsoft 中繼語言反組譯工具)來查看轉換結果.
    雖然 TlbImp 是將 COM 定義轉換為 C# 的首選方法,但也不是任何時候都可以使用它(例如,在沒有 COM 定義的類型庫時或者 TlbImp 無法處理類型庫中的定義時,就不能使用該方法).在這些情況下,另一種方法是使用 C# 屬性在 C# 原始碼中手動定義 COM 定義.建立 C# 源映射後,只需編譯 C# 原始碼就可產生託管封裝.
    執行 COM 映射需要理解的主要屬性包括:
1、ComImport:它將類標記為在外部實現的 COM 類別.
2、Guid:它用於為類或介面指定通用唯一識別碼 ( UUID ).
3、InterfaceType,它指定介面是從 IUnknown 還是從 IDispatch 派生.
4、PreserveSig,它指定是否應將本機傳回值從 HRESULT 轉換為 .NET 架構異常.
    聲明 COM coclass
COM coclass 在 C# 中表示為類.這些類必須具有與其關聯的 ComImport 屬性.下列限制適用於這些類:
1、類不能從任何其他類繼承.
2、類不能實現任何介面.
4、類還必須具有為其設定通用唯一識別碼 ( GUID ) 的 Guid 屬性.
    以下樣本在 C# 中聲明一個 coclass:
// 聲明一個COM類 FilgraphManager
[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( )
    {
        FilgraphManager filg = new FilgraphManager( );
    }
}
    聲明 COM 介面
COM 介面在 C# 中表示為具有 ComImport 和 Guid 屬性的介面.它不能在其基底介面列表中包含任何介面,而且必須按照方法在 COM 介面中出現的順序聲明介面成員函數.
    在 C# 中聲明的 COM 介面必須包含其基底介面的所有成員的聲明,IUnknown 和 IDispatch 的成員除外(.NET 架構將自動添加這些成員).從 IDispatch 派生的 COM 介面必須用 InterfaceType 屬性予以標記.
從 C# 代碼調用 COM 介面方法時,公用語言運行庫必須封送與 COM 物件之間傳遞的參數和傳回值.對於每個 .NET 架構類型均有一個預設類型,公用語言運行庫將使用此預設類型在 COM 調用間進行封送處理時封送.例如,C# 字串值的預設封送處理是封送到本機類型 LPTSTR(指向 TCHAR 字元緩衝區的指標).可以在 COM 介面的 C# 聲明中使用 MarshalAs 屬性重寫預設封送處理.
    在 COM 中,返回成功或失敗的常用方法是返回一個 HRESULT,並在 MIDL 中有一個標記為"retval"、用於方法的實際傳回值的 out 參數.在 C#(和 .NET 架構)中,指示已經發生錯誤的標準方法是引發異常.
預設情況下,.NET 架構為由其調用的 COM 介面方法在兩種異常處理類型之間提供自動對應.
    傳回值更改為標記為 retval 的參數的簽名(如果方法沒有標記為 retval 的參數,則為 void).
    標記為 retval 的參數從方法的參數列表中剝離.
    任何非成功傳回值都將導致引發 System.COMException 異常.
    此樣本顯示用 MIDL 聲明的 COM 介面以及用 C# 聲明的同一介面(注意這些方法使用 COM 錯誤處理方法).
    下面是介面轉換的C#程式:
using System.Runtime.InteropServices;
// 聲明一個COM介面 IMediaControl
[Guid( "56A868B1-0AD4-11CE-B03A-0020AF0BA770" ),
InterfaceType( ComInterfaceType.InterfaceIsDual )]
interface IMediaControl // 這裡不能列出任何基底介面
{
    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( );
}
    若要防止 HRESULT 翻譯為 COMException,請在 C# 聲明中將 PreserveSig( true ) 屬性附加到方法.
    下面是一個使用C# 映射媒體播放機COM 物件的程式.
    程式清單2 DemonCOM.cs
using System;
using System.Runtime.InteropServices;
namespace QuartzTypeLib
{
//聲明一個COM介面 IMediaControl,此介面來源於媒體播放機COM類
    [Guid( "56A868B1-0AD4-11CE-B03A-0020AF0BA770" ),
    InterfaceType( ComInterfaceType.InterfaceIsDual )]
    interface IMediaControl
    { //列出介面成員
        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( );
    }
//聲明一個COM類:
    [ComImport, Guid( "E436EBB3-524F-11CE-9F53-0020AF0BA770" )]
    class FilgraphManager //此類不能再繼承其它基類或介面
    {
//這裡不能有任何代碼 ,系統自動增加一個預設的建構函式
    }
}
class MainClass
{
    public static void Main( string[] args )
    {
//命令列參數:
        if ( args.Length != 1 )
        {
            DisplayUsage( );
            return;
        }
        String filename = args[0];
        if ( filename.Equals( "/?" ) )
        {
            DisplayUsage( );
            return;
        }
// 聲明FilgraphManager的實類對象:
        QuartzTypeLib.FilgraphManager graphManager =new QuartzTypeLib.FilgraphManager( );
//聲明IMediaControl的實類對象::
        QuartzTypeLib.IMediaControl mc =( QuartzTypeLib.IMediaControl )graphManager;
// 調用COM的方法:
        mc.RenderFile( filename );
//運行檔案.
        mc.Run( );
//暫借停.
        Console.WriteLine( "Press Enter to continue." );
        Console.ReadLine( );
    }
    private static void DisplayUsage( )
    { // 顯示
        Console.WriteLine( "媒體播放機: 播放 AVI 檔案." );
        Console.WriteLine( "使用方法: VIDEOPLAYER.EXE 檔案名稱" );
    }
}
    運行樣本:
    若要顯示影片樣本 Clock.avi,請使用以下命令:
interop2 %windir%\clock.avi
    這將在螢幕上顯示影片,直到按 ENTER 鍵停止.
    在 .NET 架構程式中通過DllImport使用 Win32 API
.NET 架構程式可以通過靜態 DLL 進入點的方式來訪問機器碼庫.DllImport 屬性用於指定包含外部方法的實現的dll 位置.
DllImport 屬性定義如下:
namespace System.Runtime.InteropServices
{
    [AttributeUsage( AttributeTargets.Method )]
    public class DllImportAttribute: System.Attribute
    {
        public DllImportAttribute( string dllName ) {...}
        public CallingConvention CallingConvention;
        public CharSet CharSet;
        public string EntryPoint;
        public bool ExactSpelling;
        public bool PreserveSig;
        public bool SetLastError;
        public string Value { get {...} }
    }
}
    說明:
1、DllImport只能放置在方法聲明上.
2、DllImport具有單個定位參數:指定包含被匯入方法的 dll 名稱的 dllName 參數.
3、DllImport具有五個具名引數:
a、CallingConvention 參數指示進入點的呼叫慣例.如果未指定 CallingConvention,則使用預設值 CallingConvention.Winapi.
b、CharSet 參數指示用在進入點中的字元集.如果未指定 CharSet,則使用預設值 CharSet.Auto.
c、EntryPoint 參數給出 dll 中進入點的名稱.如果未指定 EntryPoint,則使用方法本身的名稱.
d、ExactSpelling 參數指示 EntryPoint 是否必須與指示的進入點的拼字完全符合.如果未指定 ExactSpelling,則使用預設值 false.
e、PreserveSig 參數指示方法的簽名應當被保留還是被轉換.當簽名被轉換時,它被轉換為一個具有 HRESULT 傳回值和該傳回值的一個名為 retval 的附加輸出參數的簽名.如果未指定 PreserveSig,則使用預設值 true.
f、SetLastError 參數指示方法是否保留 Win32"上一錯誤".如果未指定 SetLastError,則使用預設值 false.
4、它是一次性屬性類.
5、此外,用 DllImport 屬性修飾的方法必須具有 extern 修飾符.
    下面是 C# 調用 Win32 MessageBox 函數的樣本:
using System;
using System.Runtime.InteropServices;
class MainApp
{ //通過DllImport引用user32.dll類.MessageBox來自於user32.dll類
    [DllImport( "user32.dll", EntryPoint="MessageBox" )]
    public static extern int MessageBox( int hWnd, String strMessage, String strCaption, uint uiType );
    public static void Main( )
    {
        MessageBox(0, "您好,這是 PInvoke!", ".NET", 0);
    }
}
    物件導向的程式設計語言幾乎都用到了抽象類別這一概念,抽象類別為實現抽象事物提供了更大的靈活性.C#也不例外, C#通過覆蓋虛介面的技術深化了抽象類別的應用.欲瞭解這方面的知識,請看下一節-覆蓋虛介面

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.