14

クラス "myCppClass" を含む C++ Dll プロジェクトを作成し、http://msdn.microsoft.com/en-us/library/a90k134d(v=vs . 80).aspx

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

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

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

編集:投稿のタイトルが誤解を招く可能性があることに気付きました. C# からの DllImport と、C++ のドキュメントに適切に従っていることを確認することに重点を置く必要があります。

4

5 に答える 5

16

C# は、C++ クラスを直接インポートすることはできません (これは事実上、名前マングルされた C インターフェイスです)。

オプションは、COM 経由でクラスを公開するか、C++/CLI を使用してマネージ ラッパーを作成するか、C スタイル インターフェイスを公開することです。マネージ ラッパーをお勧めします。これが最も簡単で、最高のタイプ セーフが得られるからです。

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)
}

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;
    }
};
于 2011-01-19T21:55:18.287 に答える
11

私の知る限り、C# は COM インターフェイスとしか相互運用できません。幸いなことに、レジストリを備えた本格的な COM オブジェクトである必要はありません。IUnknown を実装する単純な古い C++ クラスであれば何でもかまいません。

したがって、C++ で次のようにします。

#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# 側では、次のようにします。

[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);
    }
}

C++ と C# の間の通信が仮想インターフェイスを介して行われる限り、この方法でほとんど何でも行うことができます。

于 2011-01-19T22:23:57.220 に答える
5

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

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

于 2011-01-19T21:55:30.647 に答える
0

C# と C++ は C++ や Delphi のように ABI 互換ではありません。そのため、仮想クラス メンバー (メソッド) をエクスポートし、呼び出し側でそれらを純粋に仮想として宣言して呼び出すことはできません。これは、C# が C++ オブジェクトの vtbl を処理できないためです。C++ クラスを COM でラップすることをお勧めします。これにより、他の COM 互換言語でもクラスを使用できるという別の肯定的な副作用が得られます。

于 2016-07-09T15:09:22.653 に答える