C++からC# DLL を直接利用する方法

# この記事はいったい...？
この記事は「Native C++ と C#ライブラリを CLI を使わずにどうしても連携させたい！！」という, とてもニッチな思いに応えるためのものです. 普通の人には必要のない技術情報です.  

# どうするの？
【[DllExport](https://github.com/3F/DllExport/releases)】を利用して, .NETライブラリをC++から呼び出せるようにしてあげます.
なんてすばらしいアプリケーションなんだぁ～.

最新版をダウンロードしておいてください.

# How to

## 1. C#で .NET Framework ライブラリを作成します
まず, VisualStudioを起動して, 【新しいプロジェクトの作成...】を押下します.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/8a435bd5-3205-8657-8d58-b01adccad03a.png)

【Visual C#】→【クラス ライブラリ (.NET Framework)】→【OK】の順に押下します.
必要であれば, プロジェクト名やソリューション名も変更します.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/995f3491-86f2-0c17-0d58-6b564e9bee7c.png)

以下のようなコードを記述します.
この時点ではエラーが発生していますが, あとで解消するので気にしないでください.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/ac23dca6-84c7-e44f-4e88-f17bce3217da.png)

コピペ用

```csharp
using System;
using System.Runtime.InteropServices;  // Marshalを使うのに必要

namespace ClassLibrary1
{
    public class Class1
    {
        [DllExport]
        public static int Count(IntPtr stringPtr)
        {
            var str = Marshal.PtrToStringAuto(stringPtr);    // IntPtrをstring型に変換
            return str?.Length ?? 0;                         // intやdoubleなどのプリミティブはそのままreturnでOK!!
        }

        [DllExport]
        public static IntPtr Through(IntPtr stringPtr)
        {
            var str = Marshal.PtrToStringAuto(stringPtr);    // IntPtrをstring型に変換
            return Marshal.StringToHGlobalAuto(str);         // string型をIntPtrに変換してreturn
        }

        [DllExport]
        public static IntPtr GetString()
        {
            return Marshal.StringToHGlobalAuto("hoge fuga piyo");    // string型をIntPtrに変換してreturn
        }
    }
}
```

ソリューションエクスプローラの【ソリューション】にカーソルを合わせて右クリックを押下し,
【エクスプローラーでフォルダーを開く】をクリックします.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/0e087bb4-1ad8-9723-f7c0-c32ae7c406e8.png)

フォルダが表示されると思いますので, DllExport.batをコピーします.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/24754915-d8be-bec1-b779-733f1dc4067d.png)

【PowerShell】または【コマンドプロンプト】を起動します.
サンプルではPowerShellを利用しています.

【cd】コマンドを利用して, ソリューションファイルがあるフォルダに移動します.
エクスプローラのフォルダパスをクリックするとコピーできるので, それを貼り付けると楽ちんです.
【cd [フォルダパス]】と入力できたらEnterキーを押下して, 実行しましょう.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/06bc22e3-acd0-f559-5abc-0e5828da416f.png)

【./DllExport.bat -action Configure】と入力してEnterキーを押下します.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/b5589345-ea05-1b0d-f1bf-da699bf83bb9.png)

そうすると, 以下のようなウィンドウが起動するので,
【installed】→【(86+64)】→【Apply】の順に選択していきます.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/e6f96d76-1a9e-b1a7-ba9b-535212a678fd.png)

すぐに実行が完了するので, VisualStudioに戻ります.
すると, 以下のようなアラートが出てくるので, 【すべて再読み込み】を選択します.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/3ca9120f-7520-3bdb-b1ea-53713610bfc6.png)

読み込みが完了すると先ほどまであったエラーが消えていると思います.
ここまでくれば, あとはビルドをするだけです！

【ビルド】→【ソリューションのビルド】を選択してビルドします.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/91e0dae5-9147-a928-39d4-34c55beacff4.png)

すると出力タブにビルドされたdllのパスが載っているので, そこをエクスプローラで開きます.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/a89580e0-0be2-5f59-35df-6847e84f14ac.png)

そこにdllの他に【x86】と【x64】フォルダができあがっています.
その中にあるdllがC++から直で呼び出せるようになっているので, C++から呼び出す際はこのフォルダ内のdllを利用します.
それぞれ【x86=32bit版】/【x64=64bit版】用なので, C++アプリでは適宜使い分けしてください.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/45efc687-5500-3803-8939-785bfea32575.png)



## 2. .NET DLL をC++で利用するアプリを作成します
まず, VisualStudioを起動して, 【新しいプロジェクトの作成...】を押下します.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/8a435bd5-3205-8657-8d58-b01adccad03a.png)

【Visual C++】→【Windows コンソール アプリケーション】→【OK】の順に押下します.
必要であれば, プロジェクト名やソリューション名も変更します.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/2595069b-3103-0d6b-2173-1af252d9d6a1.png)

そうすると, 以下のような画面が表示されます.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/6d96e7ab-f46a-3e41-670a-b938528f5649.png)

おもむろにコードを修正します.
ついでに不要なコメントも削除しておきましょう.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/77b34b99-154a-d66a-d89e-71ac42ee1fce.png)

コピペ用

```cpp
#include "pch.h"
#include <iostream>
#include <windows.h>
#include <string>

using namespace std;

typedef int(*Count)(const wchar_t* str);
typedef const wchar_t*(*Through)(const wchar_t* str);
typedef const wchar_t*(*GetString)();

int main()
{
    auto dll = ::LoadLibrary(L"ClassLibrary1.dll");   // さっき作ったC# DLLの名前を指定する
    {
        auto count = reinterpret_cast<Count>(::GetProcAddress(dll, "Count"));
        wcout << count(L"ほげ") << endl;
        wcout << count(L"fuga") << endl;
        wcout << count(nullptr) << endl;

        auto through = reinterpret_cast<Through>(::GetProcAddress(dll, "Through"));
        auto str0 = through(L"test");
        wcout << str0 << endl;

        auto getString = reinterpret_cast<GetString>(::GetProcAddress(dll, "GetString"));
        auto str1 = getString();
        wcout << str1 << endl;
    }
    if ( dll )
        ::FreeLibrary(dll); // 解放は忘れずに！！

    return 0;
}
```

コードの編集が完了したら, サクッとビルドしてみます.
今回はx86でビルドしましたが, x64でも構いません. ただし, このサンプルとは利用するdllが異なりますのでその点は注意してください.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/52084fb3-e228-6b0d-5536-6db4451503a9.png)

ビルドが完了すると出力タブに実行ファイルの出力先フォルダが表示されますので、そこをエクスプローラで表示します.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/29c09dec-8d03-db2c-c91e-38417ac60d60.png)

C#のdll(x86版)を実行ファイルのあるフォルダにコピーしてあげます.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/b8925281-1d04-4797-2cc7-52931815625d.png)

そうしましたら, PowerShell or コマンドプロンプトを起動して, 実行ファイルのあるフォルダパスに cd コマンドで移動します.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/c24b9536-3548-7f80-083b-18baec7af161.png)

移動できたら, exeを実行します.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/d5297cc2-67b6-2ee2-4639-9bb9ad33d6fa.png)

そうすると, 以下のように, Netive C++ から C#ライブラリを呼び出すことに成功していることが確認できます.
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125601/901cbe7c-f06b-b7cf-c921-f509b239e229.png)


# おわりに
C++からC#のライブラリを使いたいならC++/CLIを利用するのが一般的だと思いますが, さまざまな事由でそれができない場合があると思います. 私もその中の一人です.

今回ご紹介したものは非常にニッチな技術なのであまり一般的ではないと思いますが, もし私のような状況におかれた方がいらっしゃいましたら, 利用してみてください.

また, DllExport.batに関しては, 現状【C# + .NET Framework】のみ利用可能な感じでした. F#でも利用を試みましたが, 失敗に終わるという残念な結果となりましたが, リトライしてみようとは思います.
もちろん .NET Core には非対応でしたので, ご注意ください.


# 余談
今回の記事は 【[Youtube Live](https://www.youtube.com/user/dgakusei)】 で放送しながら調査したものをまとめたものとなっています.
記事にしないだけで, こんな感じの特殊な技術(ニッチな技術？)を調べたり, 普通にコーディングしたりしていますので, 興味がある方がいらっしゃいましたらぜひおこし下さい.

また, 基本的に毎日放送しているので記事のことや.NET関連の質問等がある方は, コメント等でお気軽にご質問ください. 答えられる範囲でお応えいたします. 

【[ホームページ](https://midoliy.com/)】にはE-mailアドレスも記載させていただいていますので, そちらから今回の記事以外のことでもご質問・ご意見等がございましたら, コンタクトしていただければと思います.