2012-04-30 25 views
2

私は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; 
+0

(1)このメソッドは、CXDiagnosticを使用して型指定されます。あなたはIntPtrを渡しています。 CXDiagnosticは最初のポインタ型ですか? (2)ここに表示されているコードは、渡すIntPtrを初期化するため、nullになります。アンマネージコードの逆参照はnullですか? –

+0

(1)はい、typedefは次のようになります:typedef void * CXDiagnostic; (2)外部コードから初期化され、デバッガに値があり、0ではありません。 私は1分で投稿を更新する予定だので、それも表示されます。 – Tenshiko

+0

(3)LPStructとしてマークするのはなぜですか?この記事では、LPStructの唯一の有効な使用法は、GUIDをマーシャリングするときです:http://blogs.msdn.com/b/adam_nathan/archive/2003/04/23/56635.aspx - これはGUIDのみで動作するように設計されています。*ポインタ*をCXStringに返すわけではありません。* CXString *を返すので、なぜポインタ間接指定としてマーシャリングする必要がありますか? –

答えて

2

私はLPStructに整列化することは、ここで適切でないとCXStringクラスが構造することが必要だと思います。

は、次のコードを試してみてください。また

public class Diagnostic 
{ 
    ... 
    [DllImport("libclang.dll", EntryPoint = "clang_formatDiagnostic")] 
    private static extern CXString FormatDiagnostic(IntPtr diag, uint options); 
    ... 
} 

[StructLayout(LayoutKind.Sequential)] 
public struct CXString 
{ 
    public IntPtr data; 
    public uint private_flags; 
} 

を、あなたは(デフォルトではSTDCALLはなく、例えば、純粋なCは、CDECLを使用)呼び出し規約の世話をする必要があり、構造体のバイト整列。

関連する問題