'C# / C++ Interop: How to modify class in C++ memory from C#
I'm including a C# DLL in my C++ project, and I want both to be able to edit the same struct or class in memory easily I could use functions to set/get the members or struct, but I think this would have a lot more overhead and be cumbersome. I'd rather be able to edit the member variables directly, mapping the memory of the C# side to match the C++ side.
I'm using Mono to manage the C# from C++ but I'm not sure this is important.
This is my attempt at doing so, but any changes in C# only affect the C# struct. How can I fix this?
C++ (Unmanaged) Code:
#ifdef ENGINE_CORE
#define ENGINE_CORE_API __declspec(dllexport)
#else
#define ENGINE_CORE_API __declspec(dllimport)
#endif
struct TestStruct {
int myElement = 4;
};
TestStruct testStruct;
extern "C" {
ENGINE_CORE_API void GetTestStruct(TestStruct** transf) {
*transf = &testStruct;
}
}
C# (Managed) Code:
[StructLayout(LayoutKind.Sequential)]
class TestStruct {
public int myElement;
};
public class Example {
// Called in a loop:
public void OnUpdate() {
GetTestStruct(out TestStruct testStruct);
testStruct.myElement += 2;
Logger.Print($"Int: {testStruct.myElement}");
}
#region DllImports
[DllImport("EngineCore")]
static extern void GetTestStruct([Out] out TestStruct comp);
#endregion
}
Output:
[2022-02-26 00:02:54.959] [Debug Logger] [info] Int: 6
[2022-02-26 00:02:55.220] [Debug Logger] [info] Int: 6
[2022-02-26 00:02:55.234] [Debug Logger] [info] Int: 6
Expected Output:
[2022-02-26 00:02:54.959] [Debug Logger] [info] Int: 6
[2022-02-26 00:02:55.220] [Debug Logger] [info] Int: 8
[2022-02-26 00:02:55.234] [Debug Logger] [info] Int: 10
Solution 1:[1]
From C# side, you can bring all the relative memory address offsets of all fields of object instance into C++ and access them by using the offsets added to their object adresses and cast to their POD types.
Then your C++ class would be made of only pointers directly pointing to the C# object fields.
struct TestStruct {
// points to relevant field
int* myElement;
};
TestStruct t({ptr_from_csharp});
*t.myElement=5; // pinned C# object's field changes
If you need the opposite, then C++-side doesn't require the extra pinning operation since it doesn't have GC.
If you don't want to fiddle with pointer type fields, then you can overload getter/setter of fields on C# and run the C++ DLL in those getter/setter methods so that it looks like C# owns the memory from outside but actually only changes the C++-space memory.
class TestStruct {
public int myElement {
get { return cpp("C++");}
set { cpp("change C++");}
}
};
But this would bring interop overhead.
Solution 2:[2]
I would write some kind of awk or perl script that reads the C# class and spits out the required C++ struct.
To this end, it would be helpful to:
annotate or bracket the C# class(es) with comments that the script recognises and uses to identify said class(es)
move the C++ struct(s) to a separate header file
Then, when a C# class changes, just run the script on the relevant file(s) and recompile your C++ code.
Don't forget to have the script write out a comment the the effect that the output file is machine-generated so that you don't inadvertently hand-edit it.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Paul Sanders |
