新建一个项目,名为PInvoke:

image

 

image

 

建好项目后,添加一个cpp源文件,由于只是一个Demo,我们使用默认的名称Source.cpp:

image

 

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中的函数:
image

 

内容如下,其中LIBRARY "PInvoke"中的"PInvoke"是项目的名称:

LIBRARY "PInvoke"
    EXPORTS
        TestInt
        TestIntPtr
        TestCharPtr
        TestStructPtr
        TestPtrPtr
        TestArray
        TestCallback
        TestCharWidth

C的工程已经竣工了,下面来在解决方案中新加一个C#项目:

image

建好后,将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项目上右键,属性,然后如下图:

image

 

按F5调试一下你的代码吧。

 

PInvoke Interop Assistant

PInvoke Interop Assistant是一个免费的把C/C++代码转成C#/VB.net的工具,非常好用。以下是使用截图:

image

在左边写native code,右边会自动转换成C#。所以如果有Native的类型不知转成C#该怎么写,它可以直接告诉你。

 

sample代码下载地址:https://files.cnblogs.com/dc10101/PInvoke.zip

PInvoke Interop Assistant下载地址:https://files.cnblogs.com/dc10101/PInvokeInteropAssistant.zip

posted on 2012-07-02 15:16  MainTao  阅读(4898)  评论(3编辑  收藏  举报