基於.net開發,託管的便利好處自然不用再多言,垃圾回收、內存管理等等,加之強大的FCL類庫作支持後盾,一般情況下我們都不會直接用到非託管代碼,一些常用的底層api都已經被FCL類庫進行了很好的封裝,我們只需要知道用到哪一個類即可。
但是類庫雖然強大,卻非萬能的,總有一些基於底層的api沒有被封裝,或者說程序要調用一些第三方的接口,一般都是c/c++的dll。就我本人所知,許多地區的醫療保險接口便都是非託管代碼,如c++寫的。
在這種情況之下,我們便要考慮到託管代碼對於非託管代碼的調用問題了,這種技術稱之爲互操作性技術。
在.net平臺下提供了三種互操作的實現方式:
- Platform Invoke(P/Invoke),即平臺調用,主要用於調用C庫函數和Windows API
- C++ Introp, 主要用於Managed C++(託管C++)中調用C++類庫
- COM Interop, 主要用於在.NET中調用COM組件和在COM中使用.NET程序集。
1、平臺調用:
要想實現平臺調用,需要明確以下信息:非託管dll文件的名稱、函數名稱以及參數詳細信息。然後,在託管代碼之中聲明調用非託管的函數,最後即可調用。如下面代碼便是一個典型的C#的平臺調用例子:
#region P/Invoke 設置本地時間
//聲明dll名稱以及函數信息和參數信息,注意,如果dll名稱非全路徑,則默認在C盤system32文件夾路徑下,或者當前程序運行路徑之下進行尋找
[DllImport("kernel32.dll")]
private static extern bool SetLocalTime(ref SYSTEMTIME time);
[StructLayout(LayoutKind.Sequential)]
private struct SYSTEMTIME
{
public short year;
public short month;
public short dayOfWeek;
public short day;
public short hour;
public short minute;
public short second;
public short milliseconds;
}
/// <summary>
/// 設置本地計算機時間
/// </summary>
/// <param name="dt">DateTime對象</param>
public static void SetLocalTime(DateTime dt)
{
SYSTEMTIME st;
st.year = (short)dt.Year;
st.month = (short)dt.Month;
st.dayOfWeek = (short)dt.DayOfWeek;
st.day = (short)dt.Day;
st.hour = (short)dt.Hour;
st.minute = (short)dt.Minute;
st.second = (short)dt.Second;
st.milliseconds = (short)dt.Millisecond;
SetLocalTime(ref st);
}
#endregion
2、C++ Introp調用:
通過C++ Introp調用的方式,允許非託管代碼與託管代碼處於一個程序集之內,甚至同一份源代碼之中。該調用方式是在源代碼上直接鏈接和編譯非託管代碼來實現與非託管代碼進行互操作的。C++ Interop使用託管C++來包裝非託管C++代碼,然後編譯生成程序集,然後在託管代碼中引用該程序集,從而來實現與非託管代碼的互操作。
3、COM Interop調用:
COM(Component Object Model,組件對象模型)是微軟之前推薦的一個開發技術,過去微軟使用該技術進行了許多功能組件的開發,如果把這些已經成型的COM組件用.net託管方式重做一遍,顯然是沒有意義且極爲不現實的。那麼想要在這種情況之下使用COM組件的功能,就需要一種託管代碼對其的調用技術,就是COM Interop,COM Interop不僅支持在託管代碼中使用COM組件,而且還支持向CMO組件功能託管對象。
在.NET中使用COM對象,主要有3種方法:
- 使用TlbImp工具爲COM組件創建一個互操作程序集來綁定早期的COM對象,這樣就可以在程序中添加互操作程序集來調用COM對象。(簡單的可以理解爲用調用中間層的方式去調用COM組件)
- 通過P/Invoke創建COM對象或使用C++ Interop爲COM對象編寫包裝類。
- 通過反射來後期綁定COM對象。
我們經常使用的是第一種方式,在VS開發工具中引用一個COM組件,需要這個組件已被註冊(regsvr32.exe進行COM組件的註冊或註銷),在引用之時,VS會用到Tlbimp.exe工具來爲該COM組件生成一個互操作程序集,該程序集又稱爲運行時可調用包裝 (RCW),其中包含了包裝COM組件中的類和接口。Visual Studio 將生成組件的引用添加至項目。
這樣的話,我們使用RCW互操作程序集就像使用一個普通託管對象一樣。
同時,我們也可以將託管代碼生成爲可供COM組件進行操作的程序集,需要通過COM可調用包裝(COM Callable Wrapper,即CCW),具體操作,需要對.net託管項目的屬性進行相應的設置,勾選項目屬性頁裏面的爲COM互操作註冊選項,使程序集對COM可見。經過設置之後,編譯的過程就會變爲:Visual Studio會調用類型庫導出工具(Tlbexp.exe)爲.NET程序集生成COM類型庫再使用程序集註冊工具(Regasm.exe)來完成對.NET程序集和生成的COM類型庫進行註冊,這樣COM客戶端可以使用CCW服務來對.NET對象進行調用了。