基于C#的接口基礎圖文說明教程之6
發(fā)表時間:2024-02-15 來源:明輝站整理相關軟件相關文章人氣:
[摘要]第六節(jié)、接口轉(zhuǎn)換 C#中不僅支持.Net 平臺,而且支持COM平臺。為了支持 COM和.Net,C# 包含一種稱為屬性的獨特語言特性。一個屬性實際上就是一個 C# 類,它通過修飾源代碼來提供元信息。屬性使 C# 能夠支持特定的技術(shù),如 COM 和 .Net,而不會干擾語言規(guī)范本身。C# 提供將C...
第六節(jié)、接口轉(zhuǎn)換
C#中不僅支持.Net 平臺,而且支持COM平臺。為了支持 COM和.Net,C# 包含一種稱為屬性的獨特語言特性。一個屬性實際上就是一個 C# 類,它通過修飾源代碼來提供元信息。屬性使 C# 能夠支持特定的技術(shù),如 COM 和 .Net,而不會干擾語言規(guī)范本身。C# 提供將COM接口轉(zhuǎn)換為 C#接口的屬性類。另一些屬性類將 COM類轉(zhuǎn)換為C# 類。執(zhí)行這些轉(zhuǎn)換不需要任何 IDL 或類工廠。
現(xiàn)在部署的任何COM 組件都可以在接口轉(zhuǎn)換中使用。通常情況下,所需的調(diào)整是完全自動進行的。
特別是,可以使用運行時可調(diào)用包裝 (RCW) 從 .NET 框架訪問 COM 組件。此包裝將 COM 組件提供的 COM 接口轉(zhuǎn)換為與 .NET 框架兼容的接口。對于 OLE 自動化接口,RCW 可以從類型庫中自動生成;對于非 OLE 自動化接口,開發(fā)人員可以編寫自定義 RCW,手動將 COM 接口提供的類型映射為與 .NET 框架兼容的類型。
使用ComImport引用COM組件
COM Interop 提供對現(xiàn)有 COM 組件的訪問,而不需要修改原始組件。使用ComImport引用COM組件常包括下面 幾個方面的問題:
1、創(chuàng)建 COM 對象。
2、確定 COM 接口是否由對象實現(xiàn)。
3、調(diào)用 COM 接口上的方法。
4、實現(xiàn)可由 COM 客戶端調(diào)用的對象和接口。
創(chuàng)建 COM 類包裝
要使 C# 代碼引用COM 對象和接口,需要在 C# 中包含 COM 接口的定義。完成此操作的最簡單方法是使用 TlbImp.exe(類型庫導入程序),它是一個包括在 .NET 框架 SDK 中的命令行工具。TlbImp 將 COM 類型庫轉(zhuǎn)換為 .NET 框架元數(shù)據(jù),從而有效地創(chuàng)建一個可以從任何托管語言調(diào)用的托管包裝。用 TlbImp 創(chuàng)建的 .NET 框架元數(shù)據(jù)可以通過 /R 編譯器選項包括在 C# 內(nèi)部版本中。如果使用 Visual Studio 開發(fā)環(huán)境,則只需添加對 COM 類型庫的引用,將為您自動完成此轉(zhuǎn)換。
TlbImp 執(zhí)行下列轉(zhuǎn)換:
1、COM coclass 轉(zhuǎn)換為具有無參數(shù)構(gòu)造函數(shù)的 C# 類。
2、COM 結(jié)構(gòu)轉(zhuǎn)換為具有公共字段的 C# 結(jié)構(gòu)。
檢查 TlbImp 輸出的一種很好的方法是運行 .NET 框架 SDK 命令行工具 Ildasm.exe(Microsoft 中間語言反匯編程序)來查看轉(zhuǎn)換結(jié)果。
雖然 TlbImp 是將 COM 定義轉(zhuǎn)換為 C# 的首選方法,但也不是任何時候都可以使用它(例如,在沒有 COM 定義的類型庫時或者 TlbImp 無法處理類型庫中的定義時,就不能使用該方法)。在這些情況下,另一種方法是使用 C# 屬性在 C# 源代碼中手動定義 COM 定義。創(chuàng)建 C# 源映射后,只需編譯 C# 源代碼就可產(chǎn)生托管包裝。
執(zhí)行 COM 映射需要理解的主要屬性包括:
1、ComImport:它將類標記為在外部實現(xiàn)的 COM 類。
2、Guid:它用于為類或接口指定通用唯一標識符 (UUID)。
3、InterfaceType,它指定接口是從 IUnknown 還是從 IDispatch 派生。
4、PreserveSig,它指定是否應將本機返回值從 HRESULT 轉(zhuǎn)換為 .NET 框架異常。
聲明 COM coclass
COM coclass 在 C# 中表示為類。這些類必須具有與其關聯(lián)的 ComImport 屬性。下列限制適用于這些類:
1、類不能從任何其他類繼承。
2、類不能實現(xiàn)任何接口。
4、類還必須具有為其設置全局唯一標識符 (GUID) 的 Guid 屬性。
以下示例在 C# 中聲明一個 coclass:
// 聲明一個COM類 FilgraphManager
[ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")]
class FilgraphManager
{ }
C# 編譯器將添加一個無參數(shù)構(gòu)造函數(shù),可以調(diào)用此構(gòu)造函數(shù)來創(chuàng)建 COM coclass 的實例。
創(chuàng)建 COM 對象
COM coclass 在 C# 中表示為具有無參數(shù)構(gòu)造函數(shù)的類。使用 new 運算符創(chuàng)建該類的實例等效于在 C# 中調(diào)用 CoCreateInstance。使用以上定義的類,就可以很容易地實例化此類:
class MainClass
{
public static void Main()
{
FilgraphManager filg = new FilgraphManager();
}
}
聲明 COM 接口
COM 接口在 C# 中表示為具有 ComImport 和 Guid 屬性的接口。它不能在其基接口列表中包含任何接口,而且必須按照方法在 COM 接口中出現(xiàn)的順序聲明接口成員函數(shù)。
在 C# 中聲明的 COM 接口必須包含其基接口的所有成員的聲明,IUnknown 和 IDispatch 的成員除外(.NET 框架將自動添加這些成員)。從 IDispatch 派生的 COM 接口必須用 InterfaceType 屬性予以標記。
從 C# 代碼調(diào)用 COM 接口方法時,公共語言運行庫必須封送與 COM 對象之間傳遞的參數(shù)和返回值。對于每個 .NET 框架類型均有一個默認類型,公共語言運行庫將使用此默認類型在 COM 調(diào)用間進行封送處理時封送。例如,C# 字符串值的默認封送處理是封送到本機類型 LPTSTR(指向 TCHAR 字符緩沖區(qū)的指針)?梢栽 COM 接口的 C# 聲明中使用 MarshalAs 屬性重寫默認封送處理。
在 COM 中,返回成功或失敗的常用方法是返回一個 HRESULT,并在 MIDL 中有一個標記為"retval"、用于方法的實際返回值的 out 參數(shù)。在 C#(和 .NET 框架)中,指示已經(jīng)發(fā)生錯誤的標準方法是引發(fā)異常。
默認情況下,.NET 框架為由其調(diào)用的 COM 接口方法在兩種異常處理類型之間提供自動映射。
返回值更改為標記為 retval 的參數(shù)的簽名(如果方法沒有標記為 retval 的參數(shù),則為 void)。
標記為 retval 的參數(shù)從方法的參數(shù)列表中剝離。
任何非成功返回值都將導致引發(fā) System.COMException 異常。
此示例顯示用 MIDL 聲明的 COM 接口以及用 C# 聲明的同一接口(注意這些方法使用 COM 錯誤處理方法)。
下面是接口轉(zhuǎn)換的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 //此類不能再繼承其它基類或接口
{
//這里不能有任何代碼 ,系統(tǒng)自動增加一個缺省的構(gòu)造函數(shù)
}
}
class MainClass
{
public static void Main(string[] args)
{
//命令行參數(shù):
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;
// 調(diào)用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 框架程序可以通過靜態(tài) DLL 入口點的方式來訪問本機代碼庫。DllImport 屬性用于指定包含外部方法的實現(xiàn)的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具有單個定位參數(shù):指定包含被導入方法的 dll 名稱的 dllName 參數(shù)。
3、DllImport具有五個命名參數(shù):
a、CallingConvention 參數(shù)指示入口點的調(diào)用約定。如果未指定 CallingConvention,則使用默認值 CallingConvention.Winapi。
b、CharSet 參數(shù)指示用在入口點中的字符集。如果未指定 CharSet,則使用默認值 CharSet.Auto。
c、EntryPoint 參數(shù)給出 dll 中入口點的名稱。如果未指定 EntryPoint,則使用方法本身的名稱。
d、ExactSpelling 參數(shù)指示 EntryPoint 是否必須與指示的入口點的拼寫完全匹配。如果未指定 ExactSpelling,則使用默認值 false。
e、PreserveSig 參數(shù)指示方法的簽名應當被保留還是被轉(zhuǎn)換。當簽名被轉(zhuǎn)換時,它被轉(zhuǎn)換為一個具有 HRESULT 返回值和該返回值的一個名為 retval 的附加輸出參數(shù)的簽名。如果未指定 PreserveSig,則使用默認值 true。
f、SetLastError 參數(shù)指示方法是否保留 Win32"上一錯誤"。如果未指定 SetLastError,則使用默認值 false。
4、它是一次性屬性類。
5、此外,用 DllImport 屬性修飾的方法必須具有 extern 修飾符。
下面是 C# 調(diào)用 Win32 MessageBox 函數(shù)的示例:
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 );
}
}
面向?qū)ο蟮木幊陶Z言幾乎都用到了抽象類這一概念,抽象類為實現(xiàn)抽象事物提供了更大的靈活性。C#也不例外, C#通過覆蓋虛接口的技術(shù)深化了抽象類的應用。欲了解這方面的知識,請看下一節(jié)-覆蓋虛接口