新建一个项目,名为PInvoke:
建好项目后,添加一个cpp源文件,由于只是一个Demo,我们使用默认的名称Source.cpp:
Source.cpp代码如下:
#include#include #include #include typedef struct MyType{ int i; char *s; double d; struct MyType *p;} MyType;int TestInt(int a, int b){ return a + b;}void TestIntPtr(int *i){ *i = *i * -1;}char* TestCharPtr(char *a, char *b){ return strcat(a, b);}void TestStructPtr(MyType *p){ p->i++; p->d++;}void TestPtrPtr(int **a, int length){ *a = (int*)malloc(sizeof(int) * length); memset(*a, 0, sizeof(int) * length);}void TestArray(int *a, int length){ for (int i = 0; i < length; i++) { a[i]++; }}void TestCallback(void (*pf) ( )){ pf();}int TestCharWidth(char *s){ return strlen(s);}
添加一个新文件Source.def,用于导出Dll中的函数:
内容如下,其中LIBRARY "PInvoke"中的"PInvoke"是项目的名称:
LIBRARY "PInvoke"
EXPORTS TestInt TestIntPtr TestCharPtr TestStructPtr TestPtrPtr TestArray TestCallback TestCharWidthC的工程已经竣工了,下面来在解决方案中新加一个C#项目:
建好后,将ConsoleApplication1项目设置为启动项目,修改Program.cs代码如下:
using System;using System.Collections.Generic;using System.Linq;using System.Runtime.InteropServices;using System.Text;using System.Threading.Tasks;namespace ConsoleApplication1{ class Program { [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public struct MyType { /// int public int i; /// char* [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] public string s; /// double public double d; /// myType* public IntPtr p; } public delegate void Callback(); [DllImport("PInvoke.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] public static extern int TestInt(int a, int b); [DllImport("PInvoke.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] public static extern void TestIntPtr(ref int i); [DllImport("PInvoke.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr TestCharPtr(System.IntPtr a, System.IntPtr b); [DllImport("PInvoke.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr TestStructPtr(ref MyType myType); [DllImport("PInvoke.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] public static extern void TestArray(int[] a, int length); [DllImport("PInvoke.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] public static extern void TestCallback(Callback callback); [DllImport("PInvoke.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] public static extern int TestCharWidth(IntPtr s); static void CallbackFunction() { Console.WriteLine("callback invoked"); } static void Main(string[] args) { // TestInt Console.WriteLine(TestInt(1, 2)); // TestIntPtr int i = 1; TestIntPtr(ref i); Console.WriteLine(i); // TestCharPtr IntPtr helloPtr = Marshal.StringToHGlobalAnsi("hello"); IntPtr worldPtr = Marshal.StringToHGlobalAnsi("world"); IntPtr helloWorldPtr = TestCharPtr(helloPtr, worldPtr); string helloWorld = Marshal.PtrToStringAnsi(helloWorldPtr); Console.WriteLine(helloWorld); Marshal.FreeHGlobal(helloPtr); Marshal.FreeHGlobal(worldPtr); // Marshal.FreeHGlobal(helloWorldPtr); // 因为helloWorldPtr和helloPtr指向的是同一地址,所以再次释放会报错 // TestCharWidth string a = "a的"; IntPtr aPtr = Marshal.StringToHGlobalAnsi(a); // Ansi int len = TestCharWidth(aPtr); Console.WriteLine(len); a = Marshal.PtrToStringAnsi(aPtr); Marshal.FreeHGlobal(aPtr); aPtr = Marshal.StringToHGlobalUni(a); // Unicode len = TestCharWidth(aPtr); // 值是1,strlen没有正确处理unicode,所以不要使用strlen测量unicode字符串的长度 Console.WriteLine(len); a = Marshal.PtrToStringUni(aPtr); Marshal.FreeHGlobal(aPtr); // TestStructPtr MyType myType = new MyType { i = 0, d = 1.1, s = "a的", p = IntPtr.Zero }; TestStructPtr(ref myType); // TestArray int[] array = new int[] { 1, 2, 3 }; TestArray(array, array.Length); // TestCallback TestCallback(CallbackFunction); Console.Read(); } }}
为了调试方便,我们在编译好C的dll后,自动把dll拷贝到C#项目的可执行文件目录下。在PInvoke项目上右键,属性,然后如下图:
按F5调试一下你的代码吧。
PInvoke Interop Assistant
PInvoke Interop Assistant是一个免费的把C/C++代码转成C#/VB.net的工具,非常好用。以下是使用截图:
在左边写native code,右边会自动转换成C#。所以如果有Native的类型不知转成C#该怎么写,它可以直接告诉你。
sample代码下载地址:
PInvoke Interop Assistant下载地址: