《Inside C#》筆記(十五) 非托管代碼 上
為了保證向後兼容性,C#和.NET可以通過非托管的方式運行舊代碼。非托管代碼是指沒有被.NET運行時管控的代碼。非托管代碼主要包括:平臺調用服務(PlatformInvocation Services)、不安全代碼(Unsafe Code)、COM互操作(COM interoperability)。
一 平臺調用服務
平臺調用服務(Platform Invocation Services)也被稱作PInvoke,可以使用非托管DLL中的方法、結構甚至是給其傳遞回調函數。在使用非托管DLL前需事先了解DLL內部方法的參數和返回值。
a)基本使用方法為:
MessageBoxA屬於Win32的API,需要先聲明一個與MessageBoxA的方法簽名一致的方法,然後DllImport導入這個DLL,簽名方法必須用static extern修飾。
b)簽名方法也可與DLL中的方法不同名,但需在DllImport的EntryPoint指定原始名稱。
c)使用CharSet
CharSet可以指定DLL所使用的字符集。比如上述的MessageBoxA實際上對應的是Ansi編碼,還有對應Unicode編碼的MessageBoxW,除了直接指定調用哪個,還可用下面的寫法:
編譯器會根據CharSet的類型決定調用哪種MessageBox。這應該需要DLL內部做相應配合,至少需要知道每個MessageBox對應的字符集。
d)回調
不僅C#代碼可以調用DLL的方法,DLL方法也可用回調的方式使用C#代碼。
這裏將PrintWindow作為回調函數傳遞給了API中的EnumWindows方法。
e)Marshal(排列、整理?)
在前面的例子中,DLL中MessageBox的方法參數為:
C#代碼中的方法簽名並沒有與之完全匹配,但卻能正常運行,這是因為編譯器自動進行了默認的Marshal,比如將C#的string類型對應為Win32的LPSTR。這個過程也可以手動進行,使用MarshalAs:
?
如果要Marshal返回值,要標記在方法體上面。
二 編寫不安全代碼
這裏的不安全代碼指的是沒有被.NET運行時托管的代碼,內存的分配、釋放、尋址等都不受約束,比如可以在C#代碼中使用指針,在有些場合C#指針非常有用,比如需要調用C語言編寫的API時、或者需要對內存有完全的控制時。
a)與不安全代碼相關的關鍵字unsafe和fixed
unsafe關鍵字用來告知.NET運行時,相關的代碼塊將不受托管。不受托管的代碼塊可以是方法、屬性、或者是一個方法內部的代碼片段。
fix關鍵字用來“釘”(pinning)住某個對象,這樣GC就不會嘗試對其回收了。但對象在內存的地址不會被固定,地址仍然會被運行時浮動,以避免出現內存碎片。因為地址不固定,所以這時使用指針就要小心了。
b)在C#中使用指針
C#中的指針比較特殊:只能指向值類型、數組、字符串;如果指針指向數組,數組的第一個元素必須是值類型,因為指針實際上要指向的是這個數組的第一個元素;
C#中的指針相關的運算符與C、C++一樣:&,取得某個對象的地址; *,取得對象的值; ->,取得對象中某個成員的值。簡單示例為:
編譯標記為unsafe的代碼前,需要在項目屬性中設置允許不安全代碼。
學習資料:Inside C# by Tom Archer
《Inside C#》筆記(十五) 非托管代碼 上