Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

C# Wrapper for Mailslots

5.00/5 (9 votes)
19 Jul 2013CPOL 22.5K  
A wrapper for Mailslots in C#
As .NET standard libraries do not provide classes for Mailslot operations and wrappers that I found did not satisfy my requirements, here is my implementation and how you can use it:

Introduction

Mailslot is a simple type of inter-process communication on Windows platform. It is one-way fire-and-forget no-confirmation type of communication: a server opens a slot and multiple clients can send messages to it.

.NET standard libraries do not provide classes for Mailslot operations and wrappers that I found did not satisfy my requirements - simplicity and elegance. :)

This is my implementation, here is how you can use it:

C#
using (var server = new MailslotServer("MailslotName"))
{
    var msg = server.GetNextMessage();

    if (msg != null)
    {
        DoWork();
    }

    Thread.Sleep(1000);
}

using (var client = new MailslotClient("MailslotName"))
{
    client.SendMessage("hello server :)");
}

And the code...

C#
[SuppressUnmanagedCodeSecurity]
public static class Mailslot
{
    public const int MailslotNoMessage = -1;

    [Flags]
    public enum FileDesiredAccess : uint
    {
        GenericRead = 0x80000000,
        GenericWrite = 0x40000000,
        GenericExecute = 0x20000000,
        GenericAll = 0x10000000
    }

    [Flags]
    public enum FileShareMode : uint
    {
        Zero = 0x00000000,
        FileShareDelete = 0x00000004,
        FileShareRead = 0x00000001,
        FileShareWrite = 0x00000002
    }

    public enum FileCreationDisposition : uint
    {
        CreateNew = 1,
        CreateAlways = 2,
        OpenExisting = 3,
        OpenAlways = 4,
        TruncateExisting = 5
    }

    [SecurityCritical(SecurityCriticalScope.Everything)]
    [HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)]
    [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
    public sealed class SafeMailslotHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        private SafeMailslotHandle() : base(true){}
        public SafeMailslotHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle)
        {
            base.SetHandle(preexistingHandle);
        }

        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle(IntPtr handle);

        protected override bool ReleaseHandle()
        {
            return CloseHandle(base.handle);
        }
    }

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern SafeMailslotHandle CreateMailslot(string mailslotName,
                                                uint nMaxMessageSize, int lReadTimeout,
                                                IntPtr securityAttributes);


    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool GetMailslotInfo(SafeMailslotHandle hMailslot,
                                                IntPtr lpMaxMessageSize, 
                                                out int lpNextSize, out int lpMessageCount,
                                                IntPtr lpReadTimeout);


    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool ReadFile(SafeMailslotHandle handle,
                                        byte[] bytes, int numBytesToRead, 
                                        out int numBytesRead,
                                        IntPtr overlapped);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool WriteFile(SafeMailslotHandle handle,
                                        byte[] bytes, int numBytesToWrite, 
                                        out int numBytesWritten,
                                        IntPtr overlapped);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern SafeMailslotHandle CreateFile(string fileName,
                                        FileDesiredAccess desiredAccess, 
                                        FileShareMode shareMode,
                                        IntPtr securityAttributes,
                                        FileCreationDisposition creationDisposition,
                                        int flagsAndAttributes, IntPtr hTemplateFile);
}

public class MailslotServer : IDisposable
{
    private Mailslot.SafeMailslotHandle _handle;

    public MailslotServer(string name)
    {
        _handle = Mailslot.CreateMailslot(@"\\.\mailslot\" + name, 0, 0, IntPtr.Zero);
        if (_handle.IsInvalid) throw new Win32Exception();
    }

    public string GetNextMessage()
    {
        int messageBytes;
        int bytesRead;
        int messages;

        if (!Mailslot.GetMailslotInfo(_handle, IntPtr.Zero, out messageBytes, 
                     out messages, IntPtr.Zero)) throw new Win32Exception();

        if (messageBytes == Mailslot.MailslotNoMessage) return null;

        var bBuffer = new byte[messageBytes];

        if (!Mailslot.ReadFile( _handle, bBuffer, messageBytes, out bytesRead, 
             IntPtr.Zero) || bytesRead == 0) throw new Win32Exception();

        return Encoding.Unicode.GetString(bBuffer);
    }

    public void Dispose()
    {
        if (_handle != null)
        {
            _handle.Close();
            _handle = null;
        }
    }
}

public class MailslotClient : IDisposable
{
    private Mailslot.SafeMailslotHandle _handle;
    private readonly string _name;
    private readonly string _machine;

    public MailslotClient(string name) : this(name, ".") { }
    public MailslotClient(string name, string machine)
    {
        _name = name;
        _machine = machine;
    }

    public void SendMessage(string msg)
    {
        if (_handle == null) CreateHandle();

        int bytesWritten;

        byte[] bMessage = Encoding.Unicode.GetBytes(msg);

        bool succeeded = Mailslot.WriteFile(_handle, bMessage, 
             bMessage.Length, out bytesWritten, IntPtr.Zero);

        if (!succeeded || bMessage.Length != bytesWritten)
        {
            if (_handle != null) _handle.Close();
            _handle = null;

            throw new Win32Exception();
        }
    }
    public void Dispose()
    {
        if (_handle != null)
        {
            _handle.Close();
            _handle = null;
        }
    }

    private void CreateHandle()
    {
        _handle = Mailslot.CreateFile(
            @"\\" + _machine + @"\mailslot\" + _name,
            Mailslot.FileDesiredAccess.GenericWrite,
            Mailslot.FileShareMode.FileShareRead,
            IntPtr.Zero,
            Mailslot.FileCreationDisposition.OpenExisting,
            0,
            IntPtr.Zero);

        if (_handle.IsInvalid) throw new Win32Exception();
            
    }
}

History

  • 19th July, 2013: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)