私はPInvokeを使用してC++ラッパー(libclang)をC#ラッパーにラップしようとしています。C#クラスにC++構造体をマーシャリングするときのAccessViolationException
構造体を返すC++メソッドを呼び出そうとするまで、すべてが輝いていました。 私はすべてby the bookを実行しましたが、このメソッドが呼び出されると、私はAccessViolationExceptionを取得します。
ここでは、オブジェクトのメモリイメージに何かが混乱している可能性があることを読んでいます。私はチェックして、すべてのarrtibutesとどこに何を入れているが、例外が消えない場合はdoublechecked。 (私はこのコードを何時間も見てきたので、いくつか見逃しているかもしれませんが、皆さんはそうしません)。
C++部分(ない私のコードが、私はそれが動作することを確信している):
CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) {
/* Parse the hell out of a lot of data, an then make a string
In the end, string gets wrapped in a custom struct: CXString.
*/
return createCXString(Out.str(), true);
}
ここCXStringです:
typedef struct {
void *data;
unsigned private_flags;
} CXString;
だから私はラップCを表現するために、私のC#クラスを持っ++オリジナル:
public class Diagnostic
{
private IntPtr _nativeObject;
internal IntPtr NativeObject { get { return _nativeObject; } }
[DllImport("libclang.dll", EntryPoint = "clang_formatDiagnostic")]
[return: MarshalAs(UnmanagedType.LPStruct)]
private static extern CXString FormatDiagnostic(IntPtr diag, uint options);
public Diagnostic(IntPtr native)
{
_nativeObject = native;
}
public string GetString(DiagnosticDisplayOptions options = DiagnosticDisplayOptions.DisplaySourceLocation)
{
var cxstr = FormatDiagnostic(_nativeObject, (uint)options); //<-- I get the exception here
return cxstr.GetString();
}
}
私が必要とする機能はCの味(グローバル)で実装されているため、OO iの印象を作ることができます実際にはC++オブジェクトのIntPtr表現(_nativeObject
)を格納しています。ですから、私はかなり確信しています、_nativeObject
に格納されているオブジェクトは、実際にはCXDiagnosticです(私は同じライブラリの別の関数から返された参照を持っています)。 MSDNのチュートリアルが提案されているよう
public TranslationUnit(/*lots of params to init the unit*/)
{
//... there are some int conversions and initialization failsafe codes here
//this is a property of TranslationUnit
Diagnostics = new List<Diagnostic>();
//GetDiagnosticsCount is also PInvoke to count CXDiagnostic objects related to the TranslationUnit
var dgCnt = (int)GetDiagnosticsCount(_nativeObject);
for (int i = 0; i < dgCnt; i++)
{
//GetDiagnostic is also PInvoke, gets the CXDiagnostic at the given index
var diag_ptr = GetDiagnostic(_nativeObject, (uint)i);
Diagnostics.Add(new Diagnostic(diag_ptr));
}
//up until now, all the calls seem to work
//I get the expected count of diagnostics and none of the other
//PInvoke calls throw exceptions. They use IntPtrs, but none of them
//use structs.
}
だから、私が作ったC#の:
私はFormatDiagnostic方法で使用しようとしている実際のオブジェクトは、別のラッパークラスのコンストラクタ(TranslationUnit)から初期化されますCXString構造体をマーシャリングするクラス、そして、それは次のようになります。
[StructLayout(LayoutKind.Sequential)]
public class CXString
{
public IntPtr data;
public uint private_flags;
}
コードがFormatDiagnostic
コールに達したときに、私はAccessViolationExceptionを取得します。 どこが間違っていた可能性がありますか?
EDIT:
CXDiagnosticは、元のC++コードでのポインタ型である:
typedef void *CXDiagnostic;
(1)このメソッドは、CXDiagnosticを使用して型指定されます。あなたはIntPtrを渡しています。 CXDiagnosticは最初のポインタ型ですか? (2)ここに表示されているコードは、渡すIntPtrを初期化するため、nullになります。アンマネージコードの逆参照はnullですか? –
(1)はい、typedefは次のようになります:typedef void * CXDiagnostic; (2)外部コードから初期化され、デバッガに値があり、0ではありません。 私は1分で投稿を更新する予定だので、それも表示されます。 – Tenshiko
(3)LPStructとしてマークするのはなぜですか?この記事では、LPStructの唯一の有効な使用法は、GUIDをマーシャリングするときです:http://blogs.msdn.com/b/adam_nathan/archive/2003/04/23/56635.aspx - これはGUIDのみで動作するように設計されています。*ポインタ*をCXStringに返すわけではありません。* CXString *を返すので、なぜポインタ間接指定としてマーシャリングする必要がありますか? –