Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.IO;
- using System.Runtime.InteropServices;
- using System.Threading;
- using Microsoft.Win32.SafeHandles;
- namespace wyDay.Controls
- {
- /// <summary>Allow pipe communication between a server and a client.</summary>
- public class PipeClient : IDisposable
- {
- private object _lock = new object();
- [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
- static extern SafeFileHandle CreateFile(
- string pipeName,
- uint dwDesiredAccess,
- uint dwShareMode,
- IntPtr lpSecurityAttributes,
- uint dwCreationDisposition,
- uint dwFlagsAndAttributes,
- IntPtr hTemplate);
- /// <summary>Handles messages received from a server pipe</summary>
- /// <param name="message">The byte message received</param>
- public delegate void MessageReceivedHandler(byte[] message);
- /// <summary>Event is called whenever a message is received from the server pipe</summary>
- public event MessageReceivedHandler MessageReceived;
- /// <summary>Handles server disconnected messages</summary>
- public delegate void ServerDisconnectedHandler();
- /// <summary>Event is called when the server pipe is severed.</summary>
- public event ServerDisconnectedHandler ServerDisconnected;
- const int BUFFER_SIZE = 4096;
- FileStream stream;
- SafeFileHandle handle;
- Thread readThread;
- /// <summary>Gets if this client connected to a server pipe.</summary>
- public bool Connected { get; private set; }
- /// <summary>The pipe this client is connected to.</summary>
- public string PipeName { get; private set; }
- #region Dispose
- /// <summary>Indicates whether this instance is disposed.</summary>
- bool isDisposed;
- /// <summary>
- /// Finalizes an instance of the <see cref="PipeClient"/> class.
- /// Releases unmanaged resources and performs other cleanup operations before the
- /// <see cref="PipeClient"/> is reclaimed by garbage collection.
- /// </summary>
- ~PipeClient()
- {
- Dispose(false);
- }
- /// <summary>Releases unmanaged and - optionally - managed resources.</summary>
- /// <param name="isManaged">Result: <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool isManaged)
- {
- if (isManaged)
- {
- lock (_lock)
- {
- if (!isDisposed)
- {
- // dispose managed resources
- Disconnect();
- // free unmanaged resources
- // Set large fields to null.
- // Instance is disposed
- isDisposed = true;
- }
- }
- }
- // Instance is disposed
- isDisposed = true;
- }
- /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- #endregion
- /// <summary>Connects to the server with a pipename.</summary>
- /// <param name="pipename">The name of the pipe to connect to.</param>
- public void Connect(string pipename)
- {
- lock (_lock)
- {
- if (isDisposed) return;
- if (Connected)
- throw new Exception("Already connected to pipe server.");
- PipeName = pipename;
- handle =
- CreateFile(
- PipeName,
- 0xC0000000, // GENERIC_READ | GENERIC_WRITE = 0x80000000 | 0x40000000
- 0,
- IntPtr.Zero,
- 3, // OPEN_EXISTING
- 0x40000000, // FILE_FLAG_OVERLAPPED
- IntPtr.Zero);
- // could not create handle - server probably not running
- if (handle.IsInvalid)
- {
- handle = null;
- return;
- }
- Connected = true;
- // start listening for messages
- readThread = new Thread(Read) { IsBackground = true };
- readThread.Start();
- }
- }
- /// <summary>Disconnects from the server.</summary>
- public void Disconnect()
- {
- lock (_lock)
- {
- if (isDisposed) return;
- if (!Connected)
- return;
- // we're no longer connected to the server
- Connected = false;
- PipeName = null;
- // clean up resources
- if (stream != null)
- stream.Dispose();
- // If Connected == true then handle is non-null.
- // Thus, just dispose it.
- handle.Dispose();
- stream = null;
- handle = null;
- }
- }
- void Read()
- {
- lock (_lock)
- {
- if (isDisposed) return;
- stream = new FileStream(handle, FileAccess.ReadWrite, BUFFER_SIZE, true);
- }
- byte[] readBuffer = new byte[BUFFER_SIZE];
- while (true)
- {
- int bytesRead = 0;
- using (MemoryStream ms = new MemoryStream())
- {
- try
- {
- // read the total stream length
- int totalSize = stream.Read(readBuffer, 0, 4);
- // client has disconnected
- if (totalSize == 0)
- break;
- totalSize = BitConverter.ToInt32(readBuffer, 0);
- do
- {
- int numBytes = stream.Read(readBuffer, 0, Math.Min(totalSize - bytesRead, BUFFER_SIZE));
- ms.Write(readBuffer, 0, numBytes);
- bytesRead += numBytes;
- } while (bytesRead < totalSize);
- }
- catch
- {
- //read error has occurred
- break;
- }
- //client has disconnected
- if (bytesRead == 0)
- break;
- //get the event to execute while locked, but execute it outside the lock to prevent deadlock
- MessageReceivedHandler eventToExecute;
- lock (_lock)
- {
- if (isDisposed) return;
- eventToExecute = MessageReceived;
- }
- //fire message received event
- if (eventToExecute != null)
- eventToExecute(ms.ToArray());
- }
- }
- bool isLocked = false;
- try
- {
- Monitor.Enter(_lock);
- isLocked = true;
- if (isDisposed) return;
- // if connected, then the disconnection was
- // caused by a server terminating, otherwise it was from
- // a call to Disconnect()
- if (Connected)
- {
- //clean up resource
- stream.Dispose();
- handle.Dispose();
- stream = null;
- handle = null;
- // we're no longer connected to the server
- Connected = false;
- PipeName = null;
- //get the event to execute while locked, but execute it outside the lock to prevent deadlock
- var eventToExecute = ServerDisconnected;
- Monitor.Exit(_lock);
- isLocked = false;
- if (eventToExecute != null)
- eventToExecute();
- }
- }
- finally
- {
- if (isLocked)
- {
- Monitor.Exit(_lock);
- }
- }
- }
- /// <summary>Sends a message to the server.</summary>
- /// <param name="message">The message to send.</param>
- /// <returns>True if the message is sent successfully - false otherwise.</returns>
- public bool SendMessage(byte[] message)
- {
- lock (_lock)
- {
- if (isDisposed) return false;
- if (stream == null) return false;
- try
- {
- // write the entire stream length
- stream.Write(BitConverter.GetBytes(message.Length), 0, 4);
- stream.Write(message, 0, message.Length);
- stream.Flush();
- return true;
- }
- catch
- {
- return false;
- }
- }
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment