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

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

ただいまの
回答率

89.13%

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

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 292

ta93san

score 12

やりたいこと

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】

#pragma once
#include<string>
#include<vector>

#ifdef MYNATIVEDLL_EXPORTS
#define MYNATIVEDLL_API __declspec(dllexport)
#else
#define MYNATIVEDLL_API __declspec(dllimport)
#endif

class MYNATIVEDLL_API NativeVector
{
public:
    float x;
    float y;
    float z;
};

class MYNATIVEDLL_API NativeClass
{
public:
    std::string name;
    std::vector<NativeVector> points;
};

MYNATIVEDLL_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】

#pragma once
using namespace System;

namespace MyCliDLL {

    public ref class CliVector
    {
    public:
        float x;
        float y;
        float z;
    };

    public ref class CliClass
    {
    public:
        System::String^ name;
        System::Collections::Generic::List<CliVector^>^ points;
    };

    public ref class CliFuncs {
    public:
        static int CliFunc(System::String^ num, [Runtime::InteropServices::OutAttribute]System::Collections::Generic::List<CliClass^>^% data);
    };
}


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

#include "pch.h"

#include<MyNativeDll.h>
#include <msclr/marshal_cppstd.h>
#include<iostream>

#include "MyCliDll.h"

namespace MyCliDLL {
    int CliFuncs::CliFunc(System::String^ num, [Runtime::InteropServices::OutAttribute]System::Collections::Generic::List<MyCliDLL::CliClass^>^% data) {
        std::string native_num = msclr::interop::marshal_as<std::string>(num);
        std::vector<NativeClass> *native_data = new std::vector<NativeClass>();

        int result = NativeFunc(native_num, *native_data);
        data = gcnew System::Collections::Generic::List<CliClass^>();
        for (int i = 0; i < native_data->size(); i++) {
            CliClass^ tmp = gcnew CliClass();
            tmp->name = msclr::interop::marshal_as<System::String^>(native_data->at(i).name);
            tmp->points = gcnew System::Collections::Generic::List<CliVector^>();
            for (int j = 0; j < native_data->at(i).points.size(); j++) {
                CliVector^ vec = gcnew CliVector();
                vec->x = native_data->at(i).points.at(j).x;
                vec->y = native_data->at(i).points.at(j).y;
                vec->z = native_data->at(i).points.at(j).z;
                tmp->points->Add(vec);
            }
            data->Add(tmp);
        }
        return result;
    }
}


これを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】

using System;
using System.Collections.Generic;

namespace MyCsDLL
{
    public class CsVector
    {
        public float x;
        public float y;
        public float z;
    }

    public class CsClass
    {
        public string name;
        public List<CoreVector> points;
    }

    public class CsFuncs
    {
        public static int CsFunc(string num, out List<CsClass> data)
        {
            List<MyCliDLL.CliClass> _data;
            MyCliDLL.CliFuncs.CliFunc(num, out _data);
            data = new List<CsClass>();
            foreach(MyCliDLL.CliClass d in _data)
            {
                CsClass tmp = new CsClass();
                tmp.name = d.name;
                tmp.points = new List<CsVector>();
                foreach (MyCliDLL.CliVector v in d.points)
                {
                    tmp.points.Add(new CsVector() { x = v.x, y = v.y, z = v.z });
                }
                data.Add(tmp);
            }
            return 0;
        }
    }
}


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】

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestDll : MonoBehaviour
{
    void Start()
    {
        List<MyCsDLL.CsClass> data;
        MyCsDLL.CsFuncs.CsFunc("1",out data);
        foreach(MyCsDLL.CsClass d in data){
            Debug.Log("/// CsClass ///" + d);
            foreach(MyCsDLL.CsVector v in d.points){
                Debug.Log("// point // "+v.x + "," + v.y + "," + v.z);
            }
        }
    }
}

出ているエラー

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#コンソールアプリとは違うんだなと痛感しております。
探してもあまり正確な情報が無く、困り果てています。皆様よろしくお願いいたします。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

0

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/02/01 16:30

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

    キャンセル

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

  • ただいまの回答率 89.13%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる