質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

C++/CLI

C++/CLIは、.NET Frameworkの共通言語基盤であるCLI向けにC++を拡張したプログラム言語です。前身のC++マネージ拡張と比較するとシンプルで分かりやすい構文になっており、高い可読性を持ちます。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Mono

Monoは、Ecma標準に準じた.NET Framework互換の環境を実現するためのオープンソースのソフトウェア群です。Linux、Mac OS X、Windowsなど多くのプラットフォームで動作します。その他にも、特定プラットフォーム向けに特化したサブプロジェクトも存在します。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

2回答

4729閲覧

UnityでC++のNativeのDLLを使いたい

ta93san

総合スコア23

DLL

DLL(Dynamic Link Library)とは、他のモジュールからも使用する事が出来る、関数とデータが格納されているモジュールのことです。

C++/CLI

C++/CLIは、.NET Frameworkの共通言語基盤であるCLI向けにC++を拡張したプログラム言語です。前身のC++マネージ拡張と比較するとシンプルで分かりやすい構文になっており、高い可読性を持ちます。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Mono

Monoは、Ecma標準に準じた.NET Framework互換の環境を実現するためのオープンソースのソフトウェア群です。Linux、Mac OS X、Windowsなど多くのプラットフォームで動作します。その他にも、特定プラットフォーム向けに特化したサブプロジェクトも存在します。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2020/01/31 02:44

編集2020/01/31 02:52

やりたいこと

C++で書かれたNativeのDLLがあります。
これをUnityで使いたいというのが目的です。

環境は以下の通りです。

  • Windows10 pro x64
  • Unity 2019.1.5f1
  • VisualStudio2017

UnityプロジェクトのProjectSettingは以下のようになっています。
スクリプティングランタイムバージョン:.NET 4.x相当
API互換レベル:.NET 4.x

仮にDLLの内容を以下のような内容とします。名前は「MyNativeDLL.dll」とします。
Release x64でビルドしてあります。
【MyNativeDLL.hpp】

C++

1#pragma once 2#include<string> 3#include<vector> 4 5#ifdef MYNATIVEDLL_EXPORTS 6#define MYNATIVEDLL_API __declspec(dllexport) 7#else 8#define MYNATIVEDLL_API __declspec(dllimport) 9#endif 10 11class MYNATIVEDLL_API NativeVector 12{ 13public: 14 float x; 15 float y; 16 float z; 17}; 18 19class MYNATIVEDLL_API NativeClass 20{ 21public: 22 std::string name; 23 std::vector<NativeVector> points; 24}; 25 26MYNATIVEDLL_API int NativeFunc(std::string num, std::vector<NativeClass> &data);

やってみたこと

考え付いた方法を2つ試しました。

  • ①C++/CLIでラッパーDLLを作成してC#プログラムで使う時と同じように使う。
  • ②①がダメだったので①をさらにC#で作ったDLLでラップして使う。

結論から言うとどちらもうまくいきませんでした。

①C++/CLIでラッパーDLLを作成する。

VisualStudioの新規プロジェクトから「Visual C++ → CLR」でプロジェクトを作成します。
名前は「MyCliDLL」としました。
この時.NET Framework4.6.1が選択されていました。
プロジェクトプロパティからMyNativeDLLのヘッダーとDLLを参照しました。
内容は以下のようにします。
【MyCliDLL.hpp】

C++

1#pragma once 2using namespace System; 3 4namespace MyCliDLL { 5 6 public ref class CliVector 7 { 8 public: 9 float x; 10 float y; 11 float z; 12 }; 13 14 public ref class CliClass 15 { 16 public: 17 System::String^ name; 18 System::Collections::Generic::List<CliVector^>^ points; 19 }; 20 21 public ref class CliFuncs { 22 public: 23 static int CliFunc(System::String^ num, [Runtime::InteropServices::OutAttribute]System::Collections::Generic::List<CliClass^>^% data); 24 }; 25} 26

実装は以下の通りです。
【MyCliDLL.cpp】

C++

1#include "pch.h" 2 3#include<MyNativeDll.h> 4#include <msclr/marshal_cppstd.h> 5#include<iostream> 6 7#include "MyCliDll.h" 8 9namespace MyCliDLL { 10 int CliFuncs::CliFunc(System::String^ num, [Runtime::InteropServices::OutAttribute]System::Collections::Generic::List<MyCliDLL::CliClass^>^% data) { 11 std::string native_num = msclr::interop::marshal_as<std::string>(num); 12 std::vector<NativeClass> *native_data = new std::vector<NativeClass>(); 13 14 int result = NativeFunc(native_num, *native_data); 15 data = gcnew System::Collections::Generic::List<CliClass^>(); 16 for (int i = 0; i < native_data->size(); i++) { 17 CliClass^ tmp = gcnew CliClass(); 18 tmp->name = msclr::interop::marshal_as<System::String^>(native_data->at(i).name); 19 tmp->points = gcnew System::Collections::Generic::List<CliVector^>(); 20 for (int j = 0; j < native_data->at(i).points.size(); j++) { 21 CliVector^ vec = gcnew CliVector(); 22 vec->x = native_data->at(i).points.at(j).x; 23 vec->y = native_data->at(i).points.at(j).y; 24 vec->z = native_data->at(i).points.at(j).z; 25 tmp->points->Add(vec); 26 } 27 data->Add(tmp); 28 } 29 return result; 30 } 31}

これをRelease x64でビルドしました。
Build時に出てきたもの(MyCliDLL.dllとかMyCliDLL.pdb)とMyNativeDLL.dllをUnityのAssets/Plugins/に配置してVisualStudioから普通のC#プログラムでやるときと同じように参照してみました。
VisualStudioの方はちゃんと参照されてるのですが、そもそもVisualStudioのプロジェクトセッティングとUnityは関係ないのでUnityではMyCliDLL.dllを参照できないということが分かりました。

②C#で①のラッパーDLLを作成する。

UnityでC++/CLIのDLLが使えないのならC#で作ったManagedなDLLならさすがに使えるだろうと考えました。
VisualStudioのプロジェクト新規作成からVisual C# → クラスライブラリ(.NET Standard)でプロジェクトを作成しました。
名前は「MyCsDLL」としました。
プロジェクトプロパティからターゲットフレームワークを「.NET Standard2.0」としました。
参照に①で作成したMyCliDLL.dllを追加します。
内容は以下のようにします。
【MyCsDLL.cs】

C#

1using System; 2using System.Collections.Generic; 3 4namespace MyCsDLL 5{ 6 public class CsVector 7 { 8 public float x; 9 public float y; 10 public float z; 11 } 12 13 public class CsClass 14 { 15 public string name; 16 public List<CoreVector> points; 17 } 18 19 public class CsFuncs 20 { 21 public static int CsFunc(string num, out List<CsClass> data) 22 { 23 List<MyCliDLL.CliClass> _data; 24 MyCliDLL.CliFuncs.CliFunc(num, out _data); 25 data = new List<CsClass>(); 26 foreach(MyCliDLL.CliClass d in _data) 27 { 28 CsClass tmp = new CsClass(); 29 tmp.name = d.name; 30 tmp.points = new List<CsVector>(); 31 foreach (MyCliDLL.CliVector v in d.points) 32 { 33 tmp.points.Add(new CsVector() { x = v.x, y = v.y, z = v.z }); 34 } 35 data.Add(tmp); 36 } 37 return 0; 38 } 39 } 40}

Release x64でビルドしC#のコンソールアプリからは呼び出し可能なことを確認しました。
ビルド時に出てきたファイル(MyCsDLL.dll,MyCsDLL.pdb,MyCsDLL.deps.json)と①のMyCliDLL.dllとMyNativeDLL.dllをUnityのAssets/Plugins/に配置しました。
今回はVisualStudioの方でわざわざ参照しなくても、Unityの方でMyCsDLL.dllを参照してくれたようです。
UnityにはMyCsDLL.dllを呼び出す適当なコンポーネントを作っておきます。
【TestDll】

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class TestDll : MonoBehaviour 6{ 7 void Start() 8 { 9 List<MyCsDLL.CsClass> data; 10 MyCsDLL.CsFuncs.CsFunc("1",out data); 11 foreach(MyCsDLL.CsClass d in data){ 12 Debug.Log("/// CsClass ///" + d); 13 foreach(MyCsDLL.CsVector v in d.points){ 14 Debug.Log("// point // "+v.x + "," + v.y + "," + v.z); 15 } 16 } 17 } 18} 19

出ているエラー

Editor上で実行するときは下記のようなランタイムエラーが出ます。

InvalidProgramException: Invalid IL code in MyCliDLL.CliFuncs:CliFunc(string,System.Collections.Generic.List`1<MyCliDLL.CliClass>&): IL_0024: add C#のListのメンバ関数のAddとC++/CLIのListのAddが番う扱いになっている?ILってなんぞや?という状態です。

ビルドしようとすると下記のようなエラーが出ます。MyCliDLL.dllは確かにAssets/Pluginsに配置しているので、includeが許可されていないのが原因と思います。

ArgumentException: The Assembly MyCliDll is referenced by MyCsDLL ('Assets/Plugins/MyCsDLL.dll'). But the dll is not allowed to be included or could not be found.

おねがい

UnityはMonoだから普通のC#コンソールアプリとは違うんだなと痛感しております。
探してもあまり正確な情報が無く、困り果てています。皆様よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ずいぶん時間が経ってしまいましたが、ご回答いただいた方ありがとうございました。
結局Dllimportを使う方法をとりました。
この場合、各クラスの各メンバーに対応した関数を作って呼び出すという方法になりました。
例)

cpp

1class A{ 2private: 3 int num; 4public: 5 int GetNum(){return num;} 6}

に対して

cpp

1int A_GetNum(A instance){return instance.GetNum();}

という関数を用意しておき、C#側ではA_GetNumを呼び出すという流れになります。

投稿2022/10/31 06:17

ta93san

総合スコア23

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

外の為環境が無い為確実な回答ではありませんご了承ください。

DLLをご自身で作成されているのであれば、
extern "C" { ... } を用いてDLLを作成し、
そのDLLをPluginsフォルダに入れ、[DllImport] キーワードを使用して直接C#コードからDLLを参照するのが定石な気がします
(ご提示の内容だけだとラッパークラスをわざわざ作る必要性は低いように感じます)

投稿2020/01/31 06:08

MMashiro

総合スコア2380

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ta93san

2020/02/01 07:30

ご回答いただき有難うございます。 Dllimportを使う方法では、関数をインポートすることしかできないと認識しております。 つまり引数や戻り値としてストラクチャーやクラスのインスタンスを渡すことができないと。 stringとlistも使えず、int,float,double,charの配列でデータをやり取りするしかないということですよね?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問