读取内存内存映射文件 C++ 和 C#

Posted

技术标签:

【中文标题】读取内存内存映射文件 C++ 和 C#【英文标题】:Read Memory Memory Mapped File C++ and C# 【发布时间】:2020-11-06 07:06:37 【问题描述】:

我正在尝试使用内存映射文件共享从 C++ 到 C# 的结构。到目前为止,我设法在文件上写入,但我无法读取 C# 中的内容。

    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;
 
    尝试用 C# 读取
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);
                    
                
            
        
    
 

当结构中存在要共享的字符串时,出现以下错误: 指定的 Type 必须是不包含引用的结构。

当我删除字符串并仅共享 int 方向时,我得到的值为 0。有人可以帮我解决这个问题吗?

【问题讨论】:

简单地说,C++ std::string 不是 C# string。由于std::string 是成员,因此您传递的struct 不可轻易复制。此外,sizeof(Bus_1553) 并没有按照您的想法去做。如果std::string 有一千个字符,sizeof(Bus_1553) 不会改变,因为sizeof 是编译时值,而不是运行时值。 您尝试解决的一般问题称为序列化(或者有时可能是编组)。基本上,您必须设计某种通用数据格式。也许这可能只是确保 Bus_1553 是一个标准布局结构,然后您可以在 C# 端使用 Marshal.PtrToStructure @PaulMcKenzie 如果您有答案,请将其放在答案部分。 (这次选对了保罗!) 对,就像在下面的 cmets 中一样,我移到了 char name[128] 和 C# 中的相关编组字符串 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string name;。但是我仍然得到The specified Type must be a struct containing no references. Parameter name: type 【参考方案1】:

让我们从 C++ 版本的问题开始。我会加粗以确保没有人会忽略这一点,这非常重要:永远不要将指针写入磁盘

std::string 是一个指针(实际上是 2 个指针)的包装器,可根据需要为您处理分配和重新分配。您绝对不能将它们写入任何地方的“文件”,而必须写入这些指针的内容。

执行此操作的一种简单方法(在 C 中很流行)是简单地定义一个足够大的缓冲区来保存您的数据,然后根据需要使用尽可能多的缓冲区:

struct Bus_1553 // this is the structure to send

    char name[128];
    int directions; 
;

要写信给name,请使用strcpy_s 或您的操作系统等效项。

现在,一旦您在 C++ 中将此结构写入共享文件,在 C# 中读取它就是让系统(编组器)将字节汤解码为有用的托管对象。您可以通过在结构和字段定义上使用属性来做到这一点:

    [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
    

如果你正确使用编组器,你也不需要unsafe

【讨论】:

要明确... 永远不要向磁盘写入指针 不是粗体大写字母,因为它会使您的计算机爆炸或发生其他事情。它用粗体大写字母表示,因为它不起作用,Blindy 知道这一点,她/他感到很沮丧。 它会让你的程序在你以后阅读它并尝试取消引用它时爆炸。我不确定你想在这里表达什么观点。 我认为这种格式还不够糟糕。 永远不要使用GETS来读取来自网络的输入是可以的,但是将它用于this似乎过度了。 请注意,您将指针写入磁盘,其他人(包括修复此问题后的 OP)将处理编写工作软件。 我试图去寻找 marshal 你有没有建议,但我仍然收到以下错误 The specified Type must be a struct containing no references. Parameter name: type 。现在,当代码中没有字符或字符串时,我实际上可以共享数字,但是当我在 C# 中设置 char name[128] 和相对编组字符串时会出现错误。我还把名字写成了`strcpy_s(p_1553->name, "IFF");`

以上是关于读取内存内存映射文件 C++ 和 C#的主要内容,如果未能解决你的问题,请参考以下文章

C#大文件读取和查询--内存映射

C#大文件读取和查询--内存映射

C++:从内存映射文件中读取/获取数据

使用 Protobuf 和内存映射文件 C# 的 IPC

在 C# 中使用内存映射文件时是不是可以避免数据副本?

内存映射文件 VS 命名管道 - C#