新建一個項目,名為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







