このC ++関数を呼び出す方法は?

私は以下の機能を持っています:

bool __declspec(dllexport) COMS_HL7QueryAnswer(char *szZone,char* szMessage, char** o_szAnswer)

そして私はこれをC#からPInvokingしています:

public static extern bool COMS_HL7QueryAnswer(string szZone,string szMessage, out StringBuilder szAnswer);

Windows 2003で動作していますが、W2008でアクセス違反の例外が発生していて、PInvokeの境界内で発生しているようです。どんな助けも素晴らしいでしょう。

ありがとう。

EDIT: Looks like the AccessViolationException happens in the PInvoke boundary because:

  1. C#以外のコールスタックはありません。
  2. デバッガを使用すると、最後のC ++関数までF10できます。終了すると、C#例外ハンドラに移動します。
0
@Ramhound:私の編集を参照してください。ありがとう。
追加された 著者 Ignacio Soler Garcia,
@ Ramhound:あなたはもっと深くしてくださいことを説明することはできますか?
追加された 著者 Ignacio Soler Garcia,
境界内にあるように見えるという陳述を説明してください。追加情報が正確にどのように失敗したかは参考になります。
追加された 著者 Security Hound,
あなたの署名が有効でないという事実を除いて、あなたの呼び出し規約も一致しません。それを解決したいかもしれません。
追加された 著者 Security Hound,

2 答え

あなたが使用する必要があります

[DllImport(CallingConvention = CallingConvention.Cdecl)]
public static extern bool COMS_HL7QueryAnswer(string szZone,string szMessage, out IntPtr szAnswer);

p/invokeは、正しい方法を知らないので、データを取得した後にポインタを保持し、正しい解放機能に渡す必要があります。

C ++側を変更することが許されている場合、これをp/invoke-friendlyにするためにできることはたくさんあります。または、C ++/CLIレイヤーを使用することもできます。

3
追加された
StringBuilderをGCしたときにCLRが* szAnswerを解放しないのはなぜですか?まあ、多分* szAnswerは何かグローバルなのでしょうか?
追加された 著者 Ignacio Soler Garcia,
素晴らしいですが、今は動作しますが、データを解放する方法はわかりません。これは、新しいC ++で作成されています。私がHoulobalのように理解できないことについてマーシャルの方法で話す...
追加された 著者 Ignacio Soler Garcia,
はい、申し訳ありませんが、私は新しいことを意味しました。
追加された 著者 Ignacio Soler Garcia,
@ SoMoS: * o_szAnswer は、C ++コード内のいくつかのバッファのアドレスに割り当てられました。 CLRはそのバッファから StringBuilder に読み込むことができますが、バッファを解放する方法はわかりません。だからそれを解放しようとするか、間違った割り当て解除関数を推測するか、アクセス違反が発生するか、何もせずにメモリリークが発生します。
追加された 著者 Ben Voigt,
@ SoMoS:Cスタイルの文字列である * o_szAnswer を意味していました。申し訳ありませんが、それはあなたに混乱を引き起こした場合。 StringBuilder オブジェクトはガベージコレクトされますが、データはC ++を離れるときに StringBuilder にありません。
追加された 著者 Ben Voigt,
@SoMoS:C ++ DLLから解放された別の関数を解放する必要があります。そして確かにそれは new [] (配列バージョン)で割り当てられますか?
追加された 著者 Ben Voigt,
@Ramhound:私たちは、コンパイラのオプションを見ずに、C ++コードが何を使用しているかについてはわかりません。しかし、あなたはおそらく、不一致があるのは間違いありません。それに対処する更新された答え。
追加された 著者 Ben Voigt,
CallingConventionがC ++コードと一致するように設定されている場合にも役立ちます。
追加された 著者 Security Hound,

あなたのコードはメモリをリークしています。はい、W2003はピンボケ・マーシャラーが文字列を解放しようとしたことを静かに無視します。 W2008ははるかに厳しいメモリマネージャーを持ち、それに気を配りませんが、アクセス違反を引き起こします。 IntPtrとMarshal.PtrToStringAnsi()を使用してメモリをリークし続けることができます。 Cコードを修正することができれば、CoTaskMemAlloc()で文字列バッファを割り当てても問題は解決しますが、pinvoke marshallerはCoTaskMemFree()を使用します。

本当の修正は、char **の代わりにchar *です。そのため、呼び出し側は、文字列でいっぱいになるバッファを渡すことができます。 C#から out のないStringBuilder。容量を与える追加の引数を使用すると、文字列が収まらないときに偶然に収集されたガーベージ・ヒープを破損させません。

1
追加された
out IntPtr では、彼のコードには、割り当て解除関数に渡す正しいポインタがあります。
追加された 著者 Ben Voigt,