LoginSignup
115
131

More than 5 years have passed since last update.

C#からC++のDLLを使う

Last updated at Posted at 2014-04-29

Javaのコードばかり書いてたら、C#からC++のDLLを扱う場面で苦戦したのでメモをかねてまとめました。

コメントの方でも別の方法の提案を頂いています。

ミス等があればコメント・編集リクエスト願います。

  • Visual Studio 2013
  • C#は.NET Framework 4.5を使用

DLL側

mydll.h
extern "C" {
    __declspec(dllexport) int add(int a, int b);
    __declspec(dllexport) int* arr_a(int length);
    __declspec(dllexport) void arr_b(int* arr, int length);
}

__declspec(dllexport) はDLLで公開したい関数に必要。
extern "C" は、C++の仕様で関数名が変わってしまうのを防ぐ。

mydll.cpp
#include "mydll.h"

int add(int a, int b) {
    return a + b;
}


int* arr_a(int length) {
    int* arr = new int[length];

    for (int i = 0; i < length; i++) {
        arr[i] = i;
    }

    return arr;
}


void arr_b(int* arr, int length) {
    for (int i = 0; i < length; i++) {
        arr[i] = i;
    }
}

なんてことのない、普通のプログラム。

C#側

Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

// DllImportに必要
using System.Runtime.InteropServices;

namespace UseMyDLL
{
    class Program
    {
        [DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        private extern static int add(int a, int b);

        // ポインタはC#側ではIntPtrに
        [DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        private extern static IntPtr arr_a(int length);

        [DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        private extern static void arr_b(IntPtr arr, int length);

        static void Main(string[] args)
        {
            callAdd();
            callArr_a();
            callArr_b();
        }

        // 戻り値がintの場合
        private static void callAdd()
        {
            int addResult = add(1, 2);

            Console.WriteLine("== add ==");
            Console.WriteLine(addResult + "\n");
        }

        // 戻り値がポインタの場合
        private static void callArr_a()
        {
            const int NUM = 5;
            int[] arr = new int[NUM];

            // 戻り値のポインタをIntPtrで受け取る
            IntPtr ptr = arr_a(NUM);

            // マネージ配列へコピー
            Marshal.Copy(ptr, arr, 0, NUM);

            // これでメモリが解放される?
            // (追記)さすがに駄目でした
            //Marshal.FreeCoTaskMem(ptr);

            Console.WriteLine("== arr_a ==");
            foreach(int n in arr)
            {
                Console.Write(n + " ");
            }
            Console.WriteLine("\n");
        }

        // 引数がポインタの場合
        private static void callArr_b()
        {
            const int NUM = 5;
            int[] arr = new int[NUM];

            // アンマネージ配列のメモリを確保
            IntPtr ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)) * NUM);

            // 引数でポインタを渡す
            arr_b(ptr, NUM);

            // マネージ配列へコピー
            Marshal.Copy(ptr, arr, 0, NUM);

            // アンマネージ配列のメモリを解放
            Marshal.FreeCoTaskMem(ptr);

            Console.WriteLine("== arr_b ==");
            foreach (int n in arr)
            {
                Console.Write(n + " ");
            }
            Console.WriteLine("\n");
        }
    }
}

実行結果

== add ==
3

== arr_a ==
0 1 2 3 4

== arr_b ==
0 1 2 3 4

うまく動いていそう

115
131
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
115
131