You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#进程间Memory Mapped Files通信问题:跨进程传递自定义对象

跨进程内存映射文件通信问题排查与实现方案

当前实现的核心错误

  • 错误使用FileStream操作内存映射文件:代码中创建了MemoryMappedFile对象,但读写逻辑却用FileStream直接操作磁盘文件(MMF_FILE_NAME对应的物理文件),完全没有利用内存映射文件的共享内存能力,MMF对象被闲置。
  • 跨进程事件通知机制无效:DataWrittenEvent始终为null,因为Program A未订阅该事件;且SendOrPostCallback是线程间通信工具,无法实现跨进程事件通知。
  • 序列化/反序列化逻辑不匹配MMF特性:写入时用FileMode.Append导致数据叠加,未重置流位置;读取时直接从磁盘文件读取,与MMF共享内存完全脱节。
  • 资源释放不规范:手动调用Close()而非使用using语句,易引发资源泄漏,导致MMF或流无法正确释放。

正确实现流程

  1. Program A:
    • 创建/打开共享内存映射文件(MMF)及对应视图流
    • 创建跨进程事件(EventWaitHandle)接收Program B的通知
    • 启动Program B进程,阻塞等待事件触发
    • 事件触发后,从MMF读取数据、反序列化,将对象添加到列表
  2. Program B:
    • 打开Program A创建的共享MMF及视图流
    • 点击按钮时,序列化自定义对象到MMF视图流(重置流位置、控制写入大小)
    • 触发跨进程事件通知Program A
    • 自行关闭进程

修正后的代码实现

1. 共享常量与自定义对象定义

// 共享常量,两个进程需完全一致
public static class SharedConstants
{
    public const string MMF_FILE_NAME = "MySharedMMF";
    public const int MMF_MAX_SIZE = 4096; // 足够容纳序列化后的对象
    public const string EVENT_NAME = "MyDataWrittenEvent";
}

// 自定义对象,需添加DataContract特性(DataContractSerializer要求)
[DataContract]
public class MyCustomObject
{
    [DataMember]
    public int Id { get; set; }
    [DataMember]
    public string Name { get; set; }
    // 其他业务属性...
}

2. 通用MMF通信辅助类

public class MmfCommunicator : IDisposable
{
    private readonly MemoryMappedFile _mmf;
    private readonly MemoryMappedViewStream _viewStream;
    private bool _disposed = false;

    public MmfCommunicator(bool createNew = false)
    {
        if (createNew)
        {
            // Program A创建/打开MMF
            _mmf = MemoryMappedFile.CreateOrOpen(SharedConstants.MMF_FILE_NAME, SharedConstants.MMF_MAX_SIZE);
        }
        else
        {
            // Program B打开已存在的MMF
            _mmf = MemoryMappedFile.OpenExisting(SharedConstants.MMF_FILE_NAME);
        }
        _viewStream = _mmf.CreateViewStream(0, SharedConstants.MMF_MAX_SIZE, MemoryMappedFileAccess.ReadWrite);
    }

    public void WriteObject(MyCustomObject obj)
    {
        if (obj == null) throw new ArgumentNullException(nameof(obj));
        
        // 重置流位置到起始点,覆盖原有数据
        _viewStream.Position = 0;
        _viewStream.SetLength(0);

        using var writer = XmlDictionaryWriter.CreateTextWriter(_viewStream, Encoding.UTF8);
        var serializer = new DataContractSerializer(typeof(MyCustomObject));
        serializer.WriteObject(writer, obj);
        writer.Flush();
    }

    public MyCustomObject ReadObject()
    {
        _viewStream.Position = 0; // 重置到起始点读取

        using var reader = XmlDictionaryReader.CreateTextReader(_viewStream, new XmlDictionaryReaderQuotas());
        var serializer = new DataContractSerializer(typeof(MyCustomObject));
        return (MyCustomObject)serializer.ReadObject(reader);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;

        if (disposing)
        {
            _viewStream?.Dispose();
            _mmf?.Dispose();
        }

        _disposed = true;
    }
}

3. Program A 实现

class ProgramA
{
    static List<MyCustomObject> _dataList = new List<MyCustomObject>();

    static void Main(string[] args)
    {
        // 创建跨进程手动重置事件(初始状态未触发)
        using var dataWrittenEvent = new EventWaitHandle(false, EventResetMode.ManualReset, SharedConstants.EVENT_NAME);
        
        // 初始化MMF通信器
        using var mmfComm = new MmfCommunicator(createNew: true);

        // 启动Program B进程
        var process = Process.Start(@"你的ProgramB.exe路径");
        if (process == null)
        {
            Console.WriteLine("无法启动Program B");
            return;
        }

        Console.WriteLine("等待Program B发送数据...");
        // 阻塞等待事件触发
        dataWrittenEvent.WaitOne();

        // 读取并反序列化对象
        var receivedObj = mmfComm.ReadObject();
        _dataList.Add(receivedObj);

        Console.WriteLine($"已接收对象:Id={receivedObj.Id}, Name={receivedObj.Name}");
        Console.WriteLine($"列表当前元素数量:{_dataList.Count}");

        // 重置事件(如需多次通信)
        dataWrittenEvent.Reset();
    }
}

4. Program B 实现(WinForms示例)

public partial class MainForm : Form
{
    private MmfCommunicator _mmfComm;
    private EventWaitHandle _dataWrittenEvent;

    public MainForm()
    {
        InitializeComponent();
        // 初始化MMF通信器和跨进程事件
        _mmfComm = new MmfCommunicator(createNew: false);
        _dataWrittenEvent = EventWaitHandle.OpenExisting(SharedConstants.EVENT_NAME);
    }

    private void btnSendData_Click(object sender, EventArgs e)
    {
        // 构造自定义对象(可替换为用户输入数据)
        var obj = new MyCustomObject
        {
            Id = 1,
            Name = "测试数据"
        };

        // 写入MMF
        _mmfComm.WriteObject(obj);

        // 触发事件通知Program A
        _dataWrittenEvent.Set();

        // 关闭自身进程
        Application.Exit();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            components?.Dispose();
            _mmfComm?.Dispose();
            _dataWrittenEvent?.Dispose();
        }
        base.Dispose(disposing);
    }
}

关键注意事项

  • 两个进程必须使用完全一致的共享常量(MMF名称、事件名称、MMF大小)
  • 自定义对象必须添加[DataContract]和[DataMember]特性,否则DataContractSerializer无法序列化
  • 始终用using语句自动释放MMF、视图流和事件资源,避免内存泄漏
  • 如需多次通信,可将EventWaitHandle改为自动重置模式,并循环监听事件

内容的提问来源于stack exchange,提问作者nb-1999

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 21:40:22