My code is raising an exception. I am using P/Invoke to call some C API from a .NET project.
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
It's a void function which takes as input a struct of primitives. I have several working API methods with several different structs (much more "complicated") that are working.
Setup:
- VS 17 2022
- C++ 20
- .NET Framework 4.7.2
It happens both in Debug and Release mode.
C/C++ side:
typedef struct
{
float visibility; //[m] Visibility range
float thickness; //[m] Thickness of the fog layer
// int placeholder;
} Fog;
/// @brief set fog data to be forwarded to the IG
/// @param fog \ref Fuze::Interfaces::Fog with fog data
/// @param regionId the id of the region to be updated
extern "C" __declspec(dllexport) void SetFogData(const Fog* fog, int regionId);
///interface.cpp
#include "Interface.h"
#include <iostream>
void SetFogData(const Fog* fog, int regionId)
{
std::cout << "\n\nForwarding fog data to plugins\n";
std::cout << "Fog data: " << fog->thickness << " " << fog->visibility << std::endl;
}
C# side:
using System.Runtime.InteropServices;
namespace Fuze
{
[StructLayout(LayoutKind.Sequential)]
public struct Fog
{
public float Visibility; // [m] Visibility range
public float Thickness; // [m] Thickness of the fog
// int placeholder;
}
static public class API
{
private const string dllPath = "Fuze_API.dll";
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
public static extern void SetFogData(Fog fog, int regionId);
}
}
// main.cs
using static Fuze.API;
class FakeClientDotNet
{
static void Main(string[] args)
{
Fuze.Fog fog = new Fuze.Fog();
fog.Thickness = 50.0f;
fog.Visibility = 250.0f;
SetFogData(fog, 0);
System.Console.WriteLine("\nCompleted\n");
}
}
This code generates the error. If I uncomment the int placeholder field (in both structs), it starts working and the data provided via P/Invoke is correct. I have the same behavior with a struct made of an enum (with the int specifier) and an int, in which I have to add a third int member to not get the exception in the title.
Your C function takes a
Fog*(so, a pointer):But your P/Invoke declaration takes a
Fogby value:That's a struct, and it's cdecl, so it gets copied on the stack. And then the native side tries to interpret that
fogobject as a pointer and stuff goes wrong. Or at least that's a guess, in reality it's our good old friend undefined behaviour.If you want to pass a pointer, pass a reference and let the marshaller turn that into a pointer:
And then, later: