Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Inter-Process Communication in C# Using Shared Memory

Tech May 18 3

MemoryMappedFile in .NET

The MemoryMappedFile class serves as the foundation for shared memory operations in .NET. Creating a shared memory segment requires specifying a unique name and size allocation.

// Initialize shared memory segment
MemoryMappedFile mappedFile = MemoryMappedFile.CreateNew(memoryName, capacity);

Writing data involves creating a view accessor and writing byte arrays at specific offsets:

using (var viewAccessor = mappedFile.CreateViewAccessor(0, totalCapacity))
{
    viewAccessor.WriteArray(byteOffset, sourceData, 0, sourceData.Length);
}

Reading follows a similar pattern:

using (var viewAccessor = mappedFile.CreateViewAccessor(0, totalCapacity))
{
    viewAccessor.ReadArray(byteOffset, destinationBuffer, 0, destinationBuffer.Length);
}

Encapsulated Shared Memory Implementation

SharedMemory Wrapper Class

public class SharedMemorySegment : IDisposable
{
    public string Identifier { get; private set; }
    public long Capacity { get; private set; }
    
    public static SharedMemorySegment Initialize(
        string identifier, 
        ISharedMemoryMappingContract mappingContract);
    
    public void Terminate();
    public void Dispose();
}

Mapping Contract Interface

Classes implementing this interface can map their properties direct to shared memory regions. Supported property types include primitives, structs, and byte arrays.

public interface ISharedMemoryMappingContract
{
    event Action<object propertychangeeventdata=""> OnPropertyUpdated;
    event Func<string, object> OnPropertyRequested;
}

public class PropertyChangeEventData
{
    public object NewValue { get; set; }
    public string PropertyIdentifier { get; set; }
}

public abstract class SharedMemoryMappingBase : ISharedMemoryMappingContract
{
    protected void SignalPropertyChange(object newValue, 
        [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "");
    
    protected object RetrievePropertyValue(object defaultValue, 
        [System.Runtime.CompilerServices.CallerMemberName] string propertyName = "");
    
    public event Action<object, PropertyChangeEventData> OnPropertyUpdated;
    public event Func<string, object> OnPropertyRequested;
}</object>

Implementation Guide

Defining the Data Contract

Inherit from SharedMemoryMappingBase and define properties with the required getter/setter pattern:

public class PlaybackStateData : SharedMemoryMappingBase
{
    public double Timestamp 
    { 
        set { SignalPropertyChange(value); } 
        get { return (double)RetrievePropertyValue(0.0); } 
    }
    
    public double TotalDuration 
    { 
        set { SignalPropertyChange(value); } 
        get { return (double)RetrievePropertyValue(0.0); } 
    }
    
    public int PausedState 
    { 
        set { SignalPropertyChange(value); } 
        get { return (int)RetrievePropertyValue(0); } 
    }
    
    public int MuteState 
    { 
        set { SignalPropertyChange(value); } 
        get { return (int)RetrievePropertyValue(0); } 
    }
    
    public int HeartbeatSignal 
    { 
        set { SignalPropertyChange(value); } 
        get { return (int)RetrievePropertyValue(0); } 
    }
    
    public int ControlFlags 
    { 
        set { SignalPropertyChange(value); } 
        get { return (int)RetrievePropertyValue(0); } 
    }
    
    public int DataLength 
    { 
        set { SignalPropertyChange(value); } 
        get { return (int)RetrievePropertyValue(0); } 
    }
    
    [MemoryRegion(BufferSize = 1024)]
    public byte[] InputBuffer 
    { 
        set { SignalPropertyChange(value); } 
        get { return (byte[])RetrievePropertyValue(null); } 
    }
    
    [MemoryRegion(BufferSize = 1024)]
    public byte[] OutputBuffer 
    { 
        set { SignalPropertyChange(value); } 
        get { return (byte[])RetrievePropertyValue(null); } 
    }
}

Important constraints:

  • Property types must be value types (primitives or structs). Reference types are limited to byte arrays with explicit size declaration.
  • Property declaration order determines memory layout order.
  • Current implementation uses 1-byte alignment; C++ counterparts require #pragma pack(1).

Equivalent C++ structure definition:

#pragma pack(1)
typedef struct {
    double Timestamp;
    double TotalDuration;
    int PausedState;
    int MuteState;
    int HeartbeatSignal;
    int ControlFlags;
    int DataLength;
    char InputBuffer[1024];
    char OutputBuffer[1024];
} PlaybackStateData;

Creating the Shared Memory Segment

var playbackData = new PlaybackStateData();
var memorySegment = SharedMemorySegment.Initialize("GlobalPlaybackState", playbackData);

Cleanup

memorySegment.Dispose();

Practical Example

The following example demonstrates communication between a C# parent process and a C++ child process using a mutex for synchronization.

C# Parent Process

using System;
using System.Diagnostics;
using System.Text;
using System.Threading;

namespace SharedMemoryDemo
{
    public class ProcessControllerData : SharedMemoryMappingBase
    {
        public double Timestamp 
        { 
            set { SignalPropertyChange(value); } 
            get { return (double)RetrievePropertyValue(0.0); } 
        }
        
        public double TotalDuration 
        { 
            set { SignalPropertyChange(value); } 
            get { return (double)RetrievePropertyValue(0.0); } 
        }
        
        public int PausedState 
        { 
            set { SignalPropertyChange(value); } 
            get { return (int)RetrievePropertyValue(0); } 
        }
        
        public int MuteState 
        { 
            set { SignalPropertyChange(value); } 
            get { return (int)RetrievePropertyValue(0); } 
        }
        
        public int HeartbeatSignal 
        { 
            set { SignalPropertyChange(value); } 
            get { return (int)RetrievePropertyValue(0); } 
        }
        
        public int ControlFlags 
        { 
            set { SignalPropertyChange(value); } 
            get { return (int)RetrievePropertyValue(0); } 
        }
        
        public int DataLength 
        { 
            set { SignalPropertyChange(value); } 
            get { return (int)RetrievePropertyValue(0); } 
        }
        
        [MemoryRegion(BufferSize = 1024)]
        public byte[] MessageBuffer 
        { 
            set { SignalPropertyChange(value); } 
            get { return (byte[])RetrievePropertyValue(null); } 
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("[Parent] Process started");
            
            var controllerData = new ProcessControllerData();
            var memorySegment = SharedMemorySegment.Initialize("IpcDemoChannel", controllerData);
            
            using var syncMutex = new Mutex(false, "IpcDemoMutex");
            
            Console.WriteLine("[Parent] Sending: hello world!");
            
            syncMutex.WaitOne();
            controllerData.MessageBuffer = Encoding.ASCII.GetBytes("hello world!");
            syncMutex.ReleaseMutex();
            
            var childProcess = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = "ChildProcess.exe",
                    Arguments = "IpcDemoMutex"
                }
            };
            childProcess.Start();
            
            Thread.Sleep(2000);
            
            Console.WriteLine("[Parent] Sending: terminate");
            
            syncMutex.WaitOne();
            controllerData.MessageBuffer = Encoding.ASCII.GetBytes("terminate\0");
            syncMutex.ReleaseMutex();
            
            childProcess.WaitForExit();
            
            memorySegment.Dispose();
            Console.WriteLine("[Parent] Process terminated");
        }
    }
}

C++ Child Process

#include <iostream>
#include <Windows.h>

#pragma pack(1)
typedef struct _ControllerData {
    double Timestamp;
    double TotalDuration;
    int PausedState;
    int MuteState;
    int HeartbeatSignal;
    int ControlFlags;
    int DataLength;
    char MessageBuffer[1024];
} ControllerData;

int main(int argc, char** argv)
{
    if (argc < 2)
        return -1;
    
    printf("[Child] Process started\n");
    
    HANDLE hMutex = CreateMutexA(NULL, FALSE, argv[1]);
    
    HANDLE hMapping = OpenFileMappingA(FILE_MAP_ALL_ACCESS, FALSE, "IpcDemoChannel");
    if (hMapping == NULL)
    {
        MessageBoxA(NULL, "Failed to open shared memory", "Error", MB_OK);
        return -1;
    }
    
    ControllerData* pData = (ControllerData*)MapViewOfFile(
        hMapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(ControllerData));
    
    WaitForSingleObject(hMutex, INFINITE);
    printf("[Child] Received: %s\n", pData->MessageBuffer);
    ReleaseMutex(hMutex);
    
    while (true)
    {
        Sleep(30);
        
        WaitForSingleObject(hMutex, INFINITE);
        if (strcmp(pData->MessageBuffer, "terminate") == 0)
        {
            printf("[Child] Received: %s\n", pData->MessageBuffer);
            ReleaseMutex(hMutex);
            break;
        }
        ReleaseMutex(hMutex);
    }
    
    UnmapViewOfFile(pData);
    CloseHandle(hMapping);
    CloseHandle(hMutex);
    
    printf("[Child] Process terminated\n");
    return 0;
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.