using System; using System.Text; using System.Runtime.InteropServices; using System.Security; using System.Runtime.ConstrainedExecution; using Microsoft.Win32.SafeHandles; using System.Security.Permissions; using System.ComponentModel; using System.Threading; namespace MailSlot { public delegate void MailSlotServerMessage(string message); public delegate void MailSlotServerError(string message); class MailSlotServer { public event MailSlotServerMessage OnMessage; public event MailSlotServerError OnError; private SafeMailslotHandle hMailslot; private volatile bool stop; private Encoding encoding; public MailSlotServer() { this.hMailslot = null; this.stop = false; encoding = Encoding.Default; } public void Listen(string MailslotName) { if (this.hMailslot != null) { this.hMailslot.Close(); this.hMailslot = null; } try { // Prepare the security attributes (the lpSecurityAttributes parameter // in CreateMailslot) for the mailslot. This is optional. If the // lpSecurityAttributes parameter of CreateMailslot is NULL, the // mailslot gets a default security descriptor and the handle cannot // be inherited. The ACLs in the default security descriptor of a // mailslot grant full control to the LocalSystem account, (elevated) // administrators, and the creator owner. They also give only read // access to members of the Everyone group and the anonymous account. // However, if you want to customize the security permission of the // mailslot, (e.g. to allow Authenticated Users to read from and // write to the mailslot), you need to create a SECURITY_ATTRIBUTES // structure. SECURITY_ATTRIBUTES sa = null; sa = CreateMailslotSecurity(); // Create the mailslot. this.hMailslot = NativeMethod.CreateMailslot( @"\\.\mailslot\" + MailslotName, // The name of the mailslot 0, // No maximum message size MAILSLOT_WAIT_FOREVER, // Waits forever for a message sa // Mailslot security attributes ); if (this.hMailslot.IsInvalid) { if (OnError != null) OnError("Unable to create the mailslot."); else throw new Win32Exception(); } else { // Start server Thread th = new Thread(new ThreadStart(checkMailSlotThread)); this.stop = false; th.Start(); } } catch (Win32Exception ex) { if (OnError != null) OnError("The server throws the error: " + ex.Message); } } /// /// Encoding property /// public Encoding Encoding { set { this.encoding = value; } get { return this.encoding; } } /// /// Closes server /// public void Close() { this.stop = true; } /// /// Check messages in the mailslot. /// private void checkMailSlotThread() { while (!this.stop) { ReadMailslot(this.hMailslot); Thread.Sleep(100); } if (this.hMailslot != null) { this.hMailslot.Close(); this.hMailslot = null; } } /// /// The CreateMailslotSecurity function creates and initializes a new /// SECURITY_ATTRIBUTES object to allow Authenticated Users read and /// write access to a mailslot, and to allow the Administrators group full /// access to the mailslot. /// /// /// A SECURITY_ATTRIBUTES object that allows Authenticated Users read and /// write access to a mailslot, and allows the Administrators group full /// access to the mailslot. /// /// private SECURITY_ATTRIBUTES CreateMailslotSecurity() { // Define the SDDL for the security descriptor. string sddl = "D:" + // Discretionary ACL "(A;OICI;GRGW;;;AU)" + // Allow read/write to authenticated users "(A;OICI;GA;;;BA)"; // Allow full control to administrators SafeLocalMemHandle pSecurityDescriptor = null; if (!NativeMethod.ConvertStringSecurityDescriptorToSecurityDescriptor( sddl, 1, out pSecurityDescriptor, IntPtr.Zero)) { throw new Win32Exception(); } SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES(); sa.nLength = Marshal.SizeOf(sa); sa.lpSecurityDescriptor = pSecurityDescriptor; sa.bInheritHandle = false; return sa; } /// /// Read the messages from a mailslot by using the mailslot handle in a call /// to the ReadFile function. /// /// The handle of the mailslot /// /// If the function succeeds, the return value is true. /// private bool ReadMailslot(SafeMailslotHandle hMailslot) { int cbMessageBytes = 0; // Size of the message in bytes int cbBytesRead = 0; // Number of bytes read from the mailslot int cMessages = 0; // Number of messages in the slot bool succeeded = false; // Check for the number of messages in the mailslot. succeeded = NativeMethod.GetMailslotInfo( hMailslot, // Handle of the mailslot IntPtr.Zero, // No maximum message size out cbMessageBytes, // Size of next message out cMessages, // Number of messages IntPtr.Zero // No read time-out ); if (!succeeded) { if (OnError != null) OnError("GetMailslotInfo failed: " + Marshal.GetLastWin32Error()); return succeeded; } if (cbMessageBytes == MAILSLOT_NO_MESSAGE) { // There are no new messages in the mailslot at present return succeeded; } // Retrieve the messages one by one from the mailslot. while (cMessages != 0) { // Declare a byte array to fetch the data byte[] bBuffer = new byte[cbMessageBytes]; succeeded = NativeMethod.ReadFile( hMailslot, // Handle of mailslot bBuffer, // Buffer to receive data cbMessageBytes, // Size of buffer in bytes out cbBytesRead, // Number of bytes read from mailslot IntPtr.Zero // Not overlapped I/O ); if (!succeeded) { if (OnError != null) OnError("ReadFile failed: " + Marshal.GetLastWin32Error()); break; } // Returns the message. if (OnMessage != null) OnMessage.Invoke(this.encoding.GetString(bBuffer)); // Get the current number of un-read messages in the slot. The number // may not equal the initial message number because new messages may // arrive while we are reading the items in the slot. succeeded = NativeMethod.GetMailslotInfo( hMailslot, // Handle of the mailslot IntPtr.Zero, // No maximum message size out cbMessageBytes, // Size of next message out cMessages, // Number of messages IntPtr.Zero // No read time-out ); if (!succeeded) { if (OnError != null) OnError("GetMailslotInfo failed: " + Marshal.GetLastWin32Error()); break; } } return succeeded; } #region Native API Signatures and Types /// /// Mailslot waits forever for a message /// internal const int MAILSLOT_WAIT_FOREVER = -1; /// /// There is no next message /// internal const int MAILSLOT_NO_MESSAGE = -1; /// /// Represents a wrapper class for a mailslot handle. /// [SecurityCritical(SecurityCriticalScope.Everything), HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true), SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] internal 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); } } /// /// The SECURITY_ATTRIBUTES structure contains the security descriptor for /// an object and specifies whether the handle retrieved by specifying /// this structure is inheritable. This structure provides security /// settings for objects created by various functions, such as CreateFile, /// CreateNamedPipe, CreateProcess, RegCreateKeyEx, or RegSaveKeyEx. /// [StructLayout(LayoutKind.Sequential)] internal class SECURITY_ATTRIBUTES { public int nLength; public SafeLocalMemHandle lpSecurityDescriptor; public bool bInheritHandle; } /// /// Represents a wrapper class for a local memory pointer. /// [SuppressUnmanagedCodeSecurity, HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)] internal sealed class SafeLocalMemHandle : SafeHandleZeroOrMinusOneIsInvalid { public SafeLocalMemHandle() : base(true) { } public SafeLocalMemHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle) { base.SetHandle(preexistingHandle); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr LocalFree(IntPtr hMem); protected override bool ReleaseHandle() { return (LocalFree(base.handle) == IntPtr.Zero); } } /// /// The class exposes Windows APIs to be used in this code sample. /// [SuppressUnmanagedCodeSecurity] internal class NativeMethod { /// /// Creates an instance of a mailslot and returns a handle for subsequent /// operations. /// /// Mailslot name /// /// The maximum size of a single message /// /// /// The time a read operation can wait for a message. /// /// Security attributes /// /// If the function succeeds, the return value is a handle to the server /// end of a mailslot instance. /// [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern SafeMailslotHandle CreateMailslot(string mailslotName, uint nMaxMessageSize, int lReadTimeout, SECURITY_ATTRIBUTES securityAttributes); /// /// Retrieves information about the specified mailslot. /// /// A handle to a mailslot /// /// The maximum message size, in bytes, allowed for this mailslot. /// /// /// The size of the next message in bytes. /// /// /// The total number of messages waiting to be read. /// /// /// The amount of time, in milliseconds, a read operation can wait for a /// message to be written to the mailslot before a time-out occurs. /// /// [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); /// /// Reads data from the specified file or input/output (I/O) device. /// /// /// A handle to the device (for example, a file, file stream, physical /// disk, volume, console buffer, tape drive, socket, communications /// resource, mailslot, or pipe). /// /// /// A buffer that receives the data read from a file or device. /// /// /// The maximum number of bytes to be read. /// /// /// The number of bytes read when using a synchronous IO. /// /// /// A pointer to an OVERLAPPED structure if the file was opened with /// FILE_FLAG_OVERLAPPED. /// /// /// If the function succeeds, the return value is true. If the function /// fails, or is completing asynchronously, the return value is false. /// [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); /// /// The ConvertStringSecurityDescriptorToSecurityDescriptor function /// converts a string-format security descriptor into a valid, /// functional security descriptor. /// /// /// A string containing the string-format security descriptor (SDDL) /// to convert. /// /// /// The revision level of the sddlSecurityDescriptor string. /// Currently this value must be 1. /// /// /// A pointer to a variable that receives a pointer to the converted /// security descriptor. /// /// /// A pointer to a variable that receives the size, in bytes, of the /// converted security descriptor. This parameter can be IntPtr.Zero. /// /// /// If the function succeeds, the return value is true. /// [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor( string sddlSecurityDescriptor, int sddlRevision, out SafeLocalMemHandle pSecurityDescriptor, IntPtr securityDescriptorSize); } #endregion } }