最近、ある友人にC言語を教えているのですが、ふと思ったことがあります。
1. DLLファイルの作成
C言語やC++言語はDLL(ダイナミックリンクライブラリ)ファイルにすれば読み込めるとのことです。まずはDLLファイルを作っていきます。
1.1 Visual Studio で2つのプロジェクトを作成する
呼び出すほうは C#, 呼び出されるほうは C なので、プロジェクトを2つに分ける必要があります。この段階ですごい今更なのですが、なぜ Visual Studio が「ソリューション」と「プロジェクト」で使い分けているのかがようやく分かりました(笑)
まずはC#のプロジェクトですね。シンプルにコンソールアプリ(.NET Core) でいきます。
できたらソリューションエクスプローラーから新規プロジェクトを追加します。 私はC++の空のプロジェクトを選びました。
これでソリューションに 2つのプロジェクトが作成されました。
1.2 ソースコードを書く
後は普通にC言語を書いていけばいいのですが、その前に約束事を記述していきます。 まずはヘッダーファイルを書きます。
#pragma once //自動生成されてる extern "C" __declspec (dllexport) int main();
これでDLLにするための関数を定義できました。上記では main 関数がDLL化されたとき、C# から C のmain関数を呼び出せるようになります。 あとはソースファイルに c++ ファイルを追加して
普通にC言語を書いていきます。C++も書けますが、それは別の記事で紹介します。
#include<stdio.h> #include"dlldefine.h" //先ほど定義したヘッダファイル int main() { printf("この文字はC言語で表示しています!\n"); return 0; }
1.3 プロパティを変更する
このままビルドしても実行可能形式(.exe)が生成されてしまうので、DLLを生成するように変更します。 C++のプロジェクトを右クリックして、"プロパティ"をクリックするとプロパティページが表示されます。 構成の種類を "ダイナミック ライブラリ(.dll)" に変更します。
これでビルドしてみます。
どうやら ソリューション内の Debug というディレクトリに保存されているみたいですね。
これでDLL化が終わりました!
2. C#からDLLを使う
2.1 C#から呼び出すコードを書く
C#プロジェクトの Program.cs を次のように記述します。
using System.Runtime.InteropServices; namespace DLLApp { class Program { [DllImport("DLLfile.dll")] static extern int main(); //DLL内で定義された関数 static void Main(string[] args) { main(); //C言語のmain関数を実行する } } }
DLLImportでDLLファイルを指定する際には、先ほど生成されたDLLファイル名を記述します。 さて、これで準備はできたように思えますが、このまま実行すると例外が発生します。
どうやらDLLファイルが見つからないようです。なぜでしょうか?
2.2 例外が起こる原因
答えはプロジェクトによって、ビルドの出力先ディレクトリが異なるからです。
C/C++の場合: DLLApp\Debug C#の場合: DLLApp\DLLApp\bin\Debug\netcoreapp3.0
C#のほうが階層が深く、そのディレクトリ内でDLLを参照しようとするために例外が発生してしまうのです。 解決する方法はいくつかあります。
- DLLとC#アプリの出力先ディレクトリを統一する
- DLLの出力先ディレクトリをC#出力先ディレクトリに合わせる
- C#アプリの出力先ディレクトリをDLL出力先ディレクトリに合わせる
- DLLファイルをC#アプリの出力先にコピペする
この中で最も分かりやすいのは 1番目ですね。2番目と3番目はどちらもビルドしてみないと正確なパスが分かりません。4番目は配布されている場合は良いかもしれませんが、同じソリューションで開発している場合、DLLを変更するたびに毎回コピペする必要があり非効率です。
2.3 出力先ディレクトリの統一
各プロジェクトでプロパティを設定します。
共通のビルドパスとして DLLApp\Build\
に設定します。
まずはC#からです。
ただし、.NET Core 3.0 フレームワークで開発すると netcoreapp3.0
というディレクトリが自動で生成される ので、これを考慮しないといけません。
次に、DLLの出力先を変更します。先ほどの考慮を踏まえて、設定するパスとしては DLLApp\Build\netcoreapp3.0\
となります。(ディレクトリを指定するので、最後に \
を付けないと警告が出ます。)
これでDLLの出力先とアプリの出力先のパスが統一されました。あとはビルドするだけですね。
3 ビルドと実行
Visual Stufio には複数のプロジェクトを一気にビルドする方法があります。これがバッチビルドです。"ビルド" タブに "バッチビルド" があります。 色々と項目がありますが、それぞれのプロジェクトで Debug を選択して ビルドのチェックボックスにチェックを入れました。(使用しているOSによってはアプリが64ビット版で動作するので、それに合わせて x64を選択します。)これで "ビルド" をクリックするとチェックを入れた項目がビルドされます。
同じディレクトリにビルドされていますね
後は実行して動くか確かめてみましょう!
無事に動いていますね。C#からではなく、Cから文字を表示しています!
おわりに
CのファイルをDLL化することで、C#からCを使うことができました。 複雑なプログラムをDLL化すれば、利用する側としては引数と関数名さえ分かっていれば簡単に利用することができるのがメリットだと思います!
C#マジ愛してる。
今回作ったプログラム
参考にどうぞ。
参考にしたサイト
ありがとうございます。