やりたいこと
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#コンソールアプリとは違うんだなと痛感しております。
探してもあまり正確な情報が無く、困り果てています。皆様よろしくお願いいたします。
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。