SHOW:
|
|
- or go back to the newest paste.
| 1 | using System; | |
| 2 | using System.IO; | |
| 3 | using System.Runtime.InteropServices; | |
| 4 | using System.Threading; | |
| 5 | using Microsoft.Win32.SafeHandles; | |
| 6 | ||
| 7 | namespace wyDay.Controls | |
| 8 | {
| |
| 9 | /// <summary>Allow pipe communication between a server and a client.</summary> | |
| 10 | public class PipeClient : IDisposable | |
| 11 | {
| |
| 12 | private object _lock = new object(); | |
| 13 | ||
| 14 | [DllImport("kernel32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
| |
| 15 | static extern SafeFileHandle CreateFile( | |
| 16 | string pipeName, | |
| 17 | uint dwDesiredAccess, | |
| 18 | uint dwShareMode, | |
| 19 | IntPtr lpSecurityAttributes, | |
| 20 | uint dwCreationDisposition, | |
| 21 | uint dwFlagsAndAttributes, | |
| 22 | IntPtr hTemplate); | |
| 23 | ||
| 24 | /// <summary>Handles messages received from a server pipe</summary> | |
| 25 | /// <param name="message">The byte message received</param> | |
| 26 | public delegate void MessageReceivedHandler(byte[] message); | |
| 27 | ||
| 28 | /// <summary>Event is called whenever a message is received from the server pipe</summary> | |
| 29 | public event MessageReceivedHandler MessageReceived; | |
| 30 | ||
| 31 | /// <summary>Handles server disconnected messages</summary> | |
| 32 | public delegate void ServerDisconnectedHandler(); | |
| 33 | ||
| 34 | /// <summary>Event is called when the server pipe is severed.</summary> | |
| 35 | public event ServerDisconnectedHandler ServerDisconnected; | |
| 36 | ||
| 37 | const int BUFFER_SIZE = 4096; | |
| 38 | ||
| 39 | FileStream stream; | |
| 40 | SafeFileHandle handle; | |
| 41 | Thread readThread; | |
| 42 | ||
| 43 | /// <summary>Gets if this client connected to a server pipe.</summary> | |
| 44 | public bool Connected { get; private set; }
| |
| 45 | ||
| 46 | /// <summary>The pipe this client is connected to.</summary> | |
| 47 | public string PipeName { get; private set; }
| |
| 48 | ||
| 49 | #region Dispose | |
| 50 | ||
| 51 | /// <summary>Indicates whether this instance is disposed.</summary> | |
| 52 | bool isDisposed; | |
| 53 | ||
| 54 | /// <summary> | |
| 55 | /// Finalizes an instance of the <see cref="PipeClient"/> class. | |
| 56 | /// Releases unmanaged resources and performs other cleanup operations before the | |
| 57 | /// <see cref="PipeClient"/> is reclaimed by garbage collection. | |
| 58 | /// </summary> | |
| 59 | ~PipeClient() | |
| 60 | {
| |
| 61 | Dispose(false); | |
| 62 | } | |
| 63 | ||
| 64 | /// <summary>Releases unmanaged and - optionally - managed resources.</summary> | |
| 65 | /// <param name="isManaged">Result: <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> | |
| 66 | protected virtual void Dispose(bool isManaged) | |
| 67 | {
| |
| 68 | if (isManaged) | |
| 69 | {
| |
| 70 | lock (_lock) | |
| 71 | {
| |
| 72 | if (!isDisposed) | |
| 73 | {
| |
| 74 | ||
| 75 | // dispose managed resources | |
| 76 | Disconnect(); | |
| 77 | ||
| 78 | ||
| 79 | // free unmanaged resources | |
| 80 | // Set large fields to null. | |
| 81 | ||
| 82 | // Instance is disposed | |
| 83 | isDisposed = true; | |
| 84 | } | |
| 85 | } | |
| 86 | } | |
| 87 | // Instance is disposed | |
| 88 | isDisposed = true; | |
| 89 | } | |
| 90 | ||
| 91 | /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary> | |
| 92 | public void Dispose() | |
| 93 | {
| |
| 94 | Dispose(true); | |
| 95 | GC.SuppressFinalize(this); | |
| 96 | } | |
| 97 | ||
| 98 | #endregion | |
| 99 | ||
| 100 | /// <summary>Connects to the server with a pipename.</summary> | |
| 101 | /// <param name="pipename">The name of the pipe to connect to.</param> | |
| 102 | public void Connect(string pipename) | |
| 103 | {
| |
| 104 | lock (_lock) | |
| 105 | {
| |
| 106 | if (isDisposed) return; | |
| 107 | if (Connected) | |
| 108 | throw new Exception("Already connected to pipe server.");
| |
| 109 | ||
| 110 | PipeName = pipename; | |
| 111 | ||
| 112 | handle = | |
| 113 | CreateFile( | |
| 114 | PipeName, | |
| 115 | 0xC0000000, // GENERIC_READ | GENERIC_WRITE = 0x80000000 | 0x40000000 | |
| 116 | 0, | |
| 117 | IntPtr.Zero, | |
| 118 | 3, // OPEN_EXISTING | |
| 119 | 0x40000000, // FILE_FLAG_OVERLAPPED | |
| 120 | IntPtr.Zero); | |
| 121 | ||
| 122 | // could not create handle - server probably not running | |
| 123 | if (handle.IsInvalid) | |
| 124 | {
| |
| 125 | handle = null; | |
| 126 | return; | |
| 127 | } | |
| 128 | ||
| 129 | Connected = true; | |
| 130 | ||
| 131 | // start listening for messages | |
| 132 | readThread = new Thread(Read) { IsBackground = true };
| |
| 133 | readThread.Start(); | |
| 134 | } | |
| 135 | } | |
| 136 | ||
| 137 | /// <summary>Disconnects from the server.</summary> | |
| 138 | public void Disconnect() | |
| 139 | {
| |
| 140 | lock (_lock) | |
| 141 | {
| |
| 142 | if (isDisposed) return; | |
| 143 | ||
| 144 | if (!Connected) | |
| 145 | return; | |
| 146 | ||
| 147 | // we're no longer connected to the server | |
| 148 | Connected = false; | |
| 149 | PipeName = null; | |
| 150 | ||
| 151 | // clean up resources | |
| 152 | if (stream != null) | |
| 153 | stream.Dispose(); | |
| 154 | ||
| 155 | // If Connected == true then handle is non-null. | |
| 156 | // Thus, just dispose it. | |
| 157 | handle.Dispose(); | |
| 158 | ||
| 159 | stream = null; | |
| 160 | handle = null; | |
| 161 | } | |
| 162 | } | |
| 163 | ||
| 164 | void Read() | |
| 165 | {
| |
| 166 | lock (_lock) | |
| 167 | {
| |
| 168 | if (isDisposed) return; | |
| 169 | ||
| 170 | stream = new FileStream(handle, FileAccess.ReadWrite, BUFFER_SIZE, true); | |
| 171 | ||
| 172 | } | |
| 173 | ||
| 174 | byte[] readBuffer = new byte[BUFFER_SIZE]; | |
| 175 | ||
| 176 | while (true) | |
| 177 | {
| |
| 178 | int bytesRead = 0; | |
| 179 | ||
| 180 | using (MemoryStream ms = new MemoryStream()) | |
| 181 | {
| |
| 182 | try | |
| 183 | {
| |
| 184 | // read the total stream length | |
| 185 | int totalSize = stream.Read(readBuffer, 0, 4); | |
| 186 | ||
| 187 | // client has disconnected | |
| 188 | if (totalSize == 0) | |
| 189 | break; | |
| 190 | ||
| 191 | totalSize = BitConverter.ToInt32(readBuffer, 0); | |
| 192 | ||
| 193 | do | |
| 194 | {
| |
| 195 | int numBytes = stream.Read(readBuffer, 0, Math.Min(totalSize - bytesRead, BUFFER_SIZE)); | |
| 196 | ||
| 197 | ms.Write(readBuffer, 0, numBytes); | |
| 198 | ||
| 199 | bytesRead += numBytes; | |
| 200 | ||
| 201 | } while (bytesRead < totalSize); | |
| 202 | } | |
| 203 | catch | |
| 204 | {
| |
| 205 | //read error has occurred | |
| 206 | break; | |
| 207 | } | |
| 208 | ||
| 209 | //client has disconnected | |
| 210 | if (bytesRead == 0) | |
| 211 | break; | |
| 212 | ||
| 213 | //get the event to execute while locked, but execute it outside the lock to prevent deadlock | |
| 214 | MessageReceivedHandler eventToExecute; | |
| 215 | lock (_lock) | |
| 216 | {
| |
| 217 | if (isDisposed) return; | |
| 218 | eventToExecute = MessageReceived; | |
| 219 | } | |
| 220 | //fire message received event | |
| 221 | if (eventToExecute != null) | |
| 222 | eventToExecute(ms.ToArray()); | |
| 223 | ||
| 224 | } | |
| 225 | } | |
| 226 | ||
| 227 | bool isLocked = false; | |
| 228 | try | |
| 229 | {
| |
| 230 | Monitor.Enter(_lock); | |
| 231 | isLocked = true; | |
| 232 | if (isDisposed) return; | |
| 233 | ||
| 234 | // if connected, then the disconnection was | |
| 235 | // caused by a server terminating, otherwise it was from | |
| 236 | // a call to Disconnect() | |
| 237 | if (Connected) | |
| 238 | {
| |
| 239 | //clean up resource | |
| 240 | stream.Dispose(); | |
| 241 | handle.Dispose(); | |
| 242 | ||
| 243 | stream = null; | |
| 244 | handle = null; | |
| 245 | ||
| 246 | // we're no longer connected to the server | |
| 247 | Connected = false; | |
| 248 | PipeName = null; | |
| 249 | ||
| 250 | //get the event to execute while locked, but execute it outside the lock to prevent deadlock | |
| 251 | var eventToExecute = ServerDisconnected; | |
| 252 | - | Monitor.Exit(isLocked); |
| 252 | + | |
| 253 | isLocked = false; | |
| 254 | ||
| 255 | if (eventToExecute != null) | |
| 256 | eventToExecute(); | |
| 257 | } | |
| 258 | } | |
| 259 | finally | |
| 260 | {
| |
| 261 | if (isLocked) | |
| 262 | {
| |
| 263 | Monitor.Exit(_lock); | |
| 264 | } | |
| 265 | } | |
| 266 | } | |
| 267 | ||
| 268 | /// <summary>Sends a message to the server.</summary> | |
| 269 | /// <param name="message">The message to send.</param> | |
| 270 | /// <returns>True if the message is sent successfully - false otherwise.</returns> | |
| 271 | public bool SendMessage(byte[] message) | |
| 272 | {
| |
| 273 | lock (_lock) | |
| 274 | {
| |
| 275 | if (isDisposed) return false; | |
| 276 | ||
| 277 | if (stream == null) return false; | |
| 278 | ||
| 279 | try | |
| 280 | {
| |
| 281 | // write the entire stream length | |
| 282 | stream.Write(BitConverter.GetBytes(message.Length), 0, 4); | |
| 283 | ||
| 284 | stream.Write(message, 0, message.Length); | |
| 285 | stream.Flush(); | |
| 286 | return true; | |
| 287 | } | |
| 288 | catch | |
| 289 | {
| |
| 290 | return false; | |
| 291 | } | |
| 292 | } | |
| 293 | } | |
| 294 | } | |
| 295 | } |