web-dev-qa-db-ja.com

C#コードのC ++ DLLで定義されたクラスを使用する

C++で記述されたDLLがあります。C#コードでこのDLLを使用する必要があります。検索後、P/Invokeを使用すると必要な関数にアクセスできることがわかりましたが、これらの関数はクラスで定義され、非静的なプライベートメンバー変数を使用しています。したがって、関数を適切に使用するには、このクラスのインスタンスを作成できる必要があります。インスタンスを作成できるように、このクラスにアクセスするにはどうすればよいですか?これを行う方法を見つけることができませんでした。

C++ dllは私のコードではないことに注意する必要があると思います。

63
Dan Vogel

C#コードでC++クラスを直接使用する方法はありません。 PInvokeを間接的に使用して、タイプにアクセスできます。

基本的なパターンは、クラスFooのすべてのメンバー関数に対して、メンバー関数を呼び出す関連する非メンバー関数を作成することです。

class Foo {
public:
  int Bar();
};
extern "C" Foo* Foo_Create() { return new Foo(); }
extern "C" int Foo_Bar(Foo* pFoo) { return pFoo->Bar(); }
extern "C" void Foo_Delete(Foo* pFoo) { delete pFoo; }

今では、これらのメソッドをC#コードにPInvokingするだけです。

[DllImport("Foo.dll")]
public static extern IntPtr Foo_Create();

[DllImport("Foo.dll")]
public static extern int Foo_Bar(IntPtr value);

[DllImport("Foo.dll")]
public static extern void Foo_Delete(IntPtr value);

欠点は、渡すべき扱いにくいIntPtrがありますが、このポインターの周りにC#ラッパークラスを作成して、より使いやすいモデルを作成するのは簡単なことです。

このコードを所有していない場合でも、元のDLLをラップして小さなPInvokeレイヤーを提供する別のDLLを作成できます。

83
JaredPar

マーシャルC++クラスとPInvokeの使用

C++コード、ClassName.h

class __declspec(dllexport) CClassName
{
 public:
    CClassName();
    ~CClassName();
    void function();

};

C++コード、ClassName.cpp

CClassName::CClassName()
{
}

CClassName::~CClassName()
{
}

void CClassName::function()
{
    std::cout << "Bla bla bla" << std::endl;

}

C++コード、呼び出し元関数のClassNameCaller.hファイル

#include "ClassName.h"      

#ifdef __cplusplus
extern "C" {
#endif

extern __declspec(dllexport) CClassName* CreateClassName();

extern __declspec(dllexport) void DisposeClassName(CClassName* a_pObject);

extern __declspec(dllexport) void function(CClassName* a_pObject);


#ifdef __cplusplus
}
#endif

C++コード、呼び出し元関数のClassNameCaller.cppファイル

#include "ClassNameCaller.h"


CClassName* CreateClassName()
{
    return new CClassName();
}

void DisposeClassName(CClassName* a_pObject)
{
    if(a_pObject!= NULL)
    {
        delete a_pObject;
        a_pObject= NULL;
    }
}

void function(CClassName* a_pObject)
{
    if(a_pObject!= NULL)
    {
        a_pObject->function();
    }
}

C#コード

[DllImport("ClassNameDll.dll")]
static public extern IntPtr CreateClassName();

[DllImport("ClassNameDll.dll")]
static public extern void DisposeClassName(IntPtr pClassNameObject);

[DllImport("ClassNameDll.dll")]
static public extern void CallFunction(IntPtr pClassNameObject);

//use the functions
IntPtr pClassName = CreateClassName();

CallFunction(pClassName);

DisposeClassName(pClassName);

pClassName = IntPtr.Zero; 
18
Amir Twito

ここ は、C++クラスメソッドをVB-から呼び出す方法のサンプルです。C#の場合、手順4でサンプルプログラムを書き換えるだけで済みます。

4
Dmitry Khalatov

myfile.i

%module learnaboutswig

class A

{

public:

    void boringfunction(char *charstr);
};

swig.orgからSwigをダウンロードします

swig -c ++ -csharp myfile.i

出力を見て、うまくいくかどうかを確認してください。

3
astronought

これを行った方法は、アンマネージC++ DLLの周りに薄いマネージC++ラッパーを作成することです。マネージラッパーには、.NETアプリケーションに必要なインターフェイスを公開するアンマネージコードをラップする「プロキシ」クラスが含まれています。これは少し二重の作業ですが、通常の環境では非常にシームレスな操作が可能です。状況によっては(ASP.NETなど)依存関係によって物事が難しくなりますが、おそらくそれには遭遇しません。

2

これを処理し、必要なインターフェイスを公開する仲介者DLL(おそらくC++))を記述する必要があるかもしれません。あなたのDLLが担当しますサードパーティのDLLをロードし、このC++オブジェクトのインスタンスを作成し、設計したAPIを介して必要に応じてそのメンバー関数を公開します。その後、P/Invokeを使用してAPIを取得し、オブジェクトをクリーンに操作します。

注:DLLのAPIについては、データ型をプリミティブ(long、int、char *など)に制限して、モジュールの境界の問題を回避してください。

2
Brian

JaredParに同意します。マネージコードでアンマネージクラスのインスタンスを作成することはできません。

もう1つは、マネージC++でDLLを再コンパイルするか、それからCOMコンポーネントを作成できれば、はるかに簡単になります/

2
badbadboy