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
うまく動いていそう