P/Invoke的全稱是Platform Invoke (平台調用) 它實際上是一種函數調用機制通 過P/Invoke我們就可以調用非托管DLL中的函數。
P/Invoke依次執行以下操作:
1. 查找包含該函數的非托管DLL
2. 將該非托管DLL加載到內存中
3. 查找函數在內存中的地址並將其參數按照函數的調用約定壓棧
4. 將控制權轉移給非托管函數
步驟:
1. 建立"Win32 Project -> DLL" application。
2. 編C++類及調用函數:
"cpp.h"
1 class Student 2 { 3 public: 4 int getNumber(int i); 5 }; 6 7 extern "C" __declspec(dllexport) int _stdcall getStuNumber(int i);
"cpp.cpp"
1 #include "stdafx.h" 2 #include "Cpp.h" 3 4 int Student::getNumber(int i) 5 { 6 return i+100; 7 } 8 9 extern "C" _declspec(dllexport) int _stdcall getStuNumber(int i) 10 11 { 12 Student stu; 13 return stu.getNumber(i); 14 }
extern "C" : extern "C"使得在C++中使用C編譯方式成為可能。在“C++”下定義“C”函數,需要加extern “C”關鍵詞。用extern "C"來指明該函數使用C編譯方式。
__declspec(dllexport): 將一個函數聲名為導出函數。
_stdcall :以標准方式調用,起到查找入口點的作用。
3. 編C#調用
1 using System; 2 using System.Runtime.InteropServices; 3 namespace CSharp 4 { 5 public class CppInvoke 6 { 7 [DllImport("CPP.dll", CallingConvention=CallingConvention.StdCall)]
8 public static extern int getStuNumber(int i); 9 } 10 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 int j = CppInvoke.getStuNumber(100); 16 Console.WriteLine(j); 17 Console.ReadLine(); 18 } 19 } 20 }
DllImport: 用來標識該方法是非托管的代碼方法,在編譯器編譯的時候它能夠正確的認識出被該特性標記的是外來代碼段。當到達程序運行的時候,也能夠正確的認識出該代碼是引用非托管的代碼,這樣CLR會去加載非托管DLL文件,然后查找到入口點進行調用。
CallingConvention:在平台調用的過程中起到查找入口點的作用,在托管代碼進行非托管代碼入口點查找時,會通過CallingConvention中的值進行確認非托管入口點的調用約定。此項也可省略。
4. 設置輸出路徑,調試選項:
a. 將C++Project的“output directory” 和C#project的“output path” 設置在同一目錄,這樣以遍 C#程序能夠找到C++ DLL.
b. 選上 "Enable unmanaged code debugging"選項,以便調試時候跟蹤到C++程序。
好了。可以執行程序,看執行結果了。