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
}
}