Search code examples
c#ipcmemory-mapped-filesmemory-mapping

Memory Mapped Files between two processes


I created a C# program which does some stuff. Let's call it Program B. I want to open Program B's executable in another program, Program A, I am creating also in C#. What I want to do is to get some information(actually a custom object) from Program B on a button click which also will close the program and add the information in a List in Program A. I want to use Memory Mapped Files for this, but somehow I didn't manage to do this until now. I have created a class:

public class MemoryMappedFileCommunicator{
 public MemoryMappedFileCommunicator()
        {
            MemoryMappedFile = MemoryMappedFile.CreateOrOpen(MMF_FILE_NAME, MMF_MAX_SIZE);
           
            
            // Create a stream which allows it to write data from offset 0 to 1024 (whole memory)
            MemoryMappedViewStream = MemoryMappedFile.CreateViewStream(0, MMF_VIEW_SIZE);
            
            _commandToSend = new Command();
            //callback = new SendOrPostCallback(OnDataReceivedInternal);
            //operation = AsyncOperationManager.CreateOperation(null);
        }
}

I tried using SendOrPostCallback seen in a tutorial, but it didn't make sense to me. I also tried using an event handler, but my event did not fire. What I actually want to do is to fire an event every time I finished writing in the file, so Program A could read the information and put it into a List.

Write function looks like this:

public void WriteData(MyCustomObject obj)
        {
            FileStream writer = new FileStream(MMF_FILE_NAME, FileMode.Append);
            DataContractSerializer ser = new(typeof(MyCustomObject ));
            ser.WriteObject(writer, obj);
            writer.Close();
            DataWrittenEvent();

        }

My delegate is void and has no parameters

Read function looks like this:

public MyCustomObject ReadData()
        {
            FileStream reader = new FileStream(MMF_FILE_NAME, FileMode.Open);
            XmlDictionaryReader xmlDictionaryReader = XmlDictionaryReader.CreateTextReader(reader, new XmlDictionaryReaderQuotas());
            DataContractSerializer ser = new DataContractSerializer(typeof(MyCustomObject ));

            MyCustomObject deserializedObj = (MyCustomObject )ser.ReadObject(xmlDictionaryReader, true);
            xmlDictionaryReader.Close();
            reader.Close();

            return deserializedObj;
        }

In every program I create an instance of MemoryMappedFileCommunicator, but only in Program A's constructor I attach the DataWrittenEvent event, but it is null all the time.

What am I doing wrong? How could I manage to do this?

I expected to make a callback from Program B to Program A like this: Program B: get information -> serialize information -> write serialized information to MMF (this should rise an event to Program A) Program A: read from MMF -> deserialize information -> add to list


Solution

  • So I figured it out finally. The way I work with the 2 processes is the following:

    I have process A from which I will open process B. Process B will calculate some stuff and on the button click "Add" it will add the information into a MemoryMappedFile (created in Process A before opening Process B). It is important to give it a name and set the HandleInheritability to HandleInheritability.Inheritable. Also an EventWaitHandle is created in Process A in order to wait for Process B to do its job. I am sending objects between the two processes, so I serialize the object and after that I write the information in the MMF. The same at reading, I deserialize the object after reading it from the MMF.

    The solution looks like this:

    Process A:

    EventWaitHandle ewh = new EventWaitHandle(false, EventResetMode.AutoReset, "ObjectAddedEvent");
    MyObject obj= new();
    
                using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen("ObjectMMF", 1024, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None, HandleInheritability.Inheritable))
                {
    
                    Process myProcess = Process.Start(psi);
                    ewh.WaitOne(); // Here we wait for Process B to do its job
                    
                    // This will be executed AFTER Process B finishes its task (after ewh.Set())
                    using (MemoryMappedViewStream stream = mmf.CreateViewStream()) 
                    {
                        BinaryReader reader = new BinaryReader(stream);
                        string receivedObjectString = reader.ReadString();
                        if (!string.IsNullOrEmpty(receivedObjectString ))
                            obj= JsonSerializer.Deserialize<MyObject>(receivedObjectString);
                    }
    
                    // AddReceivedObjectToList(obj); -- this is a function I need in my program. It adds the object received from Process B into a list in Process A, after it is read by Process A from the MMF
    
                    ewh.Dispose();
                    myProcess.Kill(); // I will kill Process B manually after Process A read everything from the MMF
    

    Process B:

    MyObject obj= GetObject();
    string serializedObject = JsonSerializer.Serialize(obj);
    using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("ObjectMMF", MemoryMappedFileRights.ReadWrite, HandleInheritability.Inheritable))
                    {
    
                        bool ewhExists = EventWaitHandle.TryOpenExisting("ObjectAddedEvent", out EventWaitHandle ewh);
    
                        using (MemoryMappedViewStream stream = mmf.CreateViewStream())
                        {
                            BinaryWriter writer = new BinaryWriter(stream);
                            writer.Write(serializedObject);
                        }
    
                        ewh.Set(); // This gives Process A a signal that it finished its job and Process A will continue to read the data from the MMF
                    }