新建一个项目,名为PInvoke:
建好项目后,添加一个cpp源文件,由于只是一个Demo,我们使用默认的名称Source.cpp:
Source.cpp代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
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
TestCharWidth
C的工程已经竣工了,下面来在解决方案中新加一个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代码下载地址:http://files.cnblogs.com/dc10101/PInvoke.zip
PInvoke Interop Assistant下载地址:http://files.cnblogs.com/dc10101/PInvokeInteropAssistant.zip