Ok, looking at your code, it doesn't look like you implemented IDisposable correctly. You also didn't implement your singleton quite correctly. Lastly (this one doesn't effect functionality) While your OnMessage uses a delegate that takes a string array, it should be an actual object that inherits from
EventArgs
.
So I reimplemented the code try this first an EventArgs argument
internal class EnumerableStringEventArgs : EventArgs
{
private readonly IEnumerable<string> _strings = null;
public EnumerableStringEventArgs(IEnumerable<string> strings)
{
_strings = strings;
}
public IEnumerable<string> Strings
{
get { return _strings; }
}
}
Next your class implementing a singleton pattern, making the constructor private (eliminating the isfirst stuff) so I can force the use of
Instance
. I also used the
EventHandler
generic to make the event (eliminating the need for a delegate). I also implemented
IDisposable
and an
IsDisposed
property, and rather than using a boolean I used an int and the Interlocked functions to keep everything thread safe. I also changed the Thread to a threadpool thread, and changed the waiting mechaninsm to a
ManualResetEvent
, thus eliminating the need to keep track of the separate thread.
Finally I added a bunch of
Debug.WriteLine
s so you could see it working.
public sealed class SingleInstance : IDisposable
{
private readonly ManualResetEvent _event = new ManualResetEvent(false);
private readonly string _pipeName;
private static readonly object _SyncLock = new object();
private static SingleInstance _instance;
private Int32 _disposed;
private SingleInstance()
{
_pipeName = string.Format("LocalPipe\\{0}",
Assembly.GetExecutingAssembly().GetName().Name);
_event.Reset();
ThreadPool.QueueUserWorkItem(RunServer);
}
~SingleInstance()
{
Dispose(false);
}
public event EventHandler<EnumerableStringEventArgs> OnMessage;
public static SingleInstance Instance
{
get
{
lock (_SyncLock)
{
if (_instance == null || _instance.IsDisposed)
{
_instance = new SingleInstance();
}
return _instance;
}
}
}
public bool IsDisposed
{
get
{
return (Interlocked.Add(ref _disposed, 0) > 0);
}
}
public String Path
{
get { return _pipeName; }
}
public void Dispose()
{
Dispose(true);
}
public void WaitOne(int timeoutMilliseconds)
{
_event.WaitOne(timeoutMilliseconds);
}
private void RunServer(object state)
{
try
{
NamedPipeServerStream pipeStream = null;
while (!IsDisposed)
{
try
{
pipeStream = new NamedPipeServerStream(_pipeName);
pipeStream.WaitForConnection();
}
catch
{
pipeStream = null;
return;
}
var strings = new List<string>();
using (var sr = new StreamReader(pipeStream))
{
while (!sr.EndOfStream)
{
strings.Add(sr.ReadLine());
}
}
if (OnMessage != null)
{
OnMessage(this, new EnumerableStringEventArgs(strings));
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
_event.Set();
}
}
private void Dispose(bool disposing)
{
try
{
Debug.WriteLine("Dispose(" + disposing + ")");
if (Interlocked.Add(ref _disposed, 1) == 1)
{
if (disposing)
{
GC.SuppressFinalize(this);
}
CloseServerSocket();
}
}
finally
{
_event.WaitOne();
Interlocked.Exchange(ref _disposed, 0);
}
}
private void CloseServerSocket()
{
try
{
using (var other = new NamedPipeClientStream(_pipeName))
{
other.Connect();
using (var sw = new StreamWriter(other) { AutoFlush = true })
{
sw.WriteLine("Shutdown");
}
}
}
catch
{
}
finally
{
}
}
}
Finally a working test for it.
public class Program
{
private static readonly ManualResetEvent _event = new ManualResetEvent(false);
public static void Main(string[] args)
{
_event.Reset();
using (var instance = SingleInstance.Instance)
{
instance.OnMessage += OnMessage;
ThreadPool.QueueUserWorkItem(ThreadFunc);
_event.WaitOne();
instance.OnMessage -= OnMessage;
}
Debug.WriteLine("done");
}
private static void ThreadFunc(object state)
{
try
{
var bytes = Encoding.UTF8.GetBytes("test connection");
var length = bytes.Length;
for (int i = 0; i < 2; i++)
{
using (var client = new NamedPipeClientStream(SingleInstance.Instance.Path))
{
client.Connect();
client.Write(bytes, 0, length);
}
}
}
catch
{
}
finally
{
_event.Set();
}
}
private static void OnMessage(object sender, EnumerableStringEventArgs e)
{
foreach (var s in e.Strings)
{
Debug.WriteLine(s);
}
}
}