C#-exe间通讯(二)-共享内存

一、共享内存的帮助类

1、基于内存偏移量 ViewAccessorHelper

/**
*┌──────────────────────────────────────────────────────────────┐
*│ 描    述:两个exe程序之间通信_共享内存传递 - 基于内存偏移量(主从运行,不可单独运行)
*│ 作    者:执笔小白
*│ 版    本:1.1
*│ 创建时间:2022-12-06 10:40:56
*└──────────────────────────────────────────────────────────────┘
*┌──────────────────────────────────────────────────────────────┐
*│ 命名空间: ExeCommunicationHelper
*│ 类    名:SharedMemory_ViewAccessorHelper
*└──────────────────────────────────────────────────────────────┘
*/
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

namespace ExeCommunicationHelper
{
    /// <summary>
    /// 两个exe程序之间通信_共享内存传递(主从运行,不可单独运行)
    /// 基于内存偏移量
    /// </summary>
    public class SharedMemory_ViewAccessorHelper<T> where T : struct
    {
        /*
         * 二进制 100= 0x6400000;10=0x0A00000
         * 
         * 注意一:线程或进程间通讯时使用互斥锁
         * //bool mutexCreated;
         * //Mutex mutex = newMutex(true, "testmapmutex", out mutexCreated);  // 创建锁(指定锁名)
         * //try{
         * //    mutex.WaitOne();  // 等待其他程序释放锁
         * //    // 通讯处理...
         * //}
         * //finally{
         * //    mutex.ReleaseMutex();  // 释放锁
         * //}
         * 
         * 注意二:非持久文件映射时,注意MemoryMappedFile不要被GC。
         * 
         */

        #region 非持久文件映射
        /*
         * 1、非持久文件映射
         * 非持久文件是未与磁盘上的源文件关联的内存映射文件。
         * 当最后一个进程使用完此文件后,数据将丢失,并且垃圾回收功能将回收此文件。
         * 可使用非托管对象防止被回收
         */

        private MemoryMappedFile? mmf_NoKeep;
        /// <summary>
        /// 内存共享_基于内存偏移量的操作_读-非持久文件映射
        /// </summary>
        /// <param name="mmfName">共享内容对象名</param>
        /// <returns></returns>
        public T ReadViewAccessor_NoKeep(string mmfName)
        {
            T resultShareInfo = default;

            mmf_NoKeep = MemoryMappedFile.CreateOrOpen(mmfName, 0x6400000, MemoryMappedFileAccess.ReadWrite);  // 打开共享内存区域,大小默认为100MB
            using (MemoryMappedViewAccessor accessor = mmf_NoKeep.CreateViewAccessor(0, 100))  // 注意这里的偏移量,大小;大小100MB
            {
                int colorSize = Marshal.SizeOf(typeof(T));
                accessor.Read(0, out resultShareInfo);  // 读取视图
            }
            return resultShareInfo;
        }
        /// <summary>
        /// 内存共享_基于内存偏移量的操作_写-非持久文件映射
        /// </summary>
        /// <param name="mmfName"></param>
        /// <param name="shareInfo"></param>
        /// <returns></returns>
        public bool WirteViewAccessor_NoKeep(string mmfName, T shareInfo)
        {
            bool result = false;

            mmf_NoKeep = MemoryMappedFile.CreateOrOpen(mmfName, 0x6400000, MemoryMappedFileAccess.ReadWrite);  // 打开共享内存区域,大小默认为100MB 
            using (MemoryMappedViewAccessor accessor = mmf_NoKeep.CreateViewAccessor(0, 100))  // 注意这里的偏移量,大小;大小100MB
            {
                result = accessor.CanRead;
                if (result)
                {
                    int colorSize = Marshal.SizeOf(typeof(T));
                    // 视图操作
                    accessor.Write(0, ref shareInfo);  // 修改视图
                }
            }
            return result;
        }
        #endregion 非持久文件映射

        #region 持久文件映射
        /* 
         * 2、持久文件映射
         * 持久文件是与磁盘上的源文件关联的内存映射文件。
         * 在最后一个进程使用完此文件后,数据将保存到磁盘上的源文件中。这些内存映射文件适合用来处理非常大的源文件。
         * 不需要考虑维护共享内存对象被回收的问题
         */

        /// <summary>
        /// 内存共享_基于内存偏移量的操作_读-持久文件映射
        /// </summary>
        /// <param name="mmfFile">共享内容对象的文件路径</param>
        /// <param name="mmfName">共享内容对象名</param>
        /// <returns></returns>
        public T ReadViewAccessor_Keep(string mmfFile, string mmfName, long defaultSize = 0x6400000)
        {
            T resultShareInfo = default;

            using (MemoryMappedFile mmf_Keep = MemoryMappedFile.CreateFromFile(mmfFile, FileMode.OpenOrCreate, mmfName, defaultSize))  // "D:\\内存映射文件.data",默认100MB
            {
                using (MemoryMappedViewAccessor accessor = mmf_Keep.CreateViewAccessor(0, 100))  // 注意这里的偏移量,大小;大小需要调整
                {
                    int colorSize = Marshal.SizeOf(typeof(T));
                    // 视图操作
                    accessor.Read(0, out resultShareInfo);  // 读取视图
                }
            }

            return resultShareInfo;
        }
        /// <summary>
        /// 内存共享_基于内存偏移量的操作_写-持久文件映射
        /// </summary>
        /// <param name="mmfFile">共享内容对象的文件路径</param>
        /// <param name="mmfName">共享内容对象名</param>
        /// <param name="share">写入的内容(二进制,单位MB)</param>
        public bool WirteViewAccessor_Keep(string mmfFile, string mmfName, T shareInfo, long defaultSize = 0x6400000)
        {
            bool result = false;

            using (MemoryMappedFile mmf_Keep = MemoryMappedFile.CreateFromFile(mmfFile, FileMode.OpenOrCreate, mmfName, defaultSize))  // "D:\\内存映射文件.data",默认100MB
            {
                using (MemoryMappedViewAccessor accessor = mmf_Keep.CreateViewAccessor(0, 100))  // 注意这里的偏移量,大小;大小需要调整
                {
                    result = accessor.CanRead;
                    if (result)
                    {
                        int colorSize = Marshal.SizeOf(typeof(T));
                        // 视图操作
                        accessor.Write(0, ref shareInfo);  // 修改视图
                    }
                }
            }
            return result;
        }
        #endregion 持久文件映射
    }

    #region 示例结构
    /// <summary>
    /// 内存共享_基于内存偏移量的操作_示例结构
    /// </summary>
    public struct SharedMemory_ViewAccessorA
    {
        /// <summary>
        /// 标识ID
        /// 如:1,2,3,...
        /// </summary>
        public int SessionId { set; get; } = 0;

        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateTime { set; get; } = DateTime.Now;

        /// <summary>
        /// 自定义的共享信息
        /// </summary>
        public long InfoData { set; get; } = 0;
        //[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]  // 不支持中文字符
        //[MarshalAs(UnmanagedType.LPUTF8Str)]
        //public string InfoData;

        public SharedMemory_ViewAccessorA()
        {
            //SessionId = string.Empty;
            //InfoData = string.Empty;
        }
    }
    #endregion 示例结构
}

2、基于流的操作 ViewStreamHelper

/**
*┌──────────────────────────────────────────────────────────────┐
*│ 描    述:两个exe程序之间通信_共享内存传递 - 基于流的操作(主从运行,不可单独运行)
*│ 作    者:执笔小白
*│ 版    本:1.1
*│ 创建时间:2022-12-06 10:40:56
*└──────────────────────────────────────────────────────────────┘
*┌──────────────────────────────────────────────────────────────┐
*│ 命名空间: ExeCommunicationHelper
*│ 类    名:SharedMemory_ViewStreamHelper
*└──────────────────────────────────────────────────────────────┘
*/
using Newtonsoft.Json;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

namespace ExeCommunicationHelper
{
    /// <summary>
    /// 两个exe程序之间通信_共享内存传递(主从运行,不可单独运行)
    /// 基于流的操作
    /// </summary>
    public class SharedMemory_ViewStreamHelper<T>
    {
        /*
         * 二进制 100= 0x6400000;10=0x0A00000
         * 
         * 注意一:线程或进程间通讯时使用互斥锁
         * //bool mutexCreated;
         * //Mutex mutex = newMutex(true, "testmapmutex", out mutexCreated);  // 创建锁(指定锁名)
         * //try{
         * //    mutex.WaitOne();  // 等待其他程序释放锁
         * //    // 通讯处理...
         * //}
         * //finally{
         * //    mutex.ReleaseMutex();  // 释放锁
         * //}
         * 
         * 注意二:非持久文件映射时,注意MemoryMappedFile不要被GC。
         * 
         */

        #region 非持久文件映射
        /*
         * 1、非持久文件映射
         * 非持久文件是未与磁盘上的源文件关联的内存映射文件。
         * 当最后一个进程使用完此文件后,数据将丢失,并且垃圾回收功能将回收此文件。
         * 可使用非托管对象防止被回收
         */

        private MemoryMappedFile? mmf_NoKeep;

        /// <summary>
        /// 内存共享_基于流的操作_读-非持久文件映射
        /// </summary>
        /// <param name="mmfName">共享内容对象名</param>
        /// <returns></returns>
        public T ReadViewStream_NoKeep(string mmfName)
        {
            T resultShareInfo = default;

            mmf_NoKeep = MemoryMappedFile.CreateOrOpen(mmfName, 0x6400000, MemoryMappedFileAccess.ReadWrite);  // 打开共享内存区域,大小默认为100MB
            using (MemoryMappedViewStream stream = mmf_NoKeep.CreateViewStream())
            {
                if (stream.CanRead)
                {
                    BinaryReader reader = new(stream);
                    if (JsonConvert.DeserializeObject<T>(reader.ReadString()) is T share)
                    {
                        resultShareInfo = share;
                    }
                }
            }
            return resultShareInfo;
        }
        /// <summary>
        /// 内存共享_基于流的操作_写-非持久文件映射
        /// </summary>
        /// <param name="mmfName">共享内容对象名</param>
        /// <param name="share">写入的内容</param>
        public bool WirteViewStream_NoKeep(string mmfName, T shareInfo)
        {
            bool result = false;

            mmf_NoKeep = MemoryMappedFile.CreateOrOpen(mmfName, 0x6400000, MemoryMappedFileAccess.ReadWrite);  // 打开共享内存区域,大小默认为100MB
            using (MemoryMappedViewStream stream = mmf_NoKeep.CreateViewStream())
            {
                result = stream.CanWrite;
                if (result)
                {
                    BinaryWriter writer = new(stream);
                    writer.Write(JsonConvert.SerializeObject(shareInfo));
                }
            }
            return result;
        }
        #endregion 非持久文件映射

        #region 持久文件映射
        /* 
         * 2、持久文件映射
         * 持久文件是与磁盘上的源文件关联的内存映射文件。
         * 在最后一个进程使用完此文件后,数据将保存到磁盘上的源文件中。这些内存映射文件适合用来处理非常大的源文件。
         * 不需要考虑维护共享内存对象被回收的问题
         */

        /// <summary>
        /// 内存共享_基于流的操作_读-持久文件映射
        /// </summary>
        /// <param name="mmfFile">共享内容对象的文件路径</param>
        /// <param name="mmfName">共享内容对象名</param>
        /// <returns></returns>
        public T ReadViewStream_Keep(string mmfFile, string mmfName, long defaultSize = 0x6400000)
        {
            T resultShareInfo = default;

            using (MemoryMappedFile mmf_Keep = MemoryMappedFile.CreateFromFile(mmfFile, FileMode.OpenOrCreate, mmfName, defaultSize))  // "D:\\内存映射文件.data",默认100MB
            {
                using (MemoryMappedViewStream stream = mmf_Keep.CreateViewStream())
                {
                    if (stream.CanRead)
                    {
                        BinaryReader reader = new(stream);
                        if (JsonConvert.DeserializeObject<T>(reader.ReadString()) is T share)
                        {
                            resultShareInfo = share;
                        }
                    }
                }
            }
            return resultShareInfo;
        }
        /// <summary>
        /// 内存共享_基于流的操作_写-持久文件映射
        /// </summary>
        /// <param name="mmfFile">共享内容对象的文件路径</param>
        /// <param name="mmfName">共享内容对象名</param>
        /// <param name="share">写入的内容</param>
        public bool WirteViewStream_Keep(string mmfFile, string mmfName, T shareInfo, long defaultSize = 0x6400000)
        {
            bool result = false;
            using (MemoryMappedFile mmf_Keep = MemoryMappedFile.CreateFromFile(mmfFile, FileMode.OpenOrCreate, mmfName, defaultSize))  // "D:\\内存映射文件.data",默认100MB
            {
                using (MemoryMappedViewStream stream = mmf_Keep.CreateViewStream())
                {
                    result = stream.CanWrite;
                    if (result)
                    {
                        BinaryWriter writer = new(stream);
                        writer.Write(JsonConvert.SerializeObject(shareInfo));
                    }
                }
            }
            return result;
        }
        #endregion 持久文件映射
    }

    #region 示例结构
    /// <summary>
    /// 内存共享_基于流的操作_示例结构
    /// </summary>
    public class SharedMemory_ViewStreamA<T>
    {
        /// <summary>
        /// 标识ID
        /// 如:GUID
        /// </summary>
        public string SessionId { set; get; } = string.Empty;

        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateTime { set; get; } = DateTime.Now;

        /// <summary>
        /// 自定义的共享信息
        /// </summary>
        public T InfoData { set; get; } = default;
    }
    #endregion 示例结构
}

二、发送端:

 /// <summary>
 /// 共享内存传递_发送
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 private void btnSedMsg_SharedMemo_Click(object sender, EventArgs e)
 {
     bool result = false;

     if (radIsStream.Checked && chkIsKeep.Checked)
     {
         SharedMemory_ViewStreamA<string> shareInfo = new();
         shareInfo.SessionId = "1";
         shareInfo.CreateTime = DateTime.Now;
         shareInfo.InfoData = txtMsg_SharedMemo.Text.Trim();

         // result = MemoryMappedFile_ViewStream_Wirte_Keep(txtShareFilePath.Text.Trim(), txtShareName.Text.Trim(), shareInfo); 
         result = sharedMemoryStreamHelper.WirteViewStream_Keep(@"D:\ShareTest.data", @"ImgA", shareInfo);
     }  // 内存共享_基于流的操作_写-持久文件映射
     else if (radIsStream.Checked && !chkIsKeep.Checked)
     {
         SharedMemory_ViewStreamA<string> shareInfo = new();
         shareInfo.SessionId = "1";
         shareInfo.CreateTime = DateTime.Now;
         shareInfo.InfoData = txtMsg_SharedMemo.Text.Trim();

         result = sharedMemoryStreamHelper.WirteViewStream_NoKeep(txtShareName.Text.Trim(), shareInfo);

     }  // 内存共享_基于流的操作_写-非持久文件映射
     else if (!radIsStream.Checked && chkIsKeep.Checked)
     {
         SharedMemory_ViewAccessorA shareInfo = new();
         shareInfo.SessionId = 1;
         shareInfo.CreateTime = DateTime.Now;
         shareInfo.InfoData = Convert.ToInt32(txtMsg_SharedMemo.Text.Trim());

         //result = MemoryMappedFile_ViewAccessor_Wirte_Keep(txtShareFilePath.Text.Trim(), txtShareName.Text.Trim(), shareInfo);
         result = sharedMemoryAccessorHelper.WirteViewAccessor_Keep(@"D:\ShareTest.data", @"ImgA", shareInfo);
     }  // 内存共享_基于内存偏移量的操作_写-持久文件映射
     else if (!radIsStream.Checked && !chkIsKeep.Checked)
     {
         SharedMemory_ViewAccessorA shareInfo = new();
         shareInfo.SessionId = 1;
         shareInfo.CreateTime = DateTime.Now;
         shareInfo.InfoData = Convert.ToInt32(txtMsg_SharedMemo.Text.Trim());

         result = sharedMemoryAccessorHelper.WirteViewAccessor_NoKeep(txtShareName.Text.Trim(), shareInfo);  //
     }  // 内存共享_基于内存偏移量的操作_写-非持久文件映射
     txtMsg_SharedMemo.Text += ";" + result.ToString();
 }

三、接收端:

/// <summary>
/// 共享内存方式-手动获取(根据需要加互斥锁或进程内线程锁)
/// </summary>
private void BtnManualGet_Click(object sender, EventArgs e)
{
    if (radIsStream.Checked && chkIsKeep.Checked)  // 内存共享_基于流的操作_写-持久文件映射
    {
        SharedMemory_ViewStreamA<string> shareInfo = sharedMemoryStreamHelper.ReadViewStream_Keep(txtShareFilePath.Text.Trim(), txtShareName.Text.Trim());
        if (shareInfo != null && shareInfo.InfoData is string infoData)
        {
            rTxtShareMemo_msg.Text = "信息序号:" + shareInfo.SessionId + ";发送时间:" + shareInfo.CreateTime + ";内容:" + (!string.IsNullOrEmpty(infoData) ? infoData : string.Empty);
        }
        else
        {
            rTxtShareMemo_msg.Text = "未获取到数据!";
        }
    }
    else if (radIsStream.Checked && !chkIsKeep.Checked)  // 内存共享_基于流的操作_写-非持久文件映射
    {
        SharedMemory_ViewStreamA<string> shareInfo = sharedMemoryStreamHelper.ReadViewStream_NoKeep(txtShareName.Text.Trim());
        if (shareInfo != null && shareInfo.InfoData is string infoData)
        {
            rTxtShareMemo_msg.Text = "信息序号:" + shareInfo.SessionId + ";发送时间:" + shareInfo.CreateTime + ";内容:" + (!string.IsNullOrEmpty(infoData) ? infoData : string.Empty);
        }
        else
        {
            rTxtShareMemo_msg.Text = "未获取到数据!";
        }
    }
    else if (!radIsStream.Checked && chkIsKeep.Checked)  // 内存共享_基于内存偏移量的操作_写-持久文件映射
    {
        SharedMemory_ViewAccessorA shareInfo = sharedMemoryAccessorHelper.ReadViewAccessor_Keep(txtShareFilePath.Text.Trim(), txtShareName.Text.Trim());
        rTxtShareMemo_msg.Text = "信息序号:" + shareInfo.SessionId + ";发送时间:" + shareInfo.CreateTime + ";内容:" + (long)shareInfo.InfoData;
    }
    else if (!radIsStream.Checked && !chkIsKeep.Checked)  // 内存共享_基于内存偏移量的操作_写-非持久文件映射
    {
        SharedMemory_ViewAccessorA shareInfo = sharedMemoryAccessorHelper.ReadViewAccessor_NoKeep(txtShareName.Text.Trim());  //

        rTxtShareMemo_msg.Text = "信息序号:" + shareInfo.SessionId + ";发送时间:" + shareInfo.CreateTime + ";内容:" + (long)shareInfo.InfoData;
    }
}
posted @ 2022-12-26 10:27  ꧁执笔小白꧂  阅读(1808)  评论(0)    收藏  举报