2012-02-15 1 views
1

私は熟練したソフトウェアエンジニアですが、C#ではいくぶん新しくなっています。私は示されているようにCインターフェイスを持つC++オブジェクトで構成されたDLLを持っています。私はC#アプリケーションからDLL内のC++エントリポイントにアクセスする方法があるかどうかわからなかったので、このCインターフェイスを使用しました。DLLとの間でオブジェクトポインタを渡す方法は?

__declspec(dllexport) void *Constructor(); 
    __declspec(dllexport) void Destructor(void *pObj); 
    __declspec(dllexport) int Method(void *pObj, char *pSrcDst, int len); 

これは上記のインターフェイスのCコードです。

extern "C" void *Constructor() 
{ 
    return (void *)new Object; 
} 
extern "C" void Destructor(void *pObj) 
{ 
    delete (Object *)pObj; 
} 
extern "C" int Method(void *pObj, char *pSrcDst, int len) 
{ 
    if (!pObj) return 0; 
    return ((Object *)pObj)->Method(pSrcDst, len); 
} 

これまでのところとても良いです。今、私はC#アプリケーションからこのインタフェースにアクセスできる必要があります。私が学んだことから、私は次のC#インターフェイスを実装しました。

unsafe public class Wrapper 
    { 
     [SuppressUnmanagedCodeSecurityAttribute()] 
     [DllImport("MyDLL.dll")] 
     public static extern 
     IntPtr Constructor(); 
     [SuppressUnmanagedCodeSecurityAttribute()] 
     [DllImport("MyDLL.dll")] 
     public static extern 
     void Destructor(IntPtr pObj); 
     [SuppressUnmanagedCodeSecurityAttribute()] 
     [DllImport("MyDLL.dll")] 
     public static extern 
     int Method(IntPtr pObj, [In,Out] byte[] pSrcDst, int len); 

ここでは、上記のインターフェイスを使用するC#コードを示します。

private IntPtr pObject; 
    public object() 
    { 
    pObject = Wrapper.Constructor(); 
    } 
    ~object() 
    { 
    Wrapper.Destructor(pObject); 
    } 
    public override byte[] method() 
    { 
    byte[] bytes = new byte[100]; 
    int converted = Wrapper.Method(pObject, bytes, bytes.length); 
    return bytes; 
    } 

コードは、Wrapper.MethodとWrapper.Destructorの両方の呼び出しでアサートされます。私は、オブジェクトへのポインタを適切に処理していないと仮定します。助言がありますか?

+0

「コードが主張する」とはどういう意味ですか?エラーメッセージが表示されますか? – svick

+0

はい、 'PInvokeStackImbalanceが検出されました'。 –

+0

ほとんどのコンパイラでは、 'extern" C "関数は例外をスローしないと考えています(特にCコードがそれらを呼び出す場合)。したがって、' new Object() 'ビットを必ず例外(特に 'std :: bad_alloc')がエスケープされないようにするための例外ブロックです。 –

答えて

3

最も可能性の高い問題は、コードが正しいcalling conventionを使用していないことです。 C++ではデフォルトは__cdeclですが、DllImportではデフォルトは__stdcallです。この問題を解決するには

specify the calling convention explicitly

[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)] 
+0

OPがわからない場合は、Visual Studioのデフォルトの呼び出し規約は[コンパイラ設定](http://)です。 msdn.microsoft.com/en-us/library/46t77ak2.aspx)。デフォルトの '[DllImport()]'の規則に合うように '__stdcall'に変更することができます。 –

+0

これは問題だったようですね、ありがとう! –

+1

@ RobertKirnum、この回答があなたの問題を解決した場合は、[同意する]必要があります(http://meta.stackexchange.com/a/5235/130186)。 – svick

1

私はこの種のものを行うための最善の方法は、マネージC++/CLIのラッパーを使用していたという印象の下にありました。

Creating simple c++.net wrapper. Step-by-step

http://devmaster.net/forums/topic/7357-how-to-build-a-net-wrapper-for-a-c-library/

は有望なリード線のように見えます。

+1

管理されていない関数をほとんど呼び出す必要がない場合は、おそらくPInvokeが簡単です。しかし、ええ、C + +/CLIは確かに選択肢です。 – svick

+0

真実ですが、管理されていない機能がほとんど存在しない唯一の理由は、C++/CLIに関する知識が不足していることだけです。それを "使用しない理由"が正しいとする方法がある場合。本当にむしろ "void * Constructor"という関数を使用しますか? – Andrei

+0

C#コードの他の部分に公開されていない限り、私はそれに問題はありません。あなたがC#を知っているなら、PInvokeを使うことを学ぶことはC++/CLIを使うことを学ぶことよりもずっと簡単です。 – svick

関連する問題