前回はC#で書かれたライブラリの作成方法でしたが、今回は、C#から使えるC++で書かれたライブラリの作成方法です。
C++と言っても、C++内部でクラスを使う事は可能ですが、C#から直接呼ぶ事が出来るのは、クラスではない関数のみとなります。
そのため、クラスオブジェクトをC++のライブラリとC#間でやり取りすることは出来ないので、注意してください。(構造体を引数で渡す事は可能です。)
正直、C++のライブラリの作成方法はあまり深く理解していないのですが、メモ程度に。。
事前にVisual Studio をデフォルトのままでインストールするとC++はインストールされないので、C++をインストールしておいてください。
まず、関数を呼び出すC#のプロジェクトを作成しておき、ソリューションの右ボタンで、
追加→新しいプロジェクト
を選択します。
次にプロジェクトを追加しますが、Visual Studio のバージョンにより画面が異なるため、各バージョンに合わせて確認ください。
Visual Studio 2017, 2019の場合
Visual Studio 2019には、上部に言語、OS別表示のフィルタをかけることが出来るので、C++、Windowsを選択します。
次にプロジェクトの一覧の中から Windowsデスクトップウィザードを選択します。
次にプロジェクト名とプロジェクトの場所を指定します。
プロジェクト名は、デフォルト設定ではライブラリ名になるので、ちゃんとした名前を付ける事をお勧めします。
次に アプリケーションの種類に ダイナミックリンクライブラリ(.dll) を選択し、追加オプションでは、
プリコンパイル済みヘッダー(P)とシンボルのエクスポートにチェックを入れます。
→以降は、以下共通を参照ください。
Visual Studio 2015の場合
Visual C++ → Win32 → Win32プロジェクト
と選択し適当な名前(今回は CppDLL とします。)を付けOKボタンをクリックします。
表示されたウィザードで次へをクリックします。
アプリケーションの種類で DLL を選択し、慣れている人は 空のプロジェクト でいいんでしょうけど、私は慣れていないので、 シンボルのエクスポート と プリコンパイル済みヘッダー にチェックを入れ、完了をクリックします。
以下共通
ウィザードでDLLを作成すると、プログラムのたたき台となるサンプルコードを吐き出してくれるので、慣れないうちは、ウィザードを使った方がお勧めです。(私は、今でも使ってますが。。)
次にプロジェクトの設定を行います。
今どきのOSはほとんど64bitなので、今回は64bitOS用のライブライを作成するのを前提とします。
Visual Studio の 構成マネージャー をクリックします。
プラットフォームに x64 を選択します。
ビルドの部分いチェックが入っていない場合はチェックを入れます。
これを アクティブソリューションの構成のDebugとReleaseの両方で行います。
これでけで、ソリューションのビルドを行うと、ソリューションフォルダの直下にx64フォルダが作成されます。
さらにその中のReleaseもしくはDebugフォルダの中に、このように↓dllファイルが作成されてますが、C#から、このdllファイルを使うにはさらに細工が必要になります。
以降は、C++プロジェクトの作成時にウィザードが吐き出した
CPPDLL_API int fnCppDll(void);
の関数がC#から使えるようにC++のヘッダファイルを変更します。
C++ライブライのヘッダファイル(ここでは CppDll.h)を開き、C#から使う関数を
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
// ここに使用する関数を定義する
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
のように extern “C” { } で関数の定義を挟み込みます。
こんな感じ↓
#ifdefの部分は、あんまり分かってない。。
次に作成したC++のライブラリファイル(*.dll)がC#から使用できるようにします。
ここは、いくつかやり方があり、好みが分かれるところだと思いますが、今回は、C++のプロジェクトが作成したDLLファイルの出力先をC#の出力先にに合わせるように変更します。
C++のプロジェクト名の部分を右クリックし、
構成のプロパティ→全般→出力ディレクトリ
を
$(SolutionDir)\WindowsFormsApplication1\bin\$(Configuration)\
とします。(※WindowsFormsApplication1はC#のプロジェクト名です。)
この出力ディレクトリの設定を 構成の Release と Debug の両方設定します。
これで、C#のプログラムから参照できる位置(C#のexeファイルと同じフォルダ)にdllファイルが出力されます。
この設定を間違うと、以下のようなエラーが表示されます。
今回は64bitOS用のDLLとして作成していますが、C#のプロジェクトはOSが64bitであってもプロジェクトが初期設定状態だと、32bitで動作するので、これを64bitで動作するように変更します。
C#のプロジェクト名を右クリックし、 プロパティ をクリックし、以下のウィンドウを表示させ、 左側メニューの ビルド を選択し 32ビットを優先 のチェックを外します。
この設定を 構成 の Debug および Release の両方行います。
この設定を間違うと、 BadImageFormatExceptionはハンドルされませんでした。 というエラーメッセージが表示れます。
これで、とりあえずC#からC++の関数が動作します。
C#からは、以下のようなコードでC++の関数定義を行います。
using System.Runtime.InteropServices;
[DllImport("CppDll.dll")]
public static extern int fnCppDll();
“CppDll.dll”の部分は適宜、作成したdllファイル名に合わせて下さい。
実際に関数を呼び出す部分は、
private void button1_Click(object sender, EventArgs e)
{
int n = fnCppDll();
MessageBox.Show(n.ToString());
}
のように、ボタンクリックイベントで呼び出したとすると、以下のように表示されると動作はOKです。
さらに、これだけど少し足りなくて、C++のプロジェクトのプロパティで、
構成プロパティ→C/C++→コード生成→ランタイムライブラリ
の部分で、構成が
Releaseのとき:マルチスレッド(/MT)
Debugのとき:マルチスレッドデバッグ(/MTd)
に変更しておきます。
この設定をしないと、別のPCでプログラムを実行した際に、開発を行ったVisual Studioと同じバージョンのC++ランタイム(再頒布可能パッケージ)がインストールされていないと動作してくれないので、いざ、実行環境で動作させようとした時にハマったりもします。
以上、少し怪しい部分もあるかと思いますが、こんな感じで。。
関連記事
【C#エラー】System.BadImageFormatException 間違ったフォーマットのプログラムを読み込もうとしました。