C#进程间Memory Mapped Files通信问题:跨进程传递自定义对象
跨进程内存映射文件通信问题排查与实现方案
当前实现的核心错误
- 错误使用FileStream操作内存映射文件:代码中创建了
MemoryMappedFile对象,但读写逻辑却用FileStream直接操作磁盘文件(MMF_FILE_NAME对应的物理文件),完全没有利用内存映射文件的共享内存能力,MMF对象被闲置。 - 跨进程事件通知机制无效:
DataWrittenEvent始终为null,因为Program A未订阅该事件;且SendOrPostCallback是线程间通信工具,无法实现跨进程事件通知。 - 序列化/反序列化逻辑不匹配MMF特性:写入时用
FileMode.Append导致数据叠加,未重置流位置;读取时直接从磁盘文件读取,与MMF共享内存完全脱节。 - 资源释放不规范:手动调用
Close()而非使用using语句,易引发资源泄漏,导致MMF或流无法正确释放。
正确实现流程
- Program A:
- 创建/打开共享内存映射文件(MMF)及对应视图流
- 创建跨进程事件(
EventWaitHandle)接收Program B的通知 - 启动Program B进程,阻塞等待事件触发
- 事件触发后,从MMF读取数据、反序列化,将对象添加到列表
- 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
相关产品推荐
相关产品推荐

