主要方式
C#主要通過兩種方式提供對非托管代碼的調(diào)用,,第一種是使用平臺(tái)調(diào)用(Platform Invoke,,P/Invoke),第二種是使用不安全代碼(unsafe),,日常開發(fā)中我們使用最多的就是第一種,,所以下面我們來介紹下如何使用平臺(tái)調(diào)用。
平臺(tái)調(diào)用
CLI通過P/Invoke提供該功能,,它允許對非托管DLL所導(dǎo)出的函數(shù)執(zhí)行API調(diào)用,。平臺(tái)調(diào)用主要有這些步驟:聲明外部函數(shù)、映射數(shù)據(jù)類型、錯(cuò)誤處理,。
1. 聲明外部函數(shù)
和類的所有普通方法一樣,,我們需要在類的上下文中聲明目標(biāo)API,同時(shí)為它添加extern修飾符,,從而把它聲明為外部函數(shù),。它幾乎總是靜態(tài)的(static)。如下代碼所示,,我們聲明了外部函數(shù)SendMessage(位于user32.dll中),,EntryPoint屬性可以省略不寫,也可以指定一個(gè)函數(shù)名用來覆蓋默認(rèn)的行為,,CharSet屬性則用于指定調(diào)用API的Unicode還是ASCII版本或者由.NET平臺(tái)自動(dòng)嘗試調(diào)用,。
using System;
using System.Runtime.InteropServices;
namespace PayPlatformProxy
{
public class LevyTest
{
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
}
}
2. 數(shù)據(jù)類型映射
- 托管代碼的基元類型不會(huì)隨處理器改變大小,無論是32位還是64位處理器,,int類型始終是32位,,但在非托管代碼中,內(nèi)存指針會(huì)隨處理器而變化,。因此,,C++中的HANDLE和LPVOID等類型不能對應(yīng)為int,而應(yīng)該對應(yīng)為IntPtr,,其大小隨處理器內(nèi)存布局而變化,。
- 微軟官網(wǎng)文檔--類型封送有涉及類型的映射,大家可以參考學(xué)習(xí)下,。另外,,pinvoke.net中也有很多系統(tǒng)API在C#中的聲明示例,輸入關(guān)鍵字看看示例是如何映射,,從而完成非托管類型到托管類型的映射,。
- 此外,通過工具pinvoke-interop-assistant也可以來生成C++方法在C#中對應(yīng)的簽名,,實(shí)例演示部分我們將使用此工具,。
實(shí)例演示
如下我們定義了C++的方法LevyAgeAdd,它的目的是輸入姓名和年齡然后返回新的年齡(newAge)和提示年齡增長的信息(resultMsg),。
extern "C"
{
_declspec(dllexport) void LevyAgeAdd(const char* name, int age, int* newAge, char* resultMsg);
}
下面我們在pinvoke-interop-assistant工具中輸入C++方法的聲明,,然后點(diǎn)擊Generate就能生成對應(yīng)的C#方法的簽名了,如下圖所示
接著我們通過C#代碼來調(diào)用此C++代碼,,首先拷貝出外部函數(shù)LevyAgeAdd的聲明,,然后調(diào)用方法傳入各個(gè)參數(shù)值,如下:
[DllImport("Project1.dll", EntryPoint="LevyAgeAdd", CharSet = CharSet.Auto)]
public static extern void LevyAgeAdd([In()][MarshalAs(UnmanagedType.LPStr)] string name, int age, ref int newAge, IntPtr resultMsg);
void Main()
{
int newAge = 0;
IntPtr p = Marshal.AllocHGlobal(255);
//調(diào)用C++方法
LevyAgeAdd("楊小豆", 25, ref newAge, p);
string resultMsg = Marshal.PtrToStringAnsi(p);
$"newAge的值: {newAge}".Dump();
$"resultMsg的值: {resultMsg}".Dump();
}
輸出結(jié)果如下圖:
哈哈,,調(diào)用結(jié)果正確,!真香,!事實(shí)上我們C++代碼的實(shí)現(xiàn)也確實(shí)是把年齡增加了3歲然后輸出提示信息的。希望本文能對童鞋們有所幫助,!