P/Invoke Interop 實例


新建一個項目,名為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代碼下載地址:http://files.cnblogs.com/dc10101/PInvoke.zip

PInvoke Interop Assistant下載地址:http://files.cnblogs.com/dc10101/PInvokeInteropAssistant.zip


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM