ホームに戻る
出典 :
関連 :
目次 :
※ この文書は書きかけです。厳密には正しくありません。
C++ライブラリをC#から参照
C/C++で作成したDLLをC#でインポートし、関数をコールするための手順(ここではVisual Studio 2013を用いる)を記す。
ライブラリ(C++)側の操作
特に無し。
アプリケーション(C#)側の操作
ライブラリ関数を呼び出す処理を実装し、ビルドを行う。
Program.cs
using System.Runtime.InteropServices;
namespace Application
{
internal class Program
{
// インポート
// Library.dll の Test() 関数を _Test() 関数に紐づける
[DllImport( "Library.dll", EntryPoint = "Test")]
static extern void _Test();
static void Main()
{
_Test();
}
}
}
注意が必要な点
C++とC#では型に互換性が無く、データ授受の際に一部の型を互換性のある形式に変換(マーシャリング)する必要がある。詳細は後述。
また、64bit環境でビルドしたライブラリは32bit環境ではインポートできないため、プロジェクトの設定を揃える必要がある。(逆も同様?)
マーシャリング
データの授受に当たって、データ型を互換性のある型に変換することをマーシャリングと呼ぶ。
例えばC++の string とC#の string は互換性が無く、そのままの形式ではデータを渡すことができない。
変換が必要な型
| C++ | C# |
標準 (C互換) | std (C++11以降) | C#型 | .NET型 |
8bit整数 | 符号つき | signed char | std::int8_t | sbyte | System.SByte |
符号なし | unsigned char | std::uint8_t | byte | System.Byte |
16bit整数 | 符号つき | signed short | std::int16_t | short | System.Int16 |
符号なし | unsigned short | std::uint16_t | ushort | System.UInt16 |
32bit整数 | 符号つき | signed int signed long | std::int32_t | int | System.Int32 |
符号なし | unsigned int unsigned long | std::uint32_t | uint | System.UInt32 |
64bit整数 | 符号つき | signed long long | std::int64_t | short | System.Int64 |
符号なし | unsigned long long | std::uint64_t | ushort | System.UInt64 |
文字列 | char* | std::string | string | System.String |
C++における int のサイズは本来処理系依存だが、Win32 / Win64では32bitに統一されている。
実装例
いずれも CPlusDLL.dll をロードするものとする。
整数型
マーシャリングは不要。但し(ポインタによる)参照渡しの場合は ref / out 修飾が必要。
ライブラリ(C++)
#ifdef __cplusplus
#define DLLEXPORT extern "C" __declspec(dllexport)
#else
#define DLLEXPORT __declspec(dllexport)
#endif
// 値渡し(int)
DLLEXPORT int __stdcall Mul(int x, int y)
{
return x * y;
}
// 参照渡し(int*)
// ( result に結果を格納して返す)
DLLEXPORT void __stdcall MulUsePointer(int* x, int* y, int* result)
{
*result = (*x) * (*y);
}
アプリケーション(C#)
using System;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
// 値渡し
[DllImport("CPlusDLL", EntryPoint = "Mul")]
static extern int _Mul(int x, int y);
// 参照渡し
// DLLへの入力は ref 、DLLからの出力は out (または ref )で修飾
[DllImport("CPlusDLL", EntryPoint = "MulUsePointer")]
static extern void _MulUsePointer(ref int x, ref int y, out int result);
// 実処理部
static void Main(string[] args)
{
int x = 10;
int y = 20;
int resultMul = _Mul(x, y);
Console.WriteLine("Mul = " + resultMul);
_MulUsePointer(ref x, ref y, out int resultMulUsePointer);
Console.WriteLine("MulUsePointer = " + resultMulUsePointer);
}
}
}
文字列
C++の string / wstring とC#の string には互換性が無いため、マーシャリングが必要。
ライブラリ(C++)
#include <string>
#ifdef __cplusplus
#define DLLEXPORT extern "C" __declspec(dllexport)
#else
#define DLLEXPORT __declspec(dllexport)
#endif
static std::string _str = "";
static std::wstring _wstr = L"";
// マルチバイト文字列型
// (char* ⇒ string)
DLLEXPORT void __stdcall SetNameStr(const char* t)
{
_str = std::string(t);
}
// (string ⇒ char*)
DLLEXPORT const char* __stdcall GetNameStr()
{
return _str.c_str();
}
// ワイド文字列型
// (wchar_t* ⇒ wstring)
DLLEXPORT void __stdcall SetNameWStr(const wchar_t* t)
{
_wstr = std::wstring(t);
}
// (wstring ⇒ wchar_t*)
DLLEXPORT const wchar_t* __stdcall GetNameWStr()
{
return _wstr.c_str();
}
アプリケーション(C#)
using System;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
// マルチバイト文字列型
// CharSet = CharSet.Ansi
[DllImport("CPlusDLL", EntryPoint = "SetNameStr", CharSet = CharSet.Ansi)]
static extern void _SetNameStr(string t);
[DllImport("CPlusDLL", EntryPoint = "GetNameStr", CharSet = CharSet.Ansi)]
static extern IntPtr _GetNameStr();
// ワイド文字列型
// CharSet = CharSet.Unicode
[DllImport("CPlusDLL", EntryPoint = "SetNameWStr", CharSet = CharSet.Unicode)]
static extern void _SetNameWStr(string t);
[DllImport("CPlusDLL", EntryPoint = "GetNameWStr", CharSet = CharSet.Unicode)]
static extern IntPtr _GetNameWStr();
static void Main(string[] args)
{
string testString = "東京都新宿 1-1";
_SetNameStr(testString);
Console.WriteLine("GetNameStr = " + Marshal.PtrToStringAnsi(_GetNameStr()));
_SetNameWStr(testString);
Console.WriteLine("GetNameWStr = " + Marshal.PtrToStringUni(_GetNameWStr()));
}
}
}
配列
マーシャリングが必要。
但し、多次元配列を直接マーシャリングすることができない(要調査)ため、1次元配列に変換を行う。
ライブラリ(C++)
#ifdef __cplusplus
#define DLLEXPORT extern "C" __declspec(dllexport)
#else
#define DLLEXPORT __declspec(dllexport)
#endif
DLLEXPORT void __stdcall SetArray(const int* arr)
{
(略)
}
アプリケーション(C#)
using System;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
[DllImport("CPlusDLL", EntryPoint = "SetArray")]
static extern void _SetArray(IntPtr arr);
static void Main(string[] args)
{
int[,] matrix = new int[3, 3]
{
{0, 1, 2},
{3, 4, 5},
{6, 7, 8},
};
// matrix をシリアライズ
int arr[] = new int[3*3];
int i = 0;
for (int y = 0; y < 3; ++y)
{
for (int x = 0; x < 3; ++x)
{
arr[i] = matrix[y,x];
++i;
}
}
// データ授受用の IntPtr 生成( arr (matrix) の内容を格納する領域を確保
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(int)) * 3 * 3)
// arr の内容を ptr にコピー
Marshal.Copy(arr, 0, ptr, 3*3);
// DLL APIコール
_SetArray(ptr);
// ptr の内容を arr にコピー
Marshal.Copy(ptr, arr, 0, 3*3);
// 領域解放
Marshal.FreeHGlobal(ptr);
}
}
}
構造体
書式指定[StructLayout(LayoutKind.Sequential)]、入力・出力マーシャリング指定[In][Out]の指定が必要。
[In] : 入力方向(C# ⇒ C++)のマーシャリングを行う
[Out] : 出力方向(C++ ⇒ C#)のマーシャリングを行う
ライブラリ(C++)
#ifdef __cplusplus
#define DLLEXPORT extern "C" __declspec(dllexport)
#else
#define DLLEXPORT __declspec(dllexport)
#endif
// 構造体定義
struct stSample
{
int a;
int b;
};
// DLLに構造体を渡す
DLLEXPORT void __stdcall SetStruct(const stSample* st)
{
(略)
}
// DLLから構造体を受け取る
DLLEXPORT void __stdcall GetStruct(stSample* st)
{
(略)
}
アプリケーション(C#)
using System;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
// 構造体定義
// class として定義する点に注意(参照渡しとするため)
[StructLayout(LayoutKind.Sequential)]
class stSample
{
int a;
int b;
};
class Program
{
// DLLに構造体を渡す
[DllImport("CPlusDLL", EntryPoint = "_SetStruct")]
static extern void _SetStruct([In] stSample st);
// DLLから構造体を受け取る
[DllImport("CPlusDLL", EntryPoint = "_GetStruct")]
static extern void _GetStruct([Out] stSample st);
static void Main(string[] args)
{
stSample st;
// DLL APIコール
_SetStruct(st);
_GetStruct(st);
}
}
}