2011-01-19 13 views
11

で使用するためのC++クラスをDLLEXPORTはどうすればよい私は「myCppClass」クラスが含まれているC++ DLLプロジェクトを作成し、によって記載されているように、次のコードを使用してエクスポートDLLしようとしている: http://msdn.microsoft.com/en-us/library/a90k134d(v=vs.80).aspx私はC#アプリケーション

class __declspec(dllexport) CExampleExport : //public CObject 
{ ... class definition ... }; 

私はafx.hを必要とし、それがMFC Dllであることを意味するので、 "public CObject"を省略しました。これは良いことかどうかは分かりませんが、DLLプロジェクトのデフォルト設定とは異なります。

上記のリンクされたドキュメントから、すべての「パブリック関数とメンバー変数」がインポートに使用できると考えられました。これをC#でどのように達成できますか?単純にクラスをインスタンス化できますか?

編集:投稿のタイトルが誤解を招く可能性があることに気付きました。 C#からDllをインポートし、C++でドキュメントを正しく守っていることを確認することが重要です。

+1

DLL COMを表示させる必要があります。もっと長い説明をする時間がないので、コメントとして投稿します。 –

答えて

13

C#ではC++クラス(実質的に名前を変更したCインターフェイス)を直接インポートすることはできません。

オプションはCOMを介してクラスを公開し、C++/CLIを使用してマネージラッパーを作成するか、Cスタイルのインターフェイスを公開します。これは最も簡単なタイプの安全性を提供するので、私はマネージラッパーをお勧めします。

次のようなものになります

A C形式のインターフェイス(警告:未テストコード):

extern "C" __declspec(dllexport) 
void* CExampleExport_New(int param1, double param2) 
{ 
    return new CExampleExport(param1, param2); 
} 

extern "C" __declspec(dllexport) 
int CExampleExport_ReadValue(void* this, int param) 
{ 
    return ((CExampleExport*)this)->ReadValue(param) 
} 

A C++/CLIスタイルのラッパーは次のようになります(警告:未テストコード):

ref class ExampleExport 
{ 
private: 
    CExampleExport* impl; 
public: 
    ExampleExport(int param1, double param2) 
    { 
     impl = new CExampleExport(param1, param2); 
    } 

    int ReadValue(int param) 
    { 
     return impl->ReadValue(param); 
    } 

    ~ExampleExport() 
    { 
     delete impl; 
    } 
}; 
+0

'void *'から 'dynamic_cast'を実行することはできません。実際のポインタ型を使用することもできますが、C#はp/invokeシグネチャで 'IntPtr'を使用します。 –

+0

@Benあなたが正しいです。私は '(T *)'キャストからそれを校正する時に変更しました...今戻ってきました。実際のポインタ型を使用することもできますが、このアプローチでは安全性の欠如を強調したいと思います。 – Zooba

+0

C++のテンプレートスタイルのキャストが好きな人は、 'static_cast'を使います。 –

4

C#からpinvokeを使用してC++クラスインスタンスを作成することはできません。これは面倒な実装の詳細です.C++コンパイラは、どれだけのメモリを割り当てる必要があり、コンストラクタとデストラクタを適切に呼び出すタイミングと方法を知っています。オブジェクトのサイズはクラックするのが一番難しいですが、それを信頼できるものにする方法はありません。

C++クラスを静的メソッドにフラット化できない場合は、マネージラッパーを記述する必要があります。これはC++/CLI言語で行われ、アンマネージドクラスオブジェクトをポインタとして格納し、コンストラクタで作成し、デストラクタとファイナライザで削除した「refクラス」を作成します。

9

私の知る限り、C#はCOMインターフェイスとのみ相互運用できます。幸いなことに、レジストリを持つ完全なCOMオブジェクトである必要はありません。これは、IUnknownを実装する普通の古いC++クラスです。あなたは限り、あなたはこのようにしたいほとんど何でも行うことができます

[ComImport] 
[Guid("EFBF7D84-3EFE-41E0-952E-68A44A3E72CA")] 
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
interface MyInterface 
{ 
    [PreserveSig] double GetValue(); 
    void ThrowError(); 
} 

class Program 
{ 
    [DllImport("mylib.dll")] 
    static extern MyInterface CreateInstance(); 

    static void Main(string[] args) 
    { 
     MyInterface iface = CreateInstance(); 
     Console.WriteLine(iface.GetValue()); 
     try { iface.ThrowError(); } 
     catch(Exception ex) { Console.WriteLine(ex); } 
     Console.ReadKey(true); 
    } 
} 

:あなたはこのような何かを

#include <Windows.h> 

// Generate with from VisualStudio Tools/Create Guid menu 
static const GUID IID_MyInterface = 
{ 0xefbf7d84, 0x3efe, 0x41e0, { 0x95, 0x2e, 0x68, 0xa4, 0x4a, 0x3e, 0x72, 0xca } }; 

struct MyInterface: public IUnknown 
{ 
    // add your own functions here 
    // they should be virtual and __stdcall 
    STDMETHOD_(double, GetValue)() = 0; 
    STDMETHOD(ThrowError)() = 0; 
}; 

class MyClass: public MyInterface 
{ 
    volatile long refcount_; 

public: 
    MyClass(): refcount_(1) { } 

    STDMETHODIMP QueryInterface(REFIID guid, void **pObj) { 
     if(pObj == NULL) { 
      return E_POINTER; 
     } else if(guid == IID_IUnknown) { 
      *pObj = this; 
      AddRef(); 
      return S_OK; 
     } else if(guid == IID_MyInterface) { 
      *pObj = this; 
      AddRef(); 
      return S_OK; 
     } else { 
      // always set [out] parameter 
      *pObj = NULL; 
      return E_NOINTERFACE; 
     } 
    } 

    STDMETHODIMP_(ULONG) AddRef() { 
     return InterlockedIncrement(&refcount_); 
    } 

    STDMETHODIMP_(ULONG) Release() { 
     ULONG result = InterlockedDecrement(&refcount_); 
     if(result == 0) delete this; 
     return result; 
    } 

    STDMETHODIMP_(DOUBLE) GetValue() { 
     return 42.0; 
    } 

    STDMETHODIMP ThrowError() { 
     return E_FAIL; 
    } 
}; 

extern "C" __declspec(dllexport) LPUNKNOWN WINAPI CreateInstance() 
{ 
    return new MyClass(); 
} 

とC#側:

だから、C++でのこのような何かを行いますC++とC#間の通信が仮想インターフェイスを経由するためです。

0

実際には、DllImport属性のEntryPointプロパティを使用して、変更された名前を直接参照することができます。詳細はthis answerを参照してください。

0

C#とC++はC++やDelphiのようにABI互換ではないため、仮想クラスメンバー(メソッド)をエクスポートしたり呼び出し側で純粋に仮想宣言することはできません。 私はあなたのC++クラスをCOMでラップすることをお勧めします。そうすれば、COM互換性のある他の言語もあなたのクラスを使うことができます。

関連する問題