熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

Visual C#接口轉換

2022-06-13   來源: .NET編程 

  C#中不僅支持Net 平台而且支持COM平台為了支持 COM和NetC# 包含一種稱為屬性的獨特語言特性一個屬性實際上就是一個 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組件常包括下面 幾個方面的問題

   

  創建 COM 對象

   

  確定 COM 接口是否由對象實現

   

  調用 COM 接口上的方法

   

  實現可由 COM 客戶端調用的對象和接口

   

  創建 COM 類包裝

   

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

   

  TlbImp 執行下列轉換

   

  COM coclass 轉換為具有無參數構造函數的 C# 類

   

  COM 結構轉換為具有公共字段的 C# 結構

   

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

   

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

   

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

   

  ComImport它將類標記為在外部實現的 COM 類

   

  Guid它用於為類或接口指定通用唯一標識符 (UUID)

   

  InterfaceType它指定接口是從 IUnknown 還是從 IDispatch 派生

   

  PreserveSig它指定是否應將本機返回值從 HRESULT 轉換為 NET 框架異常

   

  聲明 COM coclass

   

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

   

  類不能從任何其他類繼承

   

  類不能實現任何接口

   

  類還必須具有為其設置全局唯一標識符 (GUID) 的 Guid 屬性

   

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

   

  // 聲明一個COM類 FilgraphManager

  [ComImport Guid(EEBBFCEFAFBA)]

  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 的參數從方法的參數列表中剝離

   

  任何非成功返回值都將導致引發 SystemCOMException 異常

   

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

   

  下面是接口轉換的C#程序

   

  using SystemRuntimeInteropServices;

  // 聲明一個COM接口 IMediaControl

  [Guid(ABADCEBAAFBA)

  InterfaceType(ComInterfaceTypeInterfaceIsDual)]

  interface IMediaControl // 這裡不能列出任何基接口

  {

  void Run();

  void Pause();

  void Stop();

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

  void RenderFile(

  [In MarshalAs(UnmanagedTypeBStr)] string strFilename);

  void AddSourceFilter(

  [In MarshalAs(UnmanagedTypeBStr)] string strFilename

  [Out MarshalAs(UnmanagedTypeInterface)] out object ppUnk);

  [return : MarshalAs(UnmanagedTypeInterface)]

  object FilterCollection();

  [return : MarshalAs(UnmanagedTypeInterface)]

  object RegFilterCollection();

  void StopWhenReady();

  }

   

  若要防止 HRESULT 翻譯為 COMException請在 C# 聲明中將 PreserveSig(true) 屬性附加到方法

   

  下面是一個使用C# 映射媒體播放機COM 對象的程序

   

  程序清單 DemonCOMcs

   

  using System;

  using SystemRuntimeInteropServices;

  namespace QuartzTypeLib

  {

  //聲明一個COM接口 IMediaControl此接口來源於媒體播放機COM類

  [Guid(ABADCEBAAFBA)

  InterfaceType(ComInterfaceTypeInterfaceIsDual)]

  interface IMediaControl

  { //列出接口成員

  void Run();

  void Pause();

  void Stop();

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

  void RenderFile(

  [In MarshalAs(UnmanagedTypeBStr)] string strFilename);

  void AddSourceFilter(

  [In MarshalAs(UnmanagedTypeBStr)] string strFilename

  [Out MarshalAs(UnmanagedTypeInterface)]

  out object ppUnk);

  [return: MarshalAs(UnmanagedTypeInterface)]

  object FilterCollection();

  [return: MarshalAs(UnmanagedTypeInterface)]

  object RegFilterCollection();

  void StopWhenReady();

  }

  //聲明一個COM類:

  [ComImport Guid(EEBBFCEFAFBA)]

  class FilgraphManager //此類不能再繼承其它基類或接口

  {

  //這裡不能有任何代碼 系統自動增加一個缺省的構造函數

  }

  }

  class MainClass

  {

  public static void Main(string[] args)

  {

  //命令行參數:

  if (argsLength != )

  {

  DisplayUsage();

  return;

  }

  String filename = args[];

  if (filenameEquals(/?))

  {

  DisplayUsage();

  return;

  }

  // 聲明FilgraphManager的實類對象

  QuartzTypeLibFilgraphManager graphManager =new QuartzTypeLibFilgraphManager();

  //聲明IMediaControl的實類對象:

  QuartzTypeLibIMediaControl mc =(QuartzTypeLibIMediaControl)graphManager;

  // 調用COM的方法

  mcRenderFile(filename);

  //運行文件

  mcRun();

  //暫借停

  ConsoleWriteLine(Press Enter to continue);

  ConsoleReadLine();

  }

  private static void DisplayUsage()

  { // 顯示

  ConsoleWriteLine(媒體播放機: 播放 AVI 文件);

  ConsoleWriteLine(使用方法: VIDEOPLAYEREXE 文件名);

  }

  }

   

  運行示例

   

  若要顯示影片示例 Clockavi請使用以下命令

   

  interop %windir%\clockavi

   

  這將在屏幕上顯示影片直到按 ENTER 鍵停止

   

  在 NET 框架程序中通過DllImport使用 Win API

   

  NET 框架程序可以通過靜態 DLL 入口點的方式來訪問本機代碼庫DllImport 屬性用於指定包含外部方法的實現的dll 位置

   

  DllImport 屬性定義如下

   

  namespace SystemRuntimeInteropServices

  {

  [AttributeUsage(AttributeTargetsMethod)]

  public class DllImportAttribute: SystemAttribute

  {

  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 {} }

  }

  }

   

  說明

   

  DllImport只能放置在方法聲明上

   

  DllImport具有單個定位參數指定包含被導入方法的 dll 名稱的 dllName 參數

   

  DllImport具有五個命名參數

   

  aCallingConvention 參數指示入口點的調用約定如果未指定 CallingConvention則使用默認值 CallingConventionWinapi

   

  bCharSet 參數指示用在入口點中的字符集如果未指定 CharSet則使用默認值 CharSetAuto

   

  cEntryPoint 參數給出 dll 中入口點的名稱如果未指定 EntryPoint則使用方法本身的名稱

   

  dExactSpelling 參數指示 EntryPoint 是否必須與指示的入口點的拼寫完全匹配如果未指定 ExactSpelling則使用默認值 false

   

  ePreserveSig 參數指示方法的簽名應當被保留還是被轉換當簽名被轉換時它被轉換為一個具有 HRESULT 返回值和該返回值的一個名為 retval 的附加輸出參數的簽名如果未指定 PreserveSig則使用默認值 true

   

  fSetLastError 參數指示方法是否保留 Win上一錯誤如果未指定 SetLastError則使用默認值 false

   

  它是一次性屬性類

   

  此外用 DllImport 屬性修飾的方法必須具有 extern 修飾符

   

  下面是 C# 調用 Win MessageBox 函數的示例

   

  using System;

  using SystemRuntimeInteropServices;

  class MainApp

  { //通過DllImport引用userdll類MessageBox來自於userdll類

  [DllImport(userdll EntryPoint=MessageBox)]

  public static extern int MessageBox(int hWnd String strMessage String strCaption uint uiType);

  public static void Main()

  {

  MessageBox( 您好這是 PInvoke! NET );

  }

  }

   

  面向對象的編程語言幾乎都用到了抽象類這一概念抽象類為實現抽象事物提供了更大的靈活性C#也不例外 C#通過覆蓋虛接口的技術深化了抽象類的應用欲了解這方面的知識請看下一節覆蓋虛接口


From:http://tw.wingwit.com/Article/program/net/201311/12116.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.