たくのろじぃのメモ部屋

プログラミング(C#)の基礎やそれを応用した技術情報をメモしておくブログです。

【C#】C を C# から使いたかったので DLL 化した話

最近、ある友人にC言語を教えているのですが、ふと思ったことがあります。

C言語ってC#から読み込めるのかな?」と。

1. DLLファイルの作成

C言語C++言語はDLL(ダイナミックリンクライブラリ)ファイルにすれば読み込めるとのことです。まずはDLLファイルを作っていきます。

1.1 Visual Studio で2つのプロジェクトを作成する

呼び出すほうは C#, 呼び出されるほうは C なので、プロジェクトを2つに分ける必要があります。この段階ですごい今更なのですが、なぜ Visual Studio が「ソリューション」と「プロジェクト」で使い分けているのかがようやく分かりました(笑)

まずはC#のプロジェクトですね。シンプルにコンソールアプリ(.NET Core) でいきます。

f:id:takunology:20191230163755p:plain

できたらソリューションエクスプローラーから新規プロジェクトを追加します。
私はC++の空のプロジェクトを選びました。

f:id:takunology:20191230164341j:plain

f:id:takunology:20191230164349p:plain

これでソリューションに 2つのプロジェクトが作成されました。

f:id:takunology:20191230164539p:plain

1.2 ソースコードを書く

後は普通にC言語を書いていけばいいのですが、その前に約束事を記述していきます。
まずはヘッダーファイルを書きます。

f:id:takunology:20191230170244p:plain

#pragma once //自動生成されてる
extern "C" __declspec (dllexport) int main();

これでDLLにするための関数を定義できました。上記では main 関数がDLL化されたとき、C# から C のmain関数を呼び出せるようになります。
あとはソースファイルに c++ ファイルを追加して

f:id:takunology:20191230172045p:plain

普通にC言語を書いていきます。C++も書けますが、それは別の記事で紹介します。

#include<stdio.h>
#include"dlldefine.h" //先ほど定義したヘッダファイル

int main() {
    printf("この文字はC言語で表示しています!\n");
    return 0;
}

1.3 プロパティを変更する

このままビルドしても実行可能形式(.exe)が生成されてしまうので、DLLを生成するように変更します。
C++のプロジェクトを右クリックして、"プロパティ"をクリックするとプロパティページが表示されます。
構成の種類を "ダイナミック ライブラリ(.dll)" に変更します。

f:id:takunology:20191230172722j:plain

これでビルドしてみます。

f:id:takunology:20191230172908p:plain

どうやら ソリューション内の Debug というディレクトリに保存されているみたいですね。

f:id:takunology:20191230173148p:plain

これで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ファイル名を記述します。
さて、これで準備はできたように思えますが、このまま実行すると例外が発生します。

f:id:takunology:20191230183055p:plain

どうやらDLLファイルが見つからないようです。なぜでしょうか?

2.2 例外が起こる原因

答えはプロジェクトによって、ビルドの出力先ディレクトリが異なるからです。

C/C++の場合: DLLApp\Debug
C#の場合: DLLApp\DLLApp\bin\Debug\netcoreapp3.0

C#のほうが階層が深く、そのディレクトリ内でDLLを参照しようとするために例外が発生してしまうのです。
解決する方法はいくつかあります。

この中で最も分かりやすいのは 1番目ですね。2番目と3番目はどちらもビルドしてみないと正確なパスが分かりません。4番目は配布されている場合は良いかもしれませんが、同じソリューションで開発している場合、DLLを変更するたびに毎回コピペする必要があり非効率です。

2.3 出力先ディレクトリの統一

各プロジェクトでプロパティを設定します。 共通のビルドパスとして DLLApp\Build\ に設定します。 まずはC#からです。

f:id:takunology:20191230185708j:plain

ただし、.NET Core 3.0 フレームワークで開発すると netcoreapp3.0 というディレクトリが自動で生成される ので、これを考慮しないといけません。

次に、DLLの出力先を変更します。先ほどの考慮を踏まえて、設定するパスとしては DLLApp\Build\netcoreapp3.0\ となります。(ディレクトリを指定するので、最後に \ を付けないと警告が出ます。)

f:id:takunology:20191230192627j:plain

これでDLLの出力先とアプリの出力先のパスが統一されました。あとはビルドするだけですね。

3 ビルドと実行

Visual Stufio には複数のプロジェクトを一気にビルドする方法があります。これがバッチビルドです。"ビルド" タブに "バッチビルド" があります。
色々と項目がありますが、それぞれのプロジェクトで Debug を選択して ビルドのチェックボックスにチェックを入れました。(使用しているOSによってはアプリが64ビット版で動作するので、それに合わせて x64を選択します。)これで "ビルド" をクリックするとチェックを入れた項目がビルドされます。

f:id:takunology:20191230194532p:plain

f:id:takunology:20191230194613p:plain

同じディレクトリにビルドされていますね

f:id:takunology:20191230193849p:plain

後は実行して動くか確かめてみましょう!

f:id:takunology:20191230194718p:plain

無事に動いていますね。C#からではなく、Cから文字を表示しています!

おわりに

CのファイルをDLL化することで、C#からCを使うことができました。
複雑なプログラムをDLL化すれば、利用する側としては引数と関数名さえ分かっていれば簡単に利用することができるのがメリットだと思います!

C#マジ愛してる。

今回作ったプログラム

参考にどうぞ。

github.com

参考にしたサイト

ありがとうございます。

qiita.com

新しいサイトの立ち上げについて(仮)

お久しぶりです。今年も残すところ1週間をきりましたね。あまり実感がわきませんが...

さて、今回は技術的な内容ではなく、お知らせみたいな感じです。

Webアプリ製作の勉強の傍ら、"たくのろじぃの自習室" というサイトを作成することにしました。内容はブログとあまり変わりません。ブログの場合、過去の記事になるほど探すのが面倒です。カテゴリを分けて見やすくしたいと思ったのと、ASP.NET Coreをやってみたいという2点の理由で、新しく立ち上げようと思いました。もちろん、ブログも更新します。

いまのところこんな感じです。

f:id:takunology:20191225004531p:plain

何を目指しているのか

最近いろんなことに手をつけているのですが、やりたいことがありすぎて1つに定まっていない状況です。今のところ、やりたいことはこんな感じです。

  • ASP.NET Core によるウェブサイト制作
  • WPF アプリケーション開発 (MVVMの理解)
  • Minecraft 自動化の続き
  • 深層学習 (今のところフォワード方向だけ)
  • 村人に村をつくらせるためのAI構想? (深層学習 × Minecraftで何かしたい)
  • Xamarin によるアプリケーション開発
  • Windows 10 IoT Core の使い方について

多すぎて笑えますねw

とにかく、「C#を応用して、ある程度の環境へ対応できるようにしたい」のが目標です。何年かかるかわかりませんが...。

なにか進展があればブログを更新していきます。今後もよろしくお願いします。

Windows 10 IoT Core を動かしてみた #1 導入編

RaspberryPi 4が発売されるということで思い出したことがあって、「そういえば家にRaspberryPi3があったな」と。久しぶりに起動してみようとして電源投入しようとしてみたら起動画面が黒いまま動かなかったのでOSを再度書き込むことに...。

PCにmicroSDをさしてみたところ、エクスプローラーがフリーズしたのでカードを買い直しました。 この機にOSも何か新しいのがないか探してみたところ、Windows 10 IoT Coreというものを見つけたので早速導入してみました。

セットアップ

まずはこのサイトに飛んで、Windows 10 IoT Core Dashboard をダウンロード&インストール。

docs.microsoft.com

インストールが完了すると、こんな画面が起動するので

f:id:takunology:20191209232252j:plain

MicroSDカードをさしたらDownload and Installをクリックして書き込みます。書き込めたらラズパイにさして起動します。 初回の起動はすごく時間がかかります。自分の環境では20分くらいでした。

起動できるとセットアップ画面になるので言語とネットワーク接続設定を行います。

f:id:takunology:20191209232535j:plain

f:id:takunology:20191209232909j:plain

設定が終わるとデバイスのステータス画面が表示されます。

f:id:takunology:20191209234215j:plain

これで動かすところまでできました。ここまで約1時間くらいだったと思います。意外と簡単に導入できました。

使ってみた感想

とにかく動作が遅いです。おそらくリッチなUIを使っているのが原因だと思われます。OSというかアプリ自体がUWPだと思うので、描画が遅いのではないかと...。ただ使えないわけではなく、少し時間を置けば多少はもたつきますが動きます。

いいなと思った点はラズパイでWindowsが動かせる点ですね。Microsoftの動画を見ていると、Visual Studioで作成したアプリをラズパイにアタッチして実行しているものがありました。つまりラズパイで.NET frameworkのアプリが動かせるということです。なんだかやってみたくなりました。