【C#】共享内存通信的学习(传递结构体)

写在前面的话

最近项目涉及到了共享内存的通信,相互之间传递结构体中的数据,所以记录一下。本篇会介绍共享内存是什么,将数据封装成一个结构体,之后通过共享内存来进行进程中的通信,代码也会附在后面。也是借鉴了很多别的大佬写的,只是在这里就不一一贴出链接了。都能搜到。

一、共享内存的介绍

1.1共享内存是什么?

共享内存是进程间通信的一种方式,这里先插句话,我们用thread打开的是线程,一个程序是一个进程,但是一个程序可以开很多线程。所以共享内存是不同程序之间的互相通信。

共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。

特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。

在Windows操作系统下,任何一个进程不允许读取、写入或是修改另一个进程的数据(包括变量、对象和内存分配等),但是在某个进程内创建的文件映射对象的视图却能够为多个其他进程所映射,这些进程共享的是物理存储器的同一个页面。

因此,当一个进程将数据写入此共享文件映射对象的视图时,其他进程可以立即获取数据变更情况。

1.2 共享内存通信原理

【C#】共享内存通信的学习(传递结构体)_第1张图片
如上图所示:
每一段程序都有自己得控制块PCB,然后有自己地址空间Addr Space,然后有一个与之对应得页表。负责将进程得虚拟地址与物理地址进行映射,通过内存管理单元MMU进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。

1.3 共享内存在C#中代码

整了一个Class,我觉得行
简单来说共享内存从创建到回收都是以下几个主要函数

  • 创建共享内存——CreateFileMapping
  • 打开共享内存——OpenFileMapping
  • 共享内心映射——MapViewOfFile
  • 内存释放函数——UnmapViewOfFile
  • 呼叫线程最后返回的错误代码——GetLastError
  • 关闭线程句柄——CloseHandle
  • 将共享内存数据读到结构体——Marshal.PtrToStructure
  • 将结构体中的数据写入共享内存——Marshal.StructureToPtr
using System.Runtime.InteropServices;//非托管代码交互,能够把非托管代码转化为托管代码

class ShareMemory
{
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);
    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName);
    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr OpenFileMapping(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName);
    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr MapViewOfFile(IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);
    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool UnmapViewOfFile(IntPtr pvBaseAddress);
    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);
    [DllImport("kernel32", EntryPoint = "GetLastError")]
    public static extern int GetLastError();

    private const int INVALID_HANDLE_VALUE = -1;
    private const int ERROR_ALREADY_EXISTS = 0xB7;//183
    private const int PAGE_READWRITE = 0x04;
    private const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004;
    private const int FILE_MAP_READ = 0x0004;
    private const int FILE_MAP_WRITE = 0x0002;

    private IntPtr m_hSharedMemoryFile = IntPtr.Zero;
    private IntPtr m_rwData = IntPtr.Zero;
    private bool m_bAlreadyExist = false;
    private bool m_bInit = false;

    private string ShareName { get; set; } //共享内存的名字
    private long MemSize { get; set; }//共享内存大小
    private IntPtr MemPtr { get; set; }//共享内存印射地址


    /// 
    /// 共享内存初始化
    /// 
    /// 共享内存名字
    /// 共享内存大小
    public ShareMemory(string MemName, long Size)
    {
        ShareName = MemName;
        MemSize = Size;
        MemPtr = IntPtr.Zero;
        m_bInit = false;
        m_bAlreadyExist = false;
        m_rwData = IntPtr.Zero;
        m_hSharedMemoryFile = IntPtr.Zero;
    }

    ~ShareMemory()
    {
        ShareMemoryClose();
    }



    /// 
    /// 创建共享内存 
    /// 
    /// 0=创建成功;1=创建共享体失败;2=打开失败;3=印射失败; 4=共享内存命名错误
    public int CreateMemory()
    {
        if (MemSize <= 0 || MemSize > 0x00800000) MemSize = 0x00800000;
        if (ShareName.Length > 0)
        {
            //创建内存共享体(INVALID_HANDLE_VALUE)
            m_hSharedMemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, IntPtr.Zero, (uint)PAGE_READWRITE, 0, (uint)MemSize, ShareName);
            if (m_hSharedMemoryFile == IntPtr.Zero)
            {
                m_bAlreadyExist = false;
                m_bInit = false;
                MemPtr = IntPtr.Zero;
                return 1; //创建共享体失败
            }
            else
            {
                if (GetLastError() == ERROR_ALREADY_EXISTS) //已经创建
                {
                    m_bAlreadyExist = true;
                    CloseHandle(m_hSharedMemoryFile);
                    m_hSharedMemoryFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, ShareName);
                    if (m_hSharedMemoryFile == null)
                    {
                        MemPtr = IntPtr.Zero;
                        return 2;//打开共享内存失败
                    }
                }
                else                                         //新创建
                {
                    m_bAlreadyExist = false;
                }
            }
            //创建内存映射
            m_rwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_ALL_ACCESS, 0, 0, (uint)MemSize);
            if (m_rwData == IntPtr.Zero)
            {
                m_bInit = false;
                CloseHandle(m_hSharedMemoryFile);
                MemPtr = IntPtr.Zero;
                return 3; //创建内存映射失败
            }
            else
            {
                m_bInit = true;
                MemPtr = m_rwData;
            }
        }
        else
        {
            return 4; //参数错误     
        }
        return 0;     //创建成功
    }

    /// 
    /// 读取共享内存并返回到类
    /// 
    /// 读取的类型
    /// 读取的数据
    public Object ReadFromMemory(Type type)
    {
        if (IntPtr.Zero == MemPtr)
        {
            return null;
        }

        Object obj = Marshal.PtrToStructure(MemPtr, type);
        return obj;

    }

    /// 
    /// 读取类并写入共享内存
    /// 
    /// 需要读取的类(结构体)
    /// 返回0表示写入成功,返回-1表示写入失败
    public int WriteToMemory(Object obj)
    {
        if (IntPtr.Zero == MemPtr)
        {
            return -1;
        }

        Marshal.StructureToPtr(obj, MemPtr, false);

        return 0;

    }

    /// 
    /// 关闭共享内存(解除印射,关闭句柄)
    /// 
    public void ShareMemoryClose()
    {
        if (m_bInit)
        {
            UnmapViewOfFile(m_rwData);
            CloseHandle(m_hSharedMemoryFile);
            m_bInit = false;
        }
    }

    /// 
    /// 获取共享内存印射地址
    /// 
    /// 
    public IntPtr GetPtr()
    {
        return MemPtr;
    }
    /// 
    /// 获取文件句柄
    /// 
    /// 
    public IntPtr GetFileMapPtr()
    {
        return m_hSharedMemoryFile;
    }
    /// 
    /// 获取共享内存大小
    /// 
    /// 
    public long GetSize()
    {
        return MemSize;
    }
    /// 
    /// 获取共享内存名字
    /// 
    /// 
    public string GetName()
    {
        return ShareName;
    }
}

二、传递数据的结构体

一般在很多例子里都是通过byte数组来传递数据,本质上都是差不多,但是结构体看着方便而且爽不是吗?废话不多说,反正上代码就完事了。记得要规定好结构体的排序方式,不然会跟人通信时容易乱码。

using System.Runtime.InteropServices;//非托管代码交互,能够把非托管代码转化为托管代码

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public class MemoryClass
{
	public short bianliang1;  //第一个变量
	public short bianliang2;  //第二个变量
	public short bianliang3;  //第三个变量
}

三、在主程序中如何使用?

还是直接上代码吧,可以直接复制粘贴的快乐对于小白来说很舒服。
我们现在VS里面新建一个C#的控制应用台程序,然后添加好MemoryClass类和ShareMemory类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharememberTest
{
    class Program
    {
        public static MemoryClass datastruct = new MemoryClass();


        static void Main(string[] args)
        {
            long ShareSize = System.Runtime.InteropServices.Marshal.SizeOf(datastruct);
            
            ShareMemory share1 = new ShareMemory("ShareMemory", ShareSize);
            //创建共享内存
            if(share1.CreateMemory() == 0)
            {
                Console.WriteLine("创建共享内存成功" + DateTime.Now.ToString());
                Console.WriteLine("当前共享内存的大小为:" + share1.GetSize().ToString());
                Console.WriteLine("当前共享内存文件句柄为:" + share1.GetFileMapPtr().ToString());
                Console.WriteLine("当前共享内存的印射地址为:" + share1.GetPtr().ToString());

                //写共享内存
                if (share1.WriteToMemory(datastruct) == 0)
                    Console.WriteLine("share1写入共享内存成功" + DateTime.Now.ToString());

                //读共享内存
                datastruct = (MemoryClass)share1.ReadFromMemory(typeof(MemoryClass));

            }

            

            //关闭共享内存 关不关都行,我留了一手
            share1.ShareMemoryClose();

            Console.ReadKey();
        }
    }
}

如果想看全部程序的话看这个下载链接,如果审核过了的话
https://download.csdn.net/download/doctor_water/87373535

你可能感兴趣的:(C#,c#,经验分享)