10687


UnityC#にて重たい処理をDLLにやってもらおうと思い、  
ViusalStudioを用いてC++のDLLを[こちらを参考に](http://ericeastwood.com/blog/17/unity-and-
dlls-c-managed-and-c-unmanaged)作成しました。

メソッドを呼び出すことはできたので、  
今度はクラス経由で呼び出してみようと思い  
C++側のクラスの定義をMSDNの[ここ](https://msdn.microsoft.com/ja-
jp/library/81h27t8c.aspx)を参考にクラスを作成したのですが  
C#側からどのように呼び出せばいいのかがわかりません。

* * *

C#側

[code]

    using UnityEngine;
    using System.Collections;
    
    // Must Need DLL Import 
    using System.Runtime.InteropServices;
    
    public class CallDlls : MonoBehaviour {
    
        // From c++ Dll (unmanaged)
        [DllImport("MathFuncsDll")]
        public static extern float TestMultiply(float a, float b);
    
        // From c++ Dll (unmanaged)
        [DllImport("MathFuncsDll")]
        public static extern float TestDivide(float a, float b);
    
        public float multiply(float a,float b)
        {
            return TestMultiply(a, b);
        }
    }
    
[/code]

* * *

C++DLL側

**// MathFuncsDll.cpp**

[code]

    #include "stdafx.h"
    #include "MathFuncsDll.h"
    #include <stdexcept>
    
    using namespace std;
    
    extern "C" {
    float TestMultiply(float a, float b)
    {
        return a * b;
    }
    
    float TestDivide(float a, float b)
    {
        if (b == 0) {
            return 0;
            //throw invalid_argument("b cannot be zero!");
        }
    
        return a / b;
        }
    }
    
[/code]

**// MathFuncsDll.h**

[code]

    #ifdef MATHFUNCSDLL_EXPORTS
    #define MATHFUNCSDLL_API __declspec(dllexport) 
    #else
    #define MATHFUNCSDLL_API __declspec(dllimport) 
    #endif
    
    extern "C" {
        class MATHFUNCSDLL_API TestClass {
            float TestMultiply(float a, float b);
            float TestDivide(float a, float b);
            //MATHFUNCSDLL_API float TestMultiply(float a, float b);
            //MATHFUNCSDLL_API float TestDivide(float a, float b);
        };
    }
    
[/code]


CC BY-SA 3.0
2015-05-27T04:43:52.083
10657
2019-01-29T04:28:17.960
2015-05-27T11:55:32.233
49
5261
question
c# unity3d c++ pinvoke
C#から自作のC++DLLのクラスを呼び出す方法


DllImport属性を使うことで、ネイティブコードを呼び出すことはできますが、  
C++で作成したクラスをDllImport属性を使って、.NET Frameworkで利用することはできません。

解決策としては、

  1. 関数ベースのインターフェースを別途用意して、管理する
  2. C++/CLI等を利用してラッパーを作成する

等が考えられると思います。


CC BY-SA 3.0
2015-05-27T05:40:54.210
10661
2015-05-27T06:22:21.873
2015-05-27T06:22:21.873
7287
7287
10657
answer


技術的にはC#コードから直接C++クラスのメンバ関数を呼ぶことも可能です。ただし、幾つかの理由からお薦めできません。@時計屋さん回答にある解決策を採用したほうがよいと思います。

DLL/C++クラスのメンバ関数はthiscall呼出し規約(calling
convention)をもちますので、`DllImport`属性で[`CallingConvention.ThisCall`](https://msdn.microsoft.com/ja-
jp/library/system.runtime.interopservices.callingconvention.aspx)を指定し、`EntryPoint`には"マングリングされた関数名"を指定する必要があります。詳細は記事
[How to Marshal a C++ Class](http://www.codeproject.com/Articles/18032/How-to-
Marshal-a-C-Class) が参考になります。

お薦めできない理由：

  * DLL/C++側クラスのコンストラクタ/デストラクタを、C#側から直呼び出すことはできません。少なくともC++クラスオブジェクトの`new`/`delete`操作を行うC++側にヘルパー関数が必要です。
  * DLL/C++側でエクスポートされる"マングリングされた関数名"に、C#側では常に追従させる必要があるためメンテナンスコストが高くなります。


CC BY-SA 3.0
2015-05-27T11:46:34.973
10687
2015-06-14T01:53:31.613
2015-06-14T01:53:31.613
49
49
10657
answer


Unityの事は全く知らないのでC++/CLIを使って良い環境かどうかはわかりませんが、C++/CLIを使った.NETとネイティブC++の連携なら以下の記事が参考になると思います。

  * [[連載! とことん VC++] 第 9 回 C++/CLI を利用した相互運用 ～.NET からのネイティブ C++ 資産の再利用～](https://code.msdn.microsoft.com/VisualC-a1dc1f1d/)
  * [[C++] C++/CLI を用いて、.NET 対応アプリケーションから MFC 対応クラスを使用する](https://code.msdn.microsoft.com/windowsdesktop/VisualC-howto-76f9cd9e)

逆方向（ネイティブC++ → .NET）の記事もあります。

  * [[連載! とことん VC++] 第 8 回 C++/CLI を利用した相互運用 ～ネイティブ C++ から .NET の利用～](https://code.msdn.microsoft.com/VisualC-ee06b200/)

なお、.NETとネイティブC++間でオブジェクトをやり取りする場合は一部を除いて変換しないといけないので、やりたい事によってはオーバーヘッドが大きくなり効果的でないかもしれません。  
また、C++/CLIの参考書・記事は少なく、.NETとネイティブC++に精通していないと学習するのが難しと思います。


CC BY-SA 3.0
2015-05-28T02:13:07.860
10704
2015-05-28T02:13:07.860
7291
10657
answer


一般的には時計屋さんの回答にあるように関数ベースでの呼び出しが使われます。

難易度は上がりますが、他の方が紹介されていない方法としてCOMインターフェースがあります。  
まじめにレジストリ登録しなくても、C++にてCOMインターフェースに沿ったC++クラスを実装しそのポインターを返します。C#側ではCOMインターフェースに相当するC#インターフェースを定義し、[Marshal.GetObjectForIUnknown()](https://msdn.microsoft.com/ja-
jp/library/system.runtime.interopservices.marshal.getobjectforiunknown\(v=vs.110\).aspx)などでインスタンスを得ます。  
この方法であればyohjpさんの提案に近い状態ですが、マングル名を意識する必要はなくなります。


CC BY-SA 3.0
2015-05-28T02:30:10.367
10706
2015-05-28T02:30:10.367
4236
10657
answer


C++のクラスをC#でDLLを経由して呼び出すことはできません。  
そういう場合は、C++/CLIでラッピングする必要があります。

[code]

    class TestClass {
    public:
        float TestMultiply(float a, float b);
        float TestDivide(float a, float b);
    };
    
[/code]

として定義した場合は、C++/CLIで以下のように書くことができます。

[code]

    public ref class TestClassWrapper {
    internal:
        TestClass* Native;
    public:
        TestClassWrapper() { Native = new TestClass(); } //非マネージクラスのインスタンスを生成
        ~TestClassWrapper(){ this->!TestClassWrapper(); } // ファイナライザを呼ぶ
        !TestClassWrapper(){ delete native; }  // ファイナライザでは非マネージクラスのインスタンスを削除
        float Multiply(float a, float b){ return Native->TestMultiply(a, b); }
        float Diviede(float a, float b){ return Native->TestDivide(a, b); }
    };
    
[/code]

コンストラクタで非マネージクラスのオブジェクトを生成し、ファイナライザでそのオブジェクトを削除、デストラクタではそのままファイナライザを呼び出します。  
あとはメソッドをC++のメンバー関数を呼べばいいですね。

C#やVBからは以下のように使うことができます。

[code]

    using(var calc = new TestClassWrapper()) {
        var ret1 = calc.Multiply(5.5f, 5.5f); 
        var ret2 = calc.Diviede(10.0f, 2.0f);
        Console.WriteLine(ret1 + ":" + ret2);
    }
    
[/code]

* * *

2019年1月29日 - "{"が抜けていたので修正しました。

* * *


CC BY-SA 4.0
2018-04-06T13:23:29.597
43011
2019-01-29T04:28:17.960
2019-01-29T04:28:17.960
27861
27861
10657
answer