我正在尝试使用PInvoke将C++库(libclang)包装到C#包装器中。
一切都很光鲜,直到我尝试调用返回结构的C++方法。
我做了一切by the book,但是当调用此方法时,我得到了AccessViolationException。
现在,我读到它可能是因为对象的内存镜像有些困惑。我检查并仔细检查了是否将所有的artrbuts和所有东西都放到了哪里,但是Exception不会消失。 (我已经看了几个小时的代码了,所以我可能错过了一些东西,你们没有)。
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
调用时,我得到AccessViolationException。有什么提示可能会出错吗?
编辑:
CXDiagnostic是原始C++代码中的指针类型:
typedef void *CXDiagnostic;
最佳答案
我认为在这里编码到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)和结构字节对齐。
关于c# - 将C++结构编码为C#类时出现AccessViolationException,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10386139/