将C++结构封送到 C# 类时的访问违规异常

本文关键字:访问 异常 结构 C++ | 更新日期: 2023-09-27 18:32:54

我正在尝试使用 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 教程所建议的那样,我创建了一个 C# 类来将 CXString 结构封送到其中,它看起来像这样:

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

当代码到达调用时,我会收到访问违规异常FormatDiagnostic。任何可能出错的提示?

编辑:

CXDiagnostic 是原始C++代码中的指针类型:

typedef void *CXDiagnostic;

将C++结构封送到 C# 类时的访问违规异常

我认为编

组到 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(和结构字节对齐。