C++のdllをC#で呼び出し方法とは?Visual Studio 2019での事前準備から紹介!

この記事でわかること
C++のdllをC#で呼び出しとは?
C#でC++のdllの呼び出しを紹介します。
過去にC++で作成した関数をC#で使いたい場合、C++の関数をdll(ダイナミックリンクライブラリ)にしてC#でそれを呼び出すことが可能です。
これを行うにはちょっとしたテクニックが必要になります。それをVisual Studio 2019を使って説明します。
C++のdllをC#で呼び出しをするにはマーシャリングの処理が必要
そもそもC++とC#はシステムが異なります。そこで異なるシステム間でデータを交換できるようにするための処理、つまり、マーシャリングの処理が必要です。
単純にC++のdllをC#で呼び出すことはできません。それはC#とC++のメモリへの配置が基本型以外では異なっているためです。しかし、マーシャリングの処理を行えばC#でC++のdllの呼び出しが可能になるのです。
Visual Studio 2019での事前準備
C#でC++のdllの呼び出しを行うためにVisual Studio 2019での事前準備に取りかかりましょう。Visual Studio 2019を起動させたら、まず、C#の.NET Frameworkのコンソールアプリケーションを選択し、それをここでは「ConsoleApp1」と名付けます。
次に呼び出しに使うC++のdll作成です。右の「ソリューションエクスプローラー」の「ソリューション」を右クリックしてそこで現われる「追加」の中の「新しいプロジェクト」を選択した後、C++のダイナミックライブラリ(.dll)を選択し「CPlusDLL」と名付けます。これで呼び出しに使うdllの作成準備完了です。
コードを書く
それではコードを書いていきます。
・ヘッダーファイル
・C++の関数
・C#のソースコード
・Visual Studio 2019の設定
・デバッグ
この順番で進めていきます。
C++のヘッダーファイル
「ソリューションエクスプローラー」の「CPlusDLL」を右クリックして「追加」の「新しい項目」を選択し、その中から「ヘッダーファイル」を選び、「dellExport.h」と名付けます。
ソースコードは次の通りです。
1 2 3 4 5 6 7 8 9 |
#pragma once #ifdef __cplusplus #define DLLEXPORT extern "C" __declspec(dllexport) #else #define DLLEXPORT __declspec(dllexport) #endif using namespace std; |
なお、
1 |
using namespace std; |
をヘッダーファィルに書き込んでいます。
C++のソースコード
C++のソースコードを見てみましょう。「ソリューションエクスプローラー」の「CPlus」を右クリックして「追加」の「新しい項目」で「C++ソースコード(.cpp)」を選択。「CPlus.cpp」と名付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#include <string> #include "dellExport.h" static string _str = ""; static wstring _wstr = L""; DLLEXPORT int __stdcall multiply(int x, int y) { return 2*x + 3*y; } DLLEXPORT void __stdcall multiplyUsePointer(int *x, int *y, int *result) { *result = 2*(*x) + 3*(*y); } DLLEXPORT void __stdcall setNameString(const char *str) { _str = string(str); } DLLEXPORT const char *__stdcall getNameString() { return _str.c_str(); } DLLEXPORT void __stdcall setNameWString(const wchar_t *str) { _wstr = wstring(str); } DLLEXPORT const wchar_t *__stdcall getNameWString() { return _wstr.c_str(); } |
C#のコード
C#のソースコードです。「ConsoleApp1」で自動生成された「Program.cs」にソースコードを書き、C++のdllの呼び出しを行います。第一の方法としてDllImport関数の第一引数に呼び出しされるCPlusDLL.dllの絶対パスのソースコードを紹介します。後に、絶対パスを書かない方法を紹介します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
using System; using System.Runtime.InteropServices; namespace ConsoleApp1 { class Program { [DllImport("C:\\path\\to\\ConsoleApp1\\x64\\Debug\\CPlusDLL.dll", EntryPoint = "multiply")] static extern int _multiply(int x, int y); [DllImport("C:\\path\\to\\ConsoleApp1\\x64\\Debug\\CPlusDLL.dll", EntryPoint = "multiplyUsePointer")] static extern void _multiplyUsePointer(ref int x, ref int y, out int result); [DllImport("C:\\path\\to\\ConsoleApp1\\x64\\Debug\\CPlusDLL.dll", EntryPoint = "setNameString", CharSet = CharSet.Ansi)] static extern void _setNameString(string t); [DllImport("C:\\path\\to\\ConsoleApp1\\x64\\Debug\\CPlusDLL.dll", EntryPoint = "getNameString", CharSet = CharSet.Ansi)] static extern IntPtr _getNameString(); [DllImport("C:\\path\\to\\ConsoleApp1\\x64\\Debug\\CPlusDLL.dll", EntryPoint = "setNameWString", CharSet = CharSet.Unicode)] static extern void _setNameWString(string t); [DllImport("C:\\path\\to\\ConsoleApp1\\x64\\Debug\\CPlusDLL.dll", EntryPoint = "getNameWString", CharSet = CharSet.Unicode)] static extern IntPtr _getNameWString(); static void Main(string[] args) { int x = 100; int y = 500; int resultMultiply = _multiply(x, y); Console.WriteLine("multiply = " + resultMultiply); _multiplyUsePointer(ref x, ref y, out int resultMultiplyUsePointer); Console.WriteLine("multiplyUsePointer = " + resultMultiplyUsePointer); string testString = "東京都中央区大手町1-1-1"; _setNameString(testString); Console.WriteLine("getNameString = " + Marshal.PtrToStringAnsi(_getNameString())); _setNameWString(testString); Console.WriteLine("getNameWString = " + Marshal.PtrToStringUni(_getNameWString())); #if DEBUG Console.WriteLine("続行するには何かキーを押してください..."); Console.ReadKey(); #endif } } } |
絶対パスを指定する方法
第一の方法としてDllImport関数の第一引数でCPlusDLL.dllへは絶対パスで指定しなければいけません。\\path\\to\\となっているのは各人の設定環境に合わせて下さい。
プロパティを編集する方法
DllImprt関数の第一引数にどうしても絶対パスを指定したくないという場合は「ソリューションエクスプローラー」の「CPlusDLL」を右クリックして、プロパティを選択します。「全般」の中で、「出力ディレクトリ」を編集します。左上で「アクティブ(Debug)」を選択した場合は「出力ディレクトリ」の「編集」をクリックして「$(SolutionDir)bin\Debug」に編集します。
次に左上で「Release」を選択し、「出力ディレクトリ」を「$(SolutionDir)bin\Release\」に編集します。
プロパティを編集したときのコード
プロパティを編集したら、Program.csのソースコードは次の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
using System; using System.Runtime.InteropServices; namespace ConsoleApp1 { class Program { [DllImport("CPlusDLL.dll", EntryPoint = "multiply")] static extern int _multiply(int x, int y); [DllImport("CPlusDLL.dll", EntryPoint = "multiplyUsePointer")] static extern void _multiplyUsePointer(ref int x, ref int y, out int result); [DllImport("CPlusDLL.dll", EntryPoint = "setNameString", CharSet = CharSet.Ansi)] static extern void _setNameString(string t); [DllImport("CPlusDLL.dll", EntryPoint = "getNameString", CharSet = CharSet.Ansi)] static extern IntPtr _getNameString(); [DllImport("CPlusDLL.dll", EntryPoint = "setNameWString", CharSet = CharSet.Unicode)] static extern void _setNameWString(string t); [DllImport("CPlusDLL.dll", EntryPoint = "getNameWString", CharSet = CharSet.Unicode)] static extern IntPtr _getNameWString(); static void Main(string[] args) { int x = 100; int y = 500; int resultMultiply = _multiply(x, y); Console.WriteLine("multiply = " + resultMultiply); _multiplyUsePointer(ref x, ref y, out int resultMultiplyUsePointer); Console.WriteLine("multiplyUsePointer = " + resultMultiplyUsePointer); string testString = "東京都中央区大手町1-1-1"; _setNameString(testString); Console.WriteLine("getNameString = " + Marshal.PtrToStringAnsi(_getNameString())); _setNameWString(testString); Console.WriteLine("getNameWString = " + Marshal.PtrToStringUni(_getNameWString())); #if DEBUG Console.WriteLine("続行するには何かキーを押してください..."); Console.ReadKey(); #endif } } } |
バッチビルド
ビルドを行う前にすることがあります。Visual Studio 2019の「ビルド」をクリックし、「バッチビルド」を選択します。「ビルド」のチェックボックスで、「ConsoleApp1」は全てチェックを入れ、「CPlusDLL」は「x64」にのみチェックを入れます。そして「ビルド」を行うと、エラーなしに正常終了するはずです。
デバッグ
Visual Studio 2019の上部真ん中にある「開始」をクリックしてデバッグを行います。きちんとCPlusDLL.dllを呼び出ししていれば、コマンドプロンプトが立ち上がり、次のように表示されれば成功です。
1 2 3 4 5 |
multiply = 1700 multiplyUsePointer = 1700 getNameString = 東京都中央区大手町1-1-1 getNameWString = 東京都中央区大手町1-1-1 続行するには何かキーを押してください... |
C++で定義した関数が行われきちんとCPlusDLL.dllの呼び出しが行われていることが分かります。
C++のdllをC#で呼び出し
システムの違うC++のdllをC#で呼び出しすることの例を紹介しました。
・Visual Studio 2019の設定
・ソースコード
・デバッグ
以上でC#でC++のdllの呼び出しに成功しました。興味のある方は自分でソースコードを書いて実際に行ってみて下さい。