2

PInvoke を使用して、C++ ライブラリ (libclang) を C# ラッパーにラップしようとしています。

構造体を返す C++ メソッドを呼び出そうとするまでは、すべてがピカピカでした。すべて本通りにやったのですが、このメソッドが呼び出されると AccessViolationException が発生します

今読んだところ、オブジェクトの記憶イメージに何か問題があるためだろうと読みました。すべてのアーティビュートを配置したかどうか、どこにもないものをチェックして再チェックしましたが、例外は消えません。(私はこのコードを何時間も見てきたので、いくつか見落としているかもしれませんが、皆さんはそうではありません)。

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 テイスト (グローバル) で実装されているため、私の C# クラスでは OO の印象を与えることができますが、実際には C++ オブジェクトの IntPtr 表現を格納しています ( _nativeObject)。したがって、に格納されているオブジェクト_nativeObjectは実際には CXDiagnostic であると確信しています (同じライブラリの別の関数から参照が返されました)。

FormatDiagnostic メソッドで使用しようとしている実際のオブジェクトは、別のラッパー クラス (TranslationUnit) のコンストラクターから初期化されます。

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

したがって、MSDN チュートリアルが示唆するように、CXString 構造体をマーシャリングする C# クラスを作成しました。これは次のようになります。

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

コードが呼び出しに到達すると、AccessViolationException が発生しFormatDiagnosticます。うまくいかなかった可能性のあるヒントはありますか?

編集:

CXDiagnostic は、元の C++ コードのポインター型です。

typedef void *CXDiagnostic;
4

1 に答える 1

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 を使用します) と構造体のバイト アラインメントにも注意する必要があります。

于 2012-04-30T17:16:51.187 に答える