I am trying to share a structure coming from C++ to C# using memory mapped file. So far I managed to write on the file, but I am unable to read the content in C#.
struct Bus_1553 // this is the structure to send
{
string name;
int directions;
};
struct Bus_1553* p_1553; // set the pointer to it
HANDLE handle; // create the handle
// here we define the data to send
string name = "IFF";
int directions = 3;
bool startShare() // Open the shared memory
{
try
{
handle = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(Bus_1553), L"DataSend");
p_1553 = (struct Bus_1553*) MapViewOfFile(handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, sizeof(Bus_1553));
return true;
}
catch (...)
{
return false;
}
}
int main()
{
if (startShare() == true)
{
while (true)
{
if (p_1553 != 0) // populate the memory
{
p_1553->name = name;
p_1553->directions = directions;
}
else
puts("create shared memory error");
}
}
if (handle != NULL)
CloseHandle(handle);
return 0;
}
namespace sharedMemoryGET
{
class sharedMemoryGET
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct Bus_Data_1553
{
public string name;
public int directions; // which directions used
}
public static MemoryMappedFile mmf;
public static MemoryMappedViewStream mmfvs;
static public bool MemOpen() // open the mapped file
{
try
{
mmf = MemoryMappedFile.OpenExisting("DataSend");
return true;
}
catch
{
return false;
}
}
public static void readData()
{
if (MemOpen())
{
using (var accessor = mmf.CreateViewAccessor())
{
accessor.Read(0, out Bus_Data_1553 a);
Console.WriteLine(a.name);
Console.WriteLine(a.directions);
}
}
}
}
}
When a string is present in the structure to share, I have the following error: The specified Type must be a struct containing no references.
When I remove the string and share only the int directions, i get a value of 0. Can someone help me figure this out?
Let's start with what's wrong with the C++ version. I'll bold this to make sure nobody ever passes over this, it's very important: NEVER WRITE POINTERS TO DISK
std::string
is a wrapper around a pointer (2 pointers actually) that handle allocation and reallocation for you as needed. You absolutely cannot write them to a "file" anywhere, you must write the contents of those pointers instead.
One simplistic way (and prevalent in C) to do this is to simply define a buffer large enough to hold your data and then use as much of it as needed:
struct Bus_1553 // this is the structure to send
{
char name[128];
int directions;
};
To write to name
, use strcpy_s
or your OS equivalent.
Now once you write this structure in C++ to your shared file, reading it in C# is about letting the system (the marshaller) decode that soup of bytes into useful managed objects. You do this by using attributes on your structure and field definition:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct Bus_Data_1553
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string name;
public int directions; // which directions used
}
Also you don't need unsafe
for this if you use the marshaller properly.