ホームに戻る
出典 :
C#からC/C++の関数をコールする方法 まとめ① - Qiita
関連 :
[VC++]基本的なDLLの作成手順 DLLの呼び出し
目次 :

※ この文書は書きかけです。厳密には正しくありません。

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_tsbyteSystem.SByte
符号なしunsigned char std::uint8_tbyteSystem.Byte
16bit整数符号つきsigned short std::int16_tshortSystem.Int16
符号なしunsigned shortstd::uint16_tushortSystem.UInt16
32bit整数符号つきsigned int
signed long
std::int32_tintSystem.Int32
符号なしunsigned int
unsigned long
std::uint32_tuintSystem.UInt32
64bit整数符号つきsigned long longstd::int64_tshortSystem.Int64
符号なしunsigned long longstd::uint64_tushortSystem.UInt64
文字列char*std::stringstringSystem.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); } } }