程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 基於C#地接口基本的教學文章之六

基於C#地接口基本的教學文章之六

編輯:C#入門知識
  第六節、接口轉換

  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#通過覆蓋虛接口的技術深化了抽象類的應用。欲了解這方面的知識,請看下一節-覆蓋虛接口

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