我的代码引发了
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
异常。我正在使用 P/Invoke 从 .NET 项目调用一些 C API。
这是一个 void 函数,它接受一个基元结构的输入。我有几种有效的 API 方法,其中有几种不同的结构(更“复杂”)正在工作。
设置: VS 17 2022 C++20 .NET 框架 4.7.2
它在调试和发布模式下都会发生。
C/C++ 方面
typedef struct
{
float visibility; //[m] Visibility range
float thickness; //[m] Thickness of the fog layer
// int placeholder;
} Fog;
extern "C" __declspec(dllexport) void SetFogData(const Fog* fog, int regionId);
C#方面
[StructLayout(LayoutKind.Sequential)]
public struct Fog
{
public float Visibility; // [m] Visibility range
public float Thickness; // [m] Thickness of the fog
// int placeholder;
}
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
public static extern void SetFogData(Fog fog, int regionId);
// main.cs
Fog fog = new Fog();
fog.Thickness = 50.0f;
fog.Visibility = 250.0f;
SetFogData(fog, 0);
此代码会生成错误。如果我取消注释
int placeholder
字段(在两个结构 obv 中),它就会开始工作,并且通过 P/Invoke 提供的数据是正确的。我对由 enum
(带有 int 说明符)和 int
组成的结构有相同的行为,其中我必须添加第三个 int
成员才能在标题中得到异常。
你的 C 函数需要一个
Fog*
(所以,一个指针):
extern "C" __declspec(dllexport) void SetFogData(const Fog* fog, int regionId);
但是您的 P/Invoke 声明按值采用
Fog
:
public static extern void SetFogData(Fog fog, int regionId);
这是一个结构体,它是 cdecl,所以它会被复制到堆栈上。然后本机端尝试将
fog
对象解释为指针,结果出了问题。或者至少这是一个猜测,实际上这是我们的好老朋友未定义的行为。
如果您想传递指针,请传递引用并让编组器将其转换为指针:
public static extern void SetFogData(ref Fog fog, int regionId);
然后,稍后:
SetFogData(ref fog, 0);