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++程序。
好了。可以执行程序,看执行结果了。